Skip to content

Commit

Permalink
feat: auto detect stdin format (json|yaml|toml) (#4)
Browse files Browse the repository at this point in the history
* feat: auto detect stdin format (json|yaml|toml)
* ci: add quick-check on PRs and split in multiple jobs
  • Loading branch information
chevdor committed Jun 17, 2021
1 parent f3216ec commit 6551eec
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 19 deletions.
24 changes: 22 additions & 2 deletions .github/workflows/quick-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ on:
push:
tags-ignore:
- v*
pull_request:
types: [opened, synchronize, reopened]

jobs:
quick_check:
quick_check-format:
runs-on: ubuntu-latest
steps:
- name: Install Rust stable toolchain
Expand All @@ -17,7 +19,7 @@ jobs:
profile: minimal
toolchain: stable
override: true
components: clippy, rustfmt
components: rustfmt

- uses: actions/checkout@v2

Expand All @@ -27,12 +29,30 @@ jobs:
command: fmt
args: --all -- --check

quick_check-clippy:
runs-on: ubuntu-latest
steps:
- name: Install Rust stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: clippy

- uses: actions/checkout@v2

- name: Cargo clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: -- -D warnings

quick_check-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: Cargo test
uses: actions-rs/cargo@v1
with:
Expand Down
118 changes: 101 additions & 17 deletions src/wrapped_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ pub struct WrappedContext {
opts: Opts,
}

#[derive(Debug, PartialEq)]
pub enum SupportedType {
Json,
Toml,
Yaml,
}

impl WrappedContext {
pub fn new(opts: Opts) -> Self {
Self { context: Context::new(), opts }
Expand All @@ -30,8 +37,8 @@ impl WrappedContext {

pub fn append_json(&mut self, str: &str) {
debug!("Appending json");
let json = str.parse::<serde_json::Value>().expect("Parse json");
let object = json.as_object().expect("Json object expected");
let json = str.parse::<serde_json::Value>().expect("JSON parsing");
let object = json.as_object().expect("JSON as object");

for (k, v) in object.iter() {
self.handle_collision("json", k, v);
Expand All @@ -40,8 +47,8 @@ impl WrappedContext {

pub fn append_toml(&mut self, str: &str) {
debug!("Appending toml");
let value = str.parse::<toml::Value>().unwrap();
let table = value.as_table().unwrap();
let value = str.parse::<toml::Value>().expect("TOML Parsing");
let table = value.as_table().expect("TOML as table");

for (k, v) in table.iter() {
self.handle_collision("toml", k, v);
Expand All @@ -50,8 +57,8 @@ impl WrappedContext {

pub fn append_yaml(&mut self, str: &str) {
debug!("Appending yaml");
let value: serde_yaml::Value = serde_yaml::from_str(str).unwrap();
let mapping = value.as_mapping().unwrap();
let value: serde_yaml::Value = serde_yaml::from_str(str).expect("YAML parsing");
let mapping = value.as_mapping().expect("YAML as mapping");

for (k, v) in mapping.iter() {
let k = k.as_str().unwrap();
Expand Down Expand Up @@ -93,6 +100,40 @@ impl WrappedContext {
}
}

pub fn get_type(str: &str) -> Option<SupportedType> {
if let Ok(v) = str.parse::<serde_json::Value>() {
if v.as_object().is_some() {
return Some(SupportedType::Json);
} else {
debug!("Found json but not an Object")
}
} else {
debug!("not json");
}

if let Ok(v) = str.parse::<toml::Value>() {
if v.as_table().is_some() {
return Some(SupportedType::Toml);
} else {
debug!("Found toml but not a table")
}
} else {
debug!("not toml");
}

if let Ok(v) = serde_yaml::from_str::<serde_yaml::Value>(str) {
if v.as_mapping().is_some() {
return Some(SupportedType::Yaml);
} else {
debug!("Found yaml but not a mapping")
}
} else {
debug!("not yaml");
}

None
}

pub fn create_context(&mut self) {
if (self.opts.env || self.opts.env_only) && self.opts.env_first {
info!("Appending env to context first, env-key: {:?}", self.opts.env_key);
Expand All @@ -104,19 +145,15 @@ impl WrappedContext {
let mut stdin = stdin.lock();
let mut buf: Vec<u8> = Vec::with_capacity(BUFFER_SIZE);
let res = stdin.read_to_end(&mut buf).map_err(|e| e.to_string());
if res.is_err() || res.unwrap() == 0 {
panic!("if you provide stdin, make sure it is not empty"); // TODO: do we care actually ?
}
res.expect("Failed reading stdin");
let input = String::from_utf8(buf.to_vec()).unwrap();

// TODO: here we could check the type and append accordingly
// match Self::get_type(&input) {
// json && input.len() > 0 => ...
// toml && input.len() > 0 => ...
// yaml && input.len() > 0 => ...
// _ => {}
// }
self.append_json(&input);
match Self::get_type(&input) {
Some(SupportedType::Json) if !input.is_empty() => self.append_json(&input),
Some(SupportedType::Toml) if !input.is_empty() => self.append_toml(&input),
Some(SupportedType::Yaml) if !input.is_empty() => self.append_yaml(&input),
_ => {}
}
} else if self.opts.context.is_some() {
// here we know that we have a Path since --stdin is not passed
let context_file = self.opts.context.as_ref().unwrap();
Expand All @@ -138,3 +175,50 @@ impl WrappedContext {
}
}
}

#[cfg(test)]
mod test_context {
use super::*;

#[test]
fn test_get_type_json() {
let data = json!({
"name": "John Doe",
"age": 43,
"phones": [
"+44 1234567",
"+44 2345678"
]
});

assert!(WrappedContext::get_type(&data.to_string()) == Some(SupportedType::Json));
}

#[test]
fn test_get_type_toml() {
let data = r##"
name = "John"
age = 42
"##;

assert!(WrappedContext::get_type(&data.to_string()) == Some(SupportedType::Toml));
}

#[test]
fn test_get_type_yaml() {
let data = r##"
name: "Bob"
ag: 42
"##;
assert!(WrappedContext::get_type(&data.to_string()) == Some(SupportedType::Yaml));
}

#[test]
fn test_get_type_na() {
let data = r##"
foobar
"##;

assert!(WrappedContext::get_type(&data.to_string()) == None);
}
}
35 changes: 35 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,41 @@ mod cli_tests {
}
}

#[cfg(test)]
mod stdin {
use std::fs;

use assert_cmd::Command;
use predicates::prelude::*;

#[test]
fn it_process_json_stdin() {
let mut cmd = Command::cargo_bin(env!("CARGO_BIN_EXE_tera")).unwrap();
let stdin = fs::read_to_string("data/basic/basic.json").unwrap();

let assert = cmd.write_stdin(stdin).arg("-t").arg("data/basic/basic.tera").arg("--stdin").assert();
assert.success().stdout(predicate::str::contains("Bob likes orange"));
}

#[test]
fn it_process_toml_stdin() {
let mut cmd = Command::cargo_bin(env!("CARGO_BIN_EXE_tera")).unwrap();
let stdin = fs::read_to_string("data/basic/basic.toml").unwrap();

let assert = cmd.write_stdin(stdin).arg("-t").arg("data/basic/basic.tera").arg("--stdin").assert();
assert.success().stdout(predicate::str::contains("Bob likes orange"));
}

#[test]
fn it_process_yaml_stdin() {
let mut cmd = Command::cargo_bin(env!("CARGO_BIN_EXE_tera")).unwrap();
let stdin = fs::read_to_string("data/basic/basic.yaml").unwrap();

let assert = cmd.write_stdin(stdin).arg("-t").arg("data/basic/basic.tera").arg("--stdin").assert();
assert.success().stdout(predicate::str::contains("Bob likes orange"));
}
}

#[cfg(test)]
mod env {
use assert_cmd::Command;
Expand Down

0 comments on commit 6551eec

Please sign in to comment.