From d81632552997572ae2fee9a97421cc7129c3b9f8 Mon Sep 17 00:00:00 2001 From: eV Date: Sun, 4 Feb 2018 23:51:34 +0000 Subject: [PATCH 1/7] changes for nom 4.0 --- Cargo.toml | 2 +- rustfmt.toml | 1 + src/common.rs | 75 ++++++++++++++++---------- src/hcl.rs | 108 ++++++++++++++++++------------------- src/json.rs | 46 +++++++--------- tests/integration_tests.rs | 6 +-- 6 files changed, 122 insertions(+), 116 deletions(-) create mode 100644 rustfmt.toml diff --git a/Cargo.toml b/Cargo.toml index f1a1385..fe8c220 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,4 @@ version = "0.1.0" [dependencies.nom] features = ["nightly"] -version = "^3.2" +path = "../nom" diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..c7ad93b --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +disable_all_formatting = true diff --git a/src/common.rs b/src/common.rs index 51c435f..4dd78c7 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,51 +1,68 @@ use std::num::ParseIntError; -use std::str::{self, FromStr}; use std::u32; +use nom; use nom::{digit, hex_digit}; +use nom::types::CompleteStr; -named!(pub boolean, - map!( - alt_complete!(tag!("true") | tag!("false")), - |value: &[u8]| value == b"true" - ) +#[macro_export] +macro_rules! complete_named ( + ($name:ident, $submac:ident!( $($args:tt)* )) => ( + fn $name<'a>( i: CompleteStr<'a> ) -> nom::IResult, CompleteStr<'a>, u32> { + $submac!(i, $($args)*) + } + ); + ($name:ident<$o:ty>, $submac:ident!( $($args:tt)* )) => ( + fn $name<'a>( i: CompleteStr<'a> ) -> nom::IResult, $o, u32> { + $submac!(i, $($args)*) + } + ); + (pub $name:ident, $submac:ident!( $($args:tt)* )) => ( + pub fn $name<'a>( i: CompleteStr<'a> ) -> nom::IResult, CompleteStr<'a>, u32> { + $submac!(i, $($args)*) + } + ); + (pub $name:ident<$o:ty>, $submac:ident!( $($args:tt)* )) => ( + pub fn $name<'a>( i: CompleteStr<'a> ) -> nom::IResult, $o, u32> { + $submac!(i, $($args)*) + } + ); ); -named!(unsigned_float, recognize!(alt_complete!( +complete_named!(pub boolean,map!( + alt!(tag!("true") | tag!("false")), + |value: CompleteStr| value == CompleteStr("true") +)); + +complete_named!(unsigned_float, recognize!(alt_complete!( delimited!(digit, tag!("."), opt!(complete!(digit))) | delimited!(opt!(digit), tag!("."), digit) | digit ))); -named!(pub float, map_res!( - map_res!( - recognize!(alt_complete!( - delimited!( - pair!(opt!(alt!(tag!("+") | tag!("-"))), unsigned_float), - tag!("e"), - pair!(opt!(alt!(tag!("+") | tag!("-"))), unsigned_float) - ) | - unsigned_float - )), - str::from_utf8 - ), - FromStr::from_str +complete_named!(pub float, flat_map!( + recognize!(alt_complete!( + delimited!( + pair!(opt!(alt!(tag!("+") | tag!("-"))), unsigned_float), + tag!("e"), + pair!(opt!(alt!(tag!("+") | tag!("-"))), unsigned_float) + ) | + unsigned_float + )), + parse_to!(f32) )); -fn to_i(i: &str) -> Result { - u32::from_str_radix(i, 16) +fn complete_to_i(i: CompleteStr) -> Result { + u32::from_str_radix(i.0, 16) } -named!(pub int, map_res!( - map_res!( - preceded!(tag!("0x"), hex_digit), - str::from_utf8 - ), - to_i +complete_named!(int, map_res!( + preceded!(tag!("0x"), hex_digit), + complete_to_i )); // TODO: add support for octal -named!(pub number, alt_complete!( +complete_named!(pub number, alt_complete!( map!(int, |i| { i as f32 }) | float )); diff --git a/src/hcl.rs b/src/hcl.rs index c460598..2162079 100644 --- a/src/hcl.rs +++ b/src/hcl.rs @@ -1,56 +1,56 @@ -use std::str::{self, FromStr}; -use std::string::String; use std::collections::HashMap; +use std::str; +use std::string::String; -use nom::{alphanumeric, eol, multispace, not_line_ending}; -use nom::IResult::Done; +use nom; +use nom::types::CompleteStr; +use nom::{Needed, ExtendInto, alphanumeric, eol, multispace, not_line_ending}; use common::{boolean, number}; use types::{JsonValue, ParseError}; pub fn parse_hcl(config: &str) -> Result { - match hcl(&config.as_bytes()[..]) { - Done(_, c) => Ok(c), + match hcl(CompleteStr(config)) { + Ok((_, c)) => Ok(c), _ => Err(0) } } -named!(hcl, map!(hcl_top, |h| JsonValue::Object(h))); +complete_named!(hcl, map!(hcl_top, |h| JsonValue::Object(h))); -named!(end_of_line, alt!(eof!() | eol)); +complete_named!(end_of_line, alt!(eof!() | eol)); -fn to_s(i:Vec) -> String { String::from_utf8_lossy(&i).into_owned() } fn slen(i: String) -> usize { i.len() } -fn ulen(i: &[u8]) -> usize { i.len() } +fn cslen(i: CompleteStr) -> usize { i.0.len() } fn take_limited(min: usize, max: usize) -> usize { if max < min { return max } return min } -named!(hcl_escaped_string, map!( +complete_named!(hcl_escaped_string, escaped_transform!(is_not!("\\\"\n"), '\\', alt!( - tag!("\\") => { |_| &b"\\"[..] } | - tag!("\"") => { |_| &b"\""[..] } | - tag!("n") => { |_| &b"\n"[..] } - )), to_s -)); + tag!("\\") => { |_| "\\" } | + tag!("\"") => { |_| "\"" } | + tag!("n") => { |_| "\n" } + )) +); -named!(hcl_template_string, map!( +complete_named!(hcl_template_string, map!( do_parse!( tag!("${") >> s: take_until_and_consume!("}") >> (s) ), - |s| { format!("${{{}}}", String::from_utf8_lossy(s)) } + |s: CompleteStr| { format!("${{{}}}", s.0) } )); -named!(hcl_quoted_escaped_string, delimited!( +complete_named!(hcl_quoted_escaped_string, delimited!( tag!("\""), map!( fold_many0!( - alt_complete!( + alt!( hcl_template_string | flat_map!(do_parse!( max: map!(peek!(hcl_escaped_string), slen) >> - min: map!(peek!(take_until!("${")), ulen) >> + min: map!(peek!(take_until!("${")), cslen) >> buf: take!(take_limited(min, max)) >> (buf) ), hcl_escaped_string) | @@ -67,20 +67,18 @@ named!(hcl_quoted_escaped_string, delimited!( tag!("\"") )); -named!(hcl_multiline_string, map!( +complete_named!(hcl_multiline_string, map!( do_parse!( - delimiter: tag!("<<") >> + tag!("<<") >> indent: opt!(tag!("-")) >> delimiter: terminated!(alphanumeric, eol) >> - delimiter_str: expr_res!(str::from_utf8(delimiter)) >> - s: take_until!(delimiter_str) >> - tag!(delimiter_str) >> + s: take_until!(delimiter.0) >> + tag!(delimiter.0) >> end_of_line >> (indent, s) ), |(indent, s)| { - let body = String::from_utf8_lossy(s); - let lines: Vec<&str> = body.split("\n").collect(); + let lines: Vec<&str> = s.0.split("\n").collect(); let mut out: Vec<&str> = Vec::new(); let count = lines.len(); @@ -113,31 +111,31 @@ named!(hcl_multiline_string, map!( )); // close enough... -named!(identifier_char, alt!(tag!("_") | tag!("-") | tag!(".") | alphanumeric)); +complete_named!(identifier_char, alt!(tag!("_") | tag!("-") | tag!(".") | alphanumeric)); -named!(hcl_unquoted_key, map!( +complete_named!(hcl_unquoted_key, fold_many0!( identifier_char, - Vec::new(), - |mut acc: Vec<_>, item| { - acc.extend(item); + String::new(), + |mut acc: String, item: CompleteStr| { + //acc.extend(item); + item.extend_into(&mut acc); acc } - ), - to_s -)); + ) +); -named!(hcl_quoted_escaped_key, +complete_named!(hcl_quoted_escaped_key, map!(do_parse!(tag!("\"") >> out: opt!(hcl_escaped_string) >> tag!("\"") >> (out)), |out| { if let Some(val) = out { val } else { "".to_string() } } )); -named!(hcl_key, alt!( +complete_named!(hcl_key, alt!( hcl_quoted_escaped_key | hcl_unquoted_key )); -named!(space, eat_separator!(&b" \t"[..])); +complete_named!(space, eat_separator!(&b" \t"[..])); macro_rules! sp ( ($i:expr, $($args:tt)*) => ( @@ -147,31 +145,31 @@ macro_rules! sp ( ) ); -named!(hcl_key_value<(String, JsonValue)>, sp!(alt_complete!( +complete_named!(hcl_key_value<(String, JsonValue)>, sp!(alt!( separated_pair!(hcl_key, tag!("="), hcl_value_nested_hash) | separated_pair!(hcl_key, tag!("="), hcl_value) | pair!(hcl_key, hcl_value_nested_hash) ))); -named!(comment_one_line, - do_parse!(alt!(tag!("//") | tag!("#")) >> opt!(not_line_ending) >> end_of_line >> (&b""[..])) +complete_named!(comment_one_line, + do_parse!(alt!(tag!("//") | tag!("#")) >> opt!(not_line_ending) >> end_of_line >> (CompleteStr(""))) ); -named!(comment_block, - do_parse!(tag!("/*") >> take_until_and_consume!("*/") >> (&b""[..])) +complete_named!(comment_block, + do_parse!(tag!("/*") >> take_until_and_consume!("*/") >> (CompleteStr(""))) ); -named!(blanks, - do_parse!(many0!(alt!(tag!(",") | multispace | comment_one_line | comment_block)) >> (&b""[..])) +complete_named!(blanks, + do_parse!(many0!(alt!(tag!(",") | multispace | comment_one_line | comment_block)) >> (CompleteStr(""))) ); -named!(hcl_key_values>, - many0!(complete!(do_parse!( +complete_named!(hcl_key_values>, + many0!(do_parse!( opt!(blanks) >> out: hcl_key_value >> opt!(blanks) >> (out) - ))) + )) ); -named!(hcl_hash>, +complete_named!(hcl_hash>, do_parse!( opt!(blanks) >> tag!("{") >> @@ -182,7 +180,7 @@ named!(hcl_hash>, ) ); -named!(hcl_top>, +complete_named!(hcl_top>, map!(hcl_key_values, |tuple_vec| { let mut top: HashMap = HashMap::new(); for (k, v) in tuple_vec.into_iter().rev() { @@ -205,7 +203,7 @@ named!(hcl_top>, ); // a bit odd if you ask me -named!(hcl_value_nested_hash, map!( +complete_named!(hcl_value_nested_hash, map!( // NOTE hcl allows arbitrarily deep nesting pair!(many0!(sp!(hcl_quoted_escaped_key)), hcl_value_hash), |(tuple_vec, value)| { @@ -223,9 +221,9 @@ named!(hcl_value_nested_hash, map!( } )); -named!(hcl_value_hash, map!(hcl_hash, |h| JsonValue::Object(h))); +complete_named!(hcl_value_hash, map!(hcl_hash, |h| JsonValue::Object(h))); -named!(hcl_array>, delimited!( +complete_named!(hcl_array>, delimited!( tag!("["), do_parse!( init: fold_many0!( @@ -249,7 +247,7 @@ named!(hcl_array>, delimited!( tag!("]") )); -named!(hcl_value, alt!( +complete_named!(hcl_value, alt!( hcl_hash => { |h| JsonValue::Object(h) } | hcl_array => { |v| JsonValue::Array(v) } | hcl_quoted_escaped_string => { |s| JsonValue::Str(s) } | diff --git a/src/json.rs b/src/json.rs index 4f1958c..8373a31 100644 --- a/src/json.rs +++ b/src/json.rs @@ -4,7 +4,9 @@ use std::collections::HashMap; use std::str; -use nom::IResult::Done; +use nom; +use nom::types::CompleteStr; +use nom::Needed; use common::{boolean, float}; use types::{JsonValue, ParseError}; @@ -14,49 +16,37 @@ use types::{JsonValue, ParseError}; // this is not intended to mirror that pub fn parse_json(config: &str) -> Result { - match json(&config.as_bytes()[..]) { - Done(_, c) => Ok(c), + match json(CompleteStr(config)) { + Ok((_, c)) => Ok(c), _ => Err(0) } } -named!(json, map!(json_hash, |h| JsonValue::Object(h))); +complete_named!(json, map!(json_hash, |h| JsonValue::Object(h))); -fn to_s(i:Vec) -> String { String::from_utf8_lossy(&i).into_owned() } - -named!(json_escaped_string, map!( +complete_named!(json_escaped_string, escaped_transform!(is_not!("\\\"\n"), '\\', alt!( - tag!("\\") => { |_| &b"\\"[..] } | - tag!("\"") => { |_| &b"\""[..] } | - tag!("n") => { |_| &b"\n"[..] } - )), to_s -)); + tag!("\\") => { |_| "\\" } | + tag!("\"") => { |_| "\"" } | + tag!("n") => { |_| "\n" } + )) +); -named!(json_string, delimited!( +complete_named!(json_string, delimited!( tag!("\""), - map!( - fold_many0!( - json_escaped_string, - Vec::new(), - |mut acc: Vec<_>, item| { - acc.push(item); - acc - } - ), - |s| { s.join("") } - ), + json_escaped_string, tag!("\"") )); -named!(json_array>, ws!(delimited!( +complete_named!(json_array>, ws!(delimited!( tag!("["), separated_list!(tag!(","), json_value), tag!("]") ))); -named!(json_key_value<(String, JsonValue)>, ws!(separated_pair!(json_string, tag!(":"), json_value))); +complete_named!(json_key_value<(String, JsonValue)>, ws!(separated_pair!(json_string, tag!(":"), json_value))); -named!(json_hash>, ws!(map!( +complete_named!(json_hash>, ws!(map!( delimited!( tag!("{"), separated_list!(tag!(","), json_key_value), @@ -71,7 +61,7 @@ named!(json_hash>, ws!(map!( } ))); -named!(json_value, ws!(alt!( +complete_named!(json_value, ws!(alt!( json_hash => { |h| JsonValue::Object(h) } | json_array => { |v| JsonValue::Array(v) } | json_string => { |s| JsonValue::Str(String::from(s)) } | diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 474b39b..03bc0cd 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -70,7 +70,7 @@ fn test_fixture(case: &str, expect_pass: bool) { let mut file = File::open(&path).unwrap(); file.read_to_string(&mut hcl).unwrap(); - if (expect_pass) { + if expect_pass { let path = Path::new(&json_path); let mut file = File::open(&path).unwrap(); file.read_to_string(&mut json).unwrap(); @@ -78,14 +78,14 @@ fn test_fixture(case: &str, expect_pass: bool) { let parsed_hcl = parse_hcl(&hcl); if let Ok(parsed_hcl) = parsed_hcl { - if (expect_pass) { + if expect_pass { let parsed_json = parse_json(&json).unwrap(); assert_eq!(parsed_hcl, parsed_json); } else { panic!("Expected failure") } } else { - if (expect_pass) { + if expect_pass { panic!("Expected success") } } From ececf4aa7968876b8991a1369f06f50edb25f609 Mon Sep 17 00:00:00 2001 From: eV Date: Mon, 5 Feb 2018 01:00:40 +0000 Subject: [PATCH 2/7] add feature for hcl style array nesting --- Cargo.toml | 3 + src/hcl.rs | 130 ++++++++++++++++++++++++++++++++++++- tests/integration_tests.rs | 3 + 3 files changed, 135 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fe8c220..cff3c37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,9 @@ authors = ["eV "] name = "molysite" version = "0.1.0" +[features] +arraynested = [] + [dependencies.nom] features = ["nightly"] path = "../nom" diff --git a/src/hcl.rs b/src/hcl.rs index 2162079..ff4e002 100644 --- a/src/hcl.rs +++ b/src/hcl.rs @@ -180,6 +180,35 @@ complete_named!(hcl_hash>, ) ); +#[cfg(not(feature="arraynested"))] +complete_named!(hcl_top>, + map!(hcl_key_values, |tuple_vec| { + let mut top: HashMap = HashMap::new(); + for (k, v) in tuple_vec.into_iter().rev() { + // FIXME use deep merge + if top.contains_key(&k) { + if let JsonValue::Object(ref val_dict) = v { + if let Some(current) = top.remove(&k) { + if let JsonValue::Object(ref top_dict) = current { + let mut copy = top_dict.clone(); + let mut val_copy = val_dict.clone(); + copy.extend(val_copy); + top.insert(k, JsonValue::Object(copy)); + continue; + } else { + top.insert(k, current); + continue; + } + } + } + } + top.insert(k, v); + } + top + }) +); + +#[cfg(feature="arraynested")] complete_named!(hcl_top>, map!(hcl_key_values, |tuple_vec| { let mut top: HashMap = HashMap::new(); @@ -202,7 +231,23 @@ complete_named!(hcl_top>, }) ); +#[cfg(not(feature="arraynested"))] +complete_named!(hcl_value_nested_hash, map!( + // NOTE hcl allows arbitrarily deep nesting + pair!(many0!(sp!(hcl_quoted_escaped_key)), hcl_value_hash), + |(tuple_vec, value)| { + let mut cur = value; + for parent in tuple_vec.into_iter().rev() { + let mut h: HashMap = HashMap::new(); + h.insert(parent.to_string(), cur); + cur = JsonValue::Object(h); + } + cur + } +)); + // a bit odd if you ask me +#[cfg(feature="arraynested")] complete_named!(hcl_value_nested_hash, map!( // NOTE hcl allows arbitrarily deep nesting pair!(many0!(sp!(hcl_quoted_escaped_key)), hcl_value_hash), @@ -345,6 +390,23 @@ fn hcl_string_multi_with_template() { panic!("object did not parse"); } +#[cfg(not(feature="arraynested"))] +#[test] +fn hcl_block_empty_key() { + let test = "foo \"\" {\nbar = 1\n}"; + if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { + if let Some(&JsonValue::Object(ref dict)) = dict.get("foo") { + if let Some(&JsonValue::Object(ref dict)) = dict.get("") { + if let Some(&JsonValue::Num(ref resp)) = dict.get("bar") { + return assert_eq!(1., *resp); + } + } + } + } + panic!("object did not parse"); +} + +#[cfg(feature="arraynested")] #[test] fn hcl_block_empty_key() { let test = "foo \"\" {\nbar = 1\n}"; @@ -364,6 +426,23 @@ fn hcl_block_empty_key() { panic!("object did not parse"); } +#[cfg(not(feature="arraynested"))] +#[test] +fn hcl_block_key() { + let test = "potato \"salad\\\"is\" {\nnot = \"real\"\n}"; + if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { + if let Some(&JsonValue::Object(ref dict)) = dict.get("potato") { + if let Some(&JsonValue::Object(ref dict)) = dict.get("salad\"is") { + if let Some(&JsonValue::Str(ref resp)) = dict.get("not") { + return assert_eq!("real", resp); + } + } + } + } + panic!("object did not parse"); +} + +#[cfg(feature="arraynested")] #[test] fn hcl_block_key() { let test = "potato \"salad\\\"is\" {\nnot = \"real\"\n}"; @@ -383,11 +462,29 @@ fn hcl_block_key() { panic!("object did not parse"); } +#[cfg(not(feature="arraynested"))] +#[test] +fn hcl_block_nested_key() { + let test = "potato \"salad\" \"is\" {\nnot = \"real\"\n}"; + if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { + if let Some(&JsonValue::Object(ref dict)) = dict.get("potato") { + if let Some(&JsonValue::Object(ref dict)) = dict.get("salad") { + if let Some(&JsonValue::Object(ref dict)) = dict.get("is") { + if let Some(&JsonValue::Str(ref resp)) = dict.get("not") { + return assert_eq!("real", resp); + } + } + } + } + } + panic!("object did not parse"); +} + +#[cfg(feature="arraynested")] #[test] fn hcl_block_nested_key() { let test = "potato \"salad\" \"is\" {\nnot = \"real\"\n}"; if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - println!("{:?}", dict); if let Some(&JsonValue::Array(ref array)) = dict.get("potato") { if let Some(&JsonValue::Object(ref dict)) = array.get(0) { if let Some(&JsonValue::Array(ref array)) = dict.get("salad") { @@ -426,6 +523,35 @@ fn hcl_key_chars() { panic!("object did not parse"); } +#[cfg(not(feature="arraynested"))] +#[test] +fn hcl_slice_expand() { + let test = "service \"foo\" { + key = \"value\" +} + +service \"bar\" { + key = \"value\" +}"; + if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { + if let Some(&JsonValue::Object(ref dict)) = dict.get("service") { + let mut pass = false; + if let Some(&JsonValue::Object(_)) = dict.get("foo") { + pass = true; + } + if !pass { panic!("missing nested object") } + pass = false; + if let Some(&JsonValue::Object(_)) = dict.get("bar") { + pass = true; + } + if !pass { panic!("missing nested object") } + return + } + } + panic!("object did not parse") +} + +#[cfg(feature="arraynested")] #[test] fn hcl_slice_expand() { let test = "service \"foo\" { @@ -447,6 +573,8 @@ service \"bar\" { pass = true; } if !pass { panic!("missing nested object") } + return } } + panic!("object did not parse") } diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 03bc0cd..a7b27cf 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -7,6 +7,7 @@ use std::path::Path; use molysite::hcl::parse_hcl; use molysite::json::parse_json; +#[cfg(feature="arraynested")] macro_rules! fixture_tests { ($($name:ident: $value:expr,)*) => { $( @@ -19,6 +20,7 @@ macro_rules! fixture_tests { } } +#[cfg(feature="arraynested")] fixture_tests! { test_fixture_assign_deep: ("assign_deep", true), test_fixture_basic: ("basic", true), @@ -59,6 +61,7 @@ fixture_tests! { //test_fixture_unterminated_brace: ("unterminated_brace", false), } +#[allow(dead_code)] fn test_fixture(case: &str, expect_pass: bool) { let mut hcl = String::new(); let mut json = String::new(); From 2910f6e23ada30816aad4f0799d60f2ba986b85e Mon Sep 17 00:00:00 2001 From: eV Date: Mon, 5 Feb 2018 02:54:23 +0000 Subject: [PATCH 3/7] add withserde feature, with parser returning serde_json value --- Cargo.toml | 17 +- src/hcl.rs | 202 +++++++++--------- src/json.rs | 61 +++--- src/lib.rs | 3 + src/types.rs | 62 ++++-- ...{integration_tests.rs => fixture_tests.rs} | 0 tests/fixtures.rs | 96 +++++++++ tests/serde.rs | 93 ++++++++ 8 files changed, 385 insertions(+), 149 deletions(-) rename tests/{integration_tests.rs => fixture_tests.rs} (100%) create mode 100644 tests/fixtures.rs create mode 100644 tests/serde.rs diff --git a/Cargo.toml b/Cargo.toml index cff3c37..429aeed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,20 @@ authors = ["eV "] name = "molysite" version = "0.1.0" -[features] -arraynested = [] - [dependencies.nom] features = ["nightly"] path = "../nom" + +[dependencies.serde_json] +optional = true +version = "1.0.9" + +[dev-dependencies] +serde_derive = "1.0.27" + +[dev-dependencies.serde] +version = "1.0.27" + +[features] +arraynested = [] +withserde = ["serde_json"] diff --git a/src/hcl.rs b/src/hcl.rs index ff4e002..f1ecac9 100644 --- a/src/hcl.rs +++ b/src/hcl.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::str; use std::string::String; @@ -7,17 +6,16 @@ use nom::types::CompleteStr; use nom::{Needed, ExtendInto, alphanumeric, eol, multispace, not_line_ending}; use common::{boolean, number}; -use types::{JsonValue, ParseError}; +use types::{Map, Number, ParseError, Value}; - -pub fn parse_hcl(config: &str) -> Result { +pub fn parse_hcl(config: &str) -> Result { match hcl(CompleteStr(config)) { Ok((_, c)) => Ok(c), _ => Err(0) } } -complete_named!(hcl, map!(hcl_top, |h| JsonValue::Object(h))); +complete_named!(hcl, map!(hcl_top, |h| Value::Object(h))); complete_named!(end_of_line, alt!(eof!() | eol)); @@ -145,7 +143,7 @@ macro_rules! sp ( ) ); -complete_named!(hcl_key_value<(String, JsonValue)>, sp!(alt!( +complete_named!(hcl_key_value<(String, Value)>, sp!(alt!( separated_pair!(hcl_key, tag!("="), hcl_value_nested_hash) | separated_pair!(hcl_key, tag!("="), hcl_value) | pair!(hcl_key, hcl_value_nested_hash) @@ -163,13 +161,13 @@ complete_named!(blanks, do_parse!(many0!(alt!(tag!(",") | multispace | comment_one_line | comment_block)) >> (CompleteStr(""))) ); -complete_named!(hcl_key_values>, +complete_named!(hcl_key_values>, many0!(do_parse!( opt!(blanks) >> out: hcl_key_value >> opt!(blanks) >> (out) )) ); -complete_named!(hcl_hash>, +complete_named!(hcl_hash>, do_parse!( opt!(blanks) >> tag!("{") >> @@ -181,19 +179,19 @@ complete_named!(hcl_hash>, ); #[cfg(not(feature="arraynested"))] -complete_named!(hcl_top>, +complete_named!(hcl_top>, map!(hcl_key_values, |tuple_vec| { - let mut top: HashMap = HashMap::new(); + let mut top: Map = Map::new(); for (k, v) in tuple_vec.into_iter().rev() { // FIXME use deep merge if top.contains_key(&k) { - if let JsonValue::Object(ref val_dict) = v { - if let Some(current) = top.remove(&k) { - if let JsonValue::Object(ref top_dict) = current { + if let Value::Object(ref val_dict) = v { + if let Some(mut current) = top.remove(&k) { + if let Value::Object(ref mut top_dict) = current { let mut copy = top_dict.clone(); let mut val_copy = val_dict.clone(); copy.extend(val_copy); - top.insert(k, JsonValue::Object(copy)); + top.insert(k, Value::Object(copy)); continue; } else { top.insert(k, current); @@ -209,17 +207,17 @@ complete_named!(hcl_top>, ); #[cfg(feature="arraynested")] -complete_named!(hcl_top>, +complete_named!(hcl_top>, map!(hcl_key_values, |tuple_vec| { - let mut top: HashMap = HashMap::new(); + let mut top: Map = Map::new(); for (k, v) in tuple_vec.into_iter().rev() { if top.contains_key(&k) { - if let JsonValue::Array(ref v_a) = v { + if let Value::Array(ref v_a) = v { if let Some(current) = top.remove(&k) { - if let JsonValue::Array(ref a) = current { + if let Value::Array(ref a) = current { let mut copy = v_a.to_vec(); copy.extend(a.to_vec()); - top.insert(k, JsonValue::Array(copy)); + top.insert(k, Value::Array(copy)); continue; } } @@ -232,15 +230,15 @@ complete_named!(hcl_top>, ); #[cfg(not(feature="arraynested"))] -complete_named!(hcl_value_nested_hash, map!( +complete_named!(hcl_value_nested_hash, map!( // NOTE hcl allows arbitrarily deep nesting pair!(many0!(sp!(hcl_quoted_escaped_key)), hcl_value_hash), |(tuple_vec, value)| { let mut cur = value; for parent in tuple_vec.into_iter().rev() { - let mut h: HashMap = HashMap::new(); + let mut h: Map = Map::new(); h.insert(parent.to_string(), cur); - cur = JsonValue::Object(h); + cur = Value::Object(h); } cur } @@ -248,27 +246,27 @@ complete_named!(hcl_value_nested_hash, map!( // a bit odd if you ask me #[cfg(feature="arraynested")] -complete_named!(hcl_value_nested_hash, map!( +complete_named!(hcl_value_nested_hash, map!( // NOTE hcl allows arbitrarily deep nesting pair!(many0!(sp!(hcl_quoted_escaped_key)), hcl_value_hash), |(tuple_vec, value)| { let mut cur = value; for parent in tuple_vec.into_iter().rev() { - let mut inner: Vec = Vec::new(); + let mut inner: Vec = Vec::new(); inner.push(cur); - let mut h: HashMap = HashMap::new(); - h.insert(parent.to_string(), JsonValue::Array(inner)); - cur = JsonValue::Object(h); + let mut h: Map = Map::new(); + h.insert(parent.to_string(), Value::Array(inner)); + cur = Value::Object(h); } - let mut outer: Vec = Vec::new(); + let mut outer: Vec = Vec::new(); outer.push(cur); - JsonValue::Array(outer) + Value::Array(outer) } )); -complete_named!(hcl_value_hash, map!(hcl_hash, |h| JsonValue::Object(h))); +complete_named!(hcl_value_hash, map!(hcl_hash, |h| Value::Object(h))); -complete_named!(hcl_array>, delimited!( +complete_named!(hcl_array>, delimited!( tag!("["), do_parse!( init: fold_many0!( @@ -292,21 +290,21 @@ complete_named!(hcl_array>, delimited!( tag!("]") )); -complete_named!(hcl_value, alt!( - hcl_hash => { |h| JsonValue::Object(h) } | - hcl_array => { |v| JsonValue::Array(v) } | - hcl_quoted_escaped_string => { |s| JsonValue::Str(s) } | - hcl_multiline_string => { |s| JsonValue::Str(s) } | - number => { |num| JsonValue::Num(num) } | - boolean => { |b| JsonValue::Boolean(b) } +complete_named!(hcl_value, alt!( + hcl_hash => { |h| Value::Object(h) } | + hcl_array => { |v| Value::Array(v) } | + hcl_quoted_escaped_string => { |s| Value::String(s) } | + hcl_multiline_string => { |s| Value::String(s) } | + number => { |num| Value::Number(Number::from_f64(num as f64).unwrap()) } | + boolean => { |b| Value::Bool(b) } )); #[test] fn hcl_hex_num() { let test = "foo = 0x42"; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Num(ref resp)) = dict.get("foo") { - return assert_eq!(66., *resp); + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::Number(ref resp)) = dict.get("foo") { + return assert_eq!(Number::from_f64(66.).unwrap(), *resp); } } panic!("object did not parse"); @@ -316,8 +314,8 @@ fn hcl_hex_num() { #[test] fn hcl_string_empty() { let test = "foo = \"\""; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Str(ref resp)) = dict.get("foo") { + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::String(ref resp)) = dict.get("foo") { return assert_eq!("", resp); } } @@ -327,8 +325,8 @@ fn hcl_string_empty() { #[test] fn hcl_string_with_escaped_quote_test() { let test = "foo = \"bar\\\"foo\""; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Str(ref resp)) = dict.get("foo") { + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::String(ref resp)) = dict.get("foo") { return assert_eq!("bar\"foo", resp); } } @@ -338,8 +336,8 @@ fn hcl_string_with_escaped_quote_test() { #[test] fn hcl_string_with_escaped_newline_test() { let test = "foo = \"bar\\nfoo\""; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Str(ref resp)) = dict.get("foo") { + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::String(ref resp)) = dict.get("foo") { return assert_eq!("bar\nfoo", resp); } } @@ -349,8 +347,8 @@ fn hcl_string_with_escaped_newline_test() { #[test] fn hcl_string_with_space_test() { let test = "foo = \"bar foo\""; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Str(ref resp)) = dict.get("foo") { + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::String(ref resp)) = dict.get("foo") { return assert_eq!("bar foo", resp); } } @@ -360,8 +358,8 @@ fn hcl_string_with_space_test() { #[test] fn hcl_string_with_template_test() { let test = "foo = \"${bar\"foo}\""; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Str(ref resp)) = dict.get("foo") { + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::String(ref resp)) = dict.get("foo") { return assert_eq!("${bar\"foo}", resp); } } @@ -371,8 +369,8 @@ fn hcl_string_with_template_test() { #[test] fn hcl_string_with_escapes_and_template_test() { let test = "foo = \"wow\\\"wow${bar\"foo}\""; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Str(ref resp)) = dict.get("foo") { + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::String(ref resp)) = dict.get("foo") { return assert_eq!("wow\"wow${bar\"foo}", resp); } } @@ -382,8 +380,8 @@ fn hcl_string_with_escapes_and_template_test() { #[test] fn hcl_string_multi_with_template() { let test = "foo = \"wow\"\nbar= \"${bar\"foo}\""; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Str(ref resp)) = dict.get("foo") { + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::String(ref resp)) = dict.get("foo") { return assert_eq!("wow", resp); } } @@ -394,11 +392,11 @@ fn hcl_string_multi_with_template() { #[test] fn hcl_block_empty_key() { let test = "foo \"\" {\nbar = 1\n}"; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Object(ref dict)) = dict.get("foo") { - if let Some(&JsonValue::Object(ref dict)) = dict.get("") { - if let Some(&JsonValue::Num(ref resp)) = dict.get("bar") { - return assert_eq!(1., *resp); + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::Object(ref dict)) = dict.get("foo") { + if let Some(&Value::Object(ref dict)) = dict.get("") { + if let Some(&Value::Number(ref resp)) = dict.get("bar") { + return assert_eq!(Number::from_f64(1.).unwrap(), *resp); } } } @@ -410,13 +408,13 @@ fn hcl_block_empty_key() { #[test] fn hcl_block_empty_key() { let test = "foo \"\" {\nbar = 1\n}"; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Array(ref array)) = dict.get("foo") { - if let Some(&JsonValue::Object(ref dict)) = array.get(0) { - if let Some(&JsonValue::Array(ref array)) = dict.get("") { - if let Some(&JsonValue::Object(ref dict)) = array.get(0) { - if let Some(&JsonValue::Num(ref resp)) = dict.get("bar") { - return assert_eq!(1., *resp); + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::Array(ref array)) = dict.get("foo") { + if let Some(&Value::Object(ref dict)) = array.get(0) { + if let Some(&Value::Array(ref array)) = dict.get("") { + if let Some(&Value::Object(ref dict)) = array.get(0) { + if let Some(&Value::Number(ref resp)) = dict.get("bar") { + return assert_eq!(Number::from_f64(1.).unwrap(), *resp); } } } @@ -430,10 +428,10 @@ fn hcl_block_empty_key() { #[test] fn hcl_block_key() { let test = "potato \"salad\\\"is\" {\nnot = \"real\"\n}"; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Object(ref dict)) = dict.get("potato") { - if let Some(&JsonValue::Object(ref dict)) = dict.get("salad\"is") { - if let Some(&JsonValue::Str(ref resp)) = dict.get("not") { + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::Object(ref dict)) = dict.get("potato") { + if let Some(&Value::Object(ref dict)) = dict.get("salad\"is") { + if let Some(&Value::String(ref resp)) = dict.get("not") { return assert_eq!("real", resp); } } @@ -446,12 +444,12 @@ fn hcl_block_key() { #[test] fn hcl_block_key() { let test = "potato \"salad\\\"is\" {\nnot = \"real\"\n}"; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Array(ref array)) = dict.get("potato") { - if let Some(&JsonValue::Object(ref dict)) = array.get(0) { - if let Some(&JsonValue::Array(ref array)) = dict.get("salad\"is") { - if let Some(&JsonValue::Object(ref dict)) = array.get(0) { - if let Some(&JsonValue::Str(ref resp)) = dict.get("not") { + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::Array(ref array)) = dict.get("potato") { + if let Some(&Value::Object(ref dict)) = array.get(0) { + if let Some(&Value::Array(ref array)) = dict.get("salad\"is") { + if let Some(&Value::Object(ref dict)) = array.get(0) { + if let Some(&Value::String(ref resp)) = dict.get("not") { return assert_eq!("real", resp); } } @@ -466,11 +464,11 @@ fn hcl_block_key() { #[test] fn hcl_block_nested_key() { let test = "potato \"salad\" \"is\" {\nnot = \"real\"\n}"; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Object(ref dict)) = dict.get("potato") { - if let Some(&JsonValue::Object(ref dict)) = dict.get("salad") { - if let Some(&JsonValue::Object(ref dict)) = dict.get("is") { - if let Some(&JsonValue::Str(ref resp)) = dict.get("not") { + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::Object(ref dict)) = dict.get("potato") { + if let Some(&Value::Object(ref dict)) = dict.get("salad") { + if let Some(&Value::Object(ref dict)) = dict.get("is") { + if let Some(&Value::String(ref resp)) = dict.get("not") { return assert_eq!("real", resp); } } @@ -484,14 +482,14 @@ fn hcl_block_nested_key() { #[test] fn hcl_block_nested_key() { let test = "potato \"salad\" \"is\" {\nnot = \"real\"\n}"; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Array(ref array)) = dict.get("potato") { - if let Some(&JsonValue::Object(ref dict)) = array.get(0) { - if let Some(&JsonValue::Array(ref array)) = dict.get("salad") { - if let Some(&JsonValue::Object(ref dict)) = array.get(0) { - if let Some(&JsonValue::Array(ref array)) = dict.get("is") { - if let Some(&JsonValue::Object(ref dict)) = array.get(0) { - if let Some(&JsonValue::Str(ref resp)) = dict.get("not") { + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::Array(ref array)) = dict.get("potato") { + if let Some(&Value::Object(ref dict)) = array.get(0) { + if let Some(&Value::Array(ref array)) = dict.get("salad") { + if let Some(&Value::Object(ref dict)) = array.get(0) { + if let Some(&Value::Array(ref array)) = dict.get("is") { + if let Some(&Value::Object(ref dict)) = array.get(0) { + if let Some(&Value::String(ref resp)) = dict.get("not") { return assert_eq!("real", resp); } } @@ -507,15 +505,15 @@ fn hcl_block_nested_key() { #[test] fn hcl_key_chars() { let test = "foo_bar = \"bar\""; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Str(ref resp)) = dict.get("foo_bar") { + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::String(ref resp)) = dict.get("foo_bar") { return assert_eq!("bar", resp); } } let test = "foo_bar = \"bar\""; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Str(ref resp)) = dict.get("foo_bar") { + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::String(ref resp)) = dict.get("foo_bar") { return assert_eq!("bar", resp); } } @@ -533,15 +531,15 @@ fn hcl_slice_expand() { service \"bar\" { key = \"value\" }"; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Object(ref dict)) = dict.get("service") { + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::Object(ref dict)) = dict.get("service") { let mut pass = false; - if let Some(&JsonValue::Object(_)) = dict.get("foo") { + if let Some(&Value::Object(_)) = dict.get("foo") { pass = true; } if !pass { panic!("missing nested object") } pass = false; - if let Some(&JsonValue::Object(_)) = dict.get("bar") { + if let Some(&Value::Object(_)) = dict.get("bar") { pass = true; } if !pass { panic!("missing nested object") } @@ -561,15 +559,15 @@ fn hcl_slice_expand() { service \"bar\" { key = \"value\" }"; - if let Ok(JsonValue::Object(dict)) = parse_hcl(test) { - if let Some(&JsonValue::Array(ref array)) = dict.get("service") { + if let Ok(Value::Object(dict)) = parse_hcl(test) { + if let Some(&Value::Array(ref array)) = dict.get("service") { let mut pass = false; - if let Some(&JsonValue::Object(_)) = array.get(0) { + if let Some(&Value::Object(_)) = array.get(0) { pass = true; } if !pass { panic!("missing nested object") } pass = false; - if let Some(&JsonValue::Object(_)) = array.get(1) { + if let Some(&Value::Object(_)) = array.get(1) { pass = true; } if !pass { panic!("missing nested object") } diff --git a/src/json.rs b/src/json.rs index 8373a31..b1c6a2e 100644 --- a/src/json.rs +++ b/src/json.rs @@ -1,7 +1,6 @@ //! This was modified from https://github.com/Geal/nom/blob/master/tests/json.rs //! Copyright (c) 2015-2016 Geoffroy Couprie - MIT License -use std::collections::HashMap; use std::str; use nom; @@ -9,20 +8,20 @@ use nom::types::CompleteStr; use nom::Needed; use common::{boolean, float}; -use types::{JsonValue, ParseError}; +use types::{Map, Number, ParseError, Value}; // NOTE this json parser is only included for internal verification purposes // the standard hcl parser by hashicorp includes a nonstandrd json parser // this is not intended to mirror that -pub fn parse_json(config: &str) -> Result { +pub fn parse_json(config: &str) -> Result { match json(CompleteStr(config)) { Ok((_, c)) => Ok(c), _ => Err(0) } } -complete_named!(json, map!(json_hash, |h| JsonValue::Object(h))); +complete_named!(json, map!(json_hash, |h| Value::Object(h))); complete_named!(json_escaped_string, escaped_transform!(is_not!("\\\"\n"), '\\', alt!( @@ -38,22 +37,22 @@ complete_named!(json_string, delimited!( tag!("\"") )); -complete_named!(json_array>, ws!(delimited!( +complete_named!(json_array>, ws!(delimited!( tag!("["), separated_list!(tag!(","), json_value), tag!("]") ))); -complete_named!(json_key_value<(String, JsonValue)>, ws!(separated_pair!(json_string, tag!(":"), json_value))); +complete_named!(json_key_value<(String, Value)>, ws!(separated_pair!(json_string, tag!(":"), json_value))); -complete_named!(json_hash>, ws!(map!( +complete_named!(json_hash>, ws!(map!( delimited!( tag!("{"), separated_list!(tag!(","), json_key_value), tag!("}") ), |tuple_vec| { - let mut h: HashMap = HashMap::new(); + let mut h: Map = Map::new(); for (k, v) in tuple_vec { h.insert(String::from(k), v); } @@ -61,12 +60,12 @@ complete_named!(json_hash>, ws!(map!( } ))); -complete_named!(json_value, ws!(alt!( - json_hash => { |h| JsonValue::Object(h) } | - json_array => { |v| JsonValue::Array(v) } | - json_string => { |s| JsonValue::Str(String::from(s)) } | - float => { |num| JsonValue::Num(num) } | - boolean => { |b| JsonValue::Boolean(b) } +complete_named!(json_value, ws!(alt!( + json_hash => { |h| Value::Object(h) } | + json_array => { |v| Value::Array(v) } | + json_string => { |s| Value::String(String::from(s)) } | + float => { |num| Value::Number(Number::from_f64(num as f64).unwrap()) } | + boolean => { |b| Value::Bool(b) } ))); #[test] @@ -75,11 +74,11 @@ fn json_bool_test() { \"b\": \"false\" }"; - if let Ok(JsonValue::Object(dict)) = parse_json(test) { - if let Some(&JsonValue::Boolean(ref resp)) = dict.get("a") { + if let Ok(Value::Object(dict)) = parse_json(test) { + if let Some(&Value::Bool(ref resp)) = dict.get("a") { assert_eq!(true, *resp); } - if let Some(&JsonValue::Boolean(ref resp)) = dict.get("b") { + if let Some(&Value::Bool(ref resp)) = dict.get("b") { assert_eq!(false, *resp); } return @@ -94,11 +93,11 @@ fn json_hash_test() { \"b\": \"x\" }"; - if let Ok(JsonValue::Object(dict)) = parse_json(test) { - if let Some(&JsonValue::Num(ref resp)) = dict.get("a") { - assert_eq!(42., *resp); + if let Ok(Value::Object(dict)) = parse_json(test) { + if let Some(&Value::Number(ref resp)) = dict.get("a") { + assert_eq!(Number::from_f64(42.).unwrap(), *resp); } - if let Some(&JsonValue::Str(ref resp)) = dict.get("b") { + if let Some(&Value::String(ref resp)) = dict.get("b") { assert_eq!("x", *resp); } return @@ -114,23 +113,23 @@ fn json_parse_example_test() { } }"; - if let Ok(JsonValue::Object(dict)) = parse_json(test) { - if let Some(&JsonValue::Num(ref resp)) = dict.get("a") { - assert_eq!(42., *resp); + if let Ok(Value::Object(dict)) = parse_json(test) { + if let Some(&Value::Number(ref resp)) = dict.get("a") { + assert_eq!(Number::from_f64(42.).unwrap(), *resp); } - if let Some(&JsonValue::Array(ref arr)) = dict.get("b") { - if let Some(&JsonValue::Str(ref resp)) = arr.get(0) { + if let Some(&Value::Array(ref arr)) = dict.get("b") { + if let Some(&Value::String(ref resp)) = arr.get(0) { assert_eq!("x", *resp); } - if let Some(&JsonValue::Str(ref resp)) = arr.get(1) { + if let Some(&Value::String(ref resp)) = arr.get(1) { assert_eq!("y", *resp); } - if let Some(&JsonValue::Num(ref resp)) = arr.get(2) { - assert_eq!(12., *resp); + if let Some(&Value::Number(ref resp)) = arr.get(2) { + assert_eq!(Number::from_f64(12.).unwrap(), *resp); } } - if let Some(&JsonValue::Object(ref dict)) = dict.get("c") { - if let Some(&JsonValue::Str(ref resp)) = dict.get("hello") { + if let Some(&Value::Object(ref dict)) = dict.get("c") { + if let Some(&Value::String(ref resp)) = dict.get("hello") { assert_eq!("world", *resp); } } diff --git a/src/lib.rs b/src/lib.rs index fd4cb9b..552a81e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,9 @@ #[macro_use] extern crate nom; +#[cfg(feature="withserde")] +extern crate serde_json; + pub mod types; #[macro_use] diff --git a/src/types.rs b/src/types.rs index 454227f..7784fc3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,20 +1,50 @@ -use std::collections::HashMap; use std::fmt; +#[cfg(feature="withserde")] +pub use serde_json::{Map, Number, Value}; + +//#[cfg(not(feature="serde"))] +use std::collections::HashMap; + +#[cfg(not(feature="withserde"))] +pub type Map = HashMap; + +#[cfg(not(feature="withserde"))] #[derive(Clone, Debug, PartialEq)] -pub enum JsonValue { - Str(String), - Num(f32), - Array(Vec), - Object(HashMap), - Boolean(bool), +pub struct Number { + n: N, } +#[cfg(not(feature="withserde"))] +#[derive(Clone, Debug, PartialEq)] +pub enum N { + Float(f64) +} + +#[cfg(not(feature="withserde"))] +impl Number { + pub fn from_f64(f: f64) -> Option { + Some(Number { n: N::Float(f) }) + } +} + +#[cfg(not(feature="withserde"))] +#[derive(Clone, Debug, PartialEq)] +pub enum Value { + Null, + Bool(bool), + Number(Number), + String(String), + Array(Vec), + Object(Map), +} + +#[cfg(not(feature="withserde"))] #[allow(unused_must_use)] -impl fmt::Display for JsonValue { +impl fmt::Display for Value { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - JsonValue::Object(ref obj) => { + Value::Object(ref obj) => { "{".fmt(f); for (n, prop) in obj.iter().enumerate() { if n != 0 { @@ -28,7 +58,7 @@ impl fmt::Display for JsonValue { "}".fmt(f); Result::Ok(()) } - JsonValue::Array(ref arr) => { + Value::Array(ref arr) => { "[".fmt(f); for (n, item) in arr.iter().enumerate() { if n != 0 { @@ -39,10 +69,16 @@ impl fmt::Display for JsonValue { "]".fmt(f); Result::Ok(()) } - JsonValue::Str(ref string) => write!(f, "\"{}\"", string.escape_default()), - JsonValue::Num(number) => write!(f, "{}", number.to_string()), - JsonValue::Boolean(boolean) => write!(f, "{}", boolean.to_string()), + Value::String(ref string) => write!(f, "\"{}\"", string.escape_default()), + Value::Number(ref number) => { + match number.n { + N::Float(number) => write!(f, "{}", number.to_string()) + } + } + Value::Bool(boolean) => write!(f, "{}", boolean.to_string()), + Value::Null => write!(f, "null"), } } } + pub type ParseError = u32; diff --git a/tests/integration_tests.rs b/tests/fixture_tests.rs similarity index 100% rename from tests/integration_tests.rs rename to tests/fixture_tests.rs diff --git a/tests/fixtures.rs b/tests/fixtures.rs new file mode 100644 index 0000000..addb937 --- /dev/null +++ b/tests/fixtures.rs @@ -0,0 +1,96 @@ +extern crate molysite; + +use std::fs::File; +use std::io::prelude::*; +use std::path::Path; + +use molysite::hcl::parse_hcl; +use molysite::json::parse_json; + +#[cfg(feature="arraynested")] +macro_rules! fixture_tests { + ($($name:ident: $value:expr,)*) => { + $( + #[test] + fn $name() { + let (case, expect_pass) = $value; + test_fixture(case, expect_pass); + } + )* + } +} + +#[cfg(feature="arraynested")] +fixture_tests! { + test_fixture_assign_deep: ("assign_deep", true), + test_fixture_basic: ("basic", true), + test_fixture_basic_int_string: ("basic_int_string", true), + test_fixture_basic_squish: ("basic_squish", true), + //test_fixture_block_assign: ("block_assign", false), + test_fixture_decode_policy: ("decode_policy", true), + test_fixture_decode_tf_variable: ("decode_tf_variable", true), + test_fixture_empty: ("empty", true), + test_fixture_escape: ("escape", true), + test_fixture_escape_backslash: ("escape_backslash", true), + test_fixture_flat: ("flat", true), + test_fixture_float: ("float", true), + //test_fixture_git_crypt: ("git_crypt", false), + test_fixture_list_of_lists: ("list_of_lists", true), + test_fixture_list_of_maps: ("list_of_maps", true), + test_fixture_multiline: ("multiline", true), + //test_fixture_multiline_bad: ("multiline_bad", false), + test_fixture_multiline_indented: ("multiline_indented", true), + //test_fixture_multiline_literal: ("multiline_literal", false), + test_fixture_multiline_literal_with_hil: ("multiline_literal_with_hil", true), + test_fixture_multiline_no_eof: ("multiline_no_eof", true), + test_fixture_multiline_no_hanging_indent: ("multiline_no_hanging_indent", true), + //test_fixture_multiline_no_marker: ("multiline_no_marker", false), + test_fixture_nested_block_comment: ("nested_block_comment", true), + //test_fixture_nested_provider_bad: ("nested_provider_bad", false), + test_fixture_object_with_bool: ("object_with_bool", true), + test_fixture_scientific: ("scientific", true), + test_fixture_slice_expand: ("slice_expand", true), + //test_fixture_structure2: ("structure2", false), + test_fixture_structure: ("structure", true), + test_fixture_structure_flatmap: ("structure_flatmap", true), + test_fixture_structure_list: ("structure_list", true), + test_fixture_structure_multi: ("structure_multi", true), + test_fixture_terraform_heroku: ("terraform_heroku", true), + test_fixture_tfvars: ("tfvars", true), + //test_fixture_unterminated_block_comment: ("unterminated_block_comment", false), + //test_fixture_unterminated_brace: ("unterminated_brace", false), +} + +#[cfg(feature="arraynested")] +#[allow(dead_code)] +fn test_fixture(case: &str, expect_pass: bool) { + let mut hcl = String::new(); + let mut json = String::new(); + + let hcl_path = format!("tests/test-fixtures/{}.hcl", case); + let json_path = format!("tests/test-fixtures/{}.hcl.json", case); + + let path = Path::new(&hcl_path); + let mut file = File::open(&path).unwrap(); + file.read_to_string(&mut hcl).unwrap(); + + if expect_pass { + let path = Path::new(&json_path); + let mut file = File::open(&path).unwrap(); + file.read_to_string(&mut json).unwrap(); + } + + let parsed_hcl = parse_hcl(&hcl); + if let Ok(parsed_hcl) = parsed_hcl { + if expect_pass { + let parsed_json = parse_json(&json).unwrap(); + assert_eq!(parsed_hcl, parsed_json); + } else { + panic!("Expected failure") + } + } else { + if expect_pass { + panic!("Expected success") + } + } +} diff --git a/tests/serde.rs b/tests/serde.rs new file mode 100644 index 0000000..a8fe522 --- /dev/null +++ b/tests/serde.rs @@ -0,0 +1,93 @@ +extern crate serde; + +#[cfg(feature="withserde")] +extern crate serde_json; + +#[allow(unused_imports)] +#[macro_use] +extern crate serde_derive; + +extern crate molysite; + +#[allow(unused_imports)] +use std::collections::HashMap; + +#[allow(unused_imports)] +use molysite::hcl::parse_hcl; + +#[cfg(feature="withserde")] +#[test] +fn hcl_serde_basic() { + #[derive(Deserialize, Debug)] + struct User { + fingerprint: String, + location: String, + } + + let test = "fingerprint = \"foo\" +location = \"bar\""; + + if let Ok(j) = parse_hcl(test) { + let u: User = serde_json::from_value(j).unwrap(); + assert_eq!("foo", u.fingerprint); + assert_eq!("bar", u.location); + } +} + +#[cfg(not(feature="arraynested"))] +#[cfg(feature="withserde")] +#[test] +fn hcl_serde_policy() { + #[derive(Deserialize, Debug)] + struct PolicyResp { + key: HashMap, + } + + #[derive(Deserialize, Debug)] + struct Policy { + policy: String, + options: Option>, + } + + let test = "key \"\" { + policy = \"read\" +} + +key \"foo/\" { + policy = \"write\" + options = [\"list\", \"edit\"] +} + +key \"foo/bar/\" { + policy = \"read\" +} + +key \"foo/bar/baz\" { + policy = \"deny\" +}"; + + if let Ok(j) = parse_hcl(test) { + let u: PolicyResp = serde_json::from_value(j).unwrap(); + println!("{:?}", u); + if let Some(policy) = u.key.get("") { + assert_eq!(policy.policy, "read"); + } else { + panic!("missing key"); + } + if let Some(policy) = u.key.get("foo/") { + assert_eq!(policy.policy, "write"); + } else { + panic!("missing key"); + } + if let Some(policy) = u.key.get("foo/bar/") { + assert_eq!(policy.policy, "read"); + } else { + panic!("missing key"); + } + if let Some(policy) = u.key.get("foo/bar/baz") { + assert_eq!(policy.policy, "deny"); + } else { + panic!("missing key"); + } + } +} From 6e7a7e98255d371f2bb24764d0c9226f49de3ada Mon Sep 17 00:00:00 2001 From: eV Date: Sat, 23 Mar 2019 01:19:23 +0000 Subject: [PATCH 4/7] looks like upstream nom issues were fixed? --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 429aeed..8fa5dcf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,7 @@ name = "molysite" version = "0.1.0" [dependencies.nom] -features = ["nightly"] -path = "../nom" +version = "^4.2" [dependencies.serde_json] optional = true From 81eaae6c78b758e1a415a0c47e1a37c07311f12d Mon Sep 17 00:00:00 2001 From: eV Date: Sat, 23 Mar 2019 01:31:44 +0000 Subject: [PATCH 5/7] fixup warnings --- Cargo.toml | 1 + src/hcl.rs | 6 +- src/json.rs | 1 - tests/fixtures.rs | 144 +++++++++++++++++++++++----------------------- 4 files changed, 76 insertions(+), 76 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f4dadb7..892b763 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,4 @@ version = "1.0.27" [features] arraynested = [] withserde = ["serde_json"] +default = ["arraynested"] diff --git a/src/hcl.rs b/src/hcl.rs index 1d28163..4e9c7f2 100644 --- a/src/hcl.rs +++ b/src/hcl.rs @@ -3,7 +3,7 @@ use std::string::String; use nom; use nom::types::CompleteStr; -use nom::{alphanumeric, eol, multispace, not_line_ending, ExtendInto, Needed}; +use nom::{alphanumeric, eol, multispace, not_line_ending, ExtendInto}; use crate::common::{boolean, number}; use crate::types::{Map, Number, ParseError, Value}; @@ -104,7 +104,7 @@ complete_named!( let mut min_indent = 80; if let Some(_) = indent { for (i, line) in lines.clone().into_iter().enumerate() { - let indent_num = line.len() - line.trim_left().len(); + let indent_num = line.len() - line.trim_start().len(); if indent_num < min_indent { min_indent = indent_num; } @@ -232,7 +232,7 @@ complete_named!( if let Some(mut current) = top.remove(&k) { if let Value::Object(ref mut top_dict) = current { let mut copy = top_dict.clone(); - let mut val_copy = val_dict.clone(); + let val_copy = val_dict.clone(); copy.extend(val_copy); top.insert(k, Value::Object(copy)); continue; diff --git a/src/json.rs b/src/json.rs index 183558e..fa64093 100644 --- a/src/json.rs +++ b/src/json.rs @@ -5,7 +5,6 @@ use std::str; use nom; use nom::types::CompleteStr; -use nom::Needed; use crate::common::{boolean, float}; use crate::types::{Map, Number, ParseError, Value}; diff --git a/tests/fixtures.rs b/tests/fixtures.rs index 0df1ddc..b7b078d 100644 --- a/tests/fixtures.rs +++ b/tests/fixtures.rs @@ -1,14 +1,15 @@ extern crate molysite; -use std::fs::File; -use std::io::prelude::*; -use std::path::Path; +#[cfg(feature = "arraynested")] +mod fixtures { + use std::fs::File; + use std::io::prelude::*; + use std::path::Path; -use molysite::hcl::parse_hcl; -use molysite::json::parse_json; + use molysite::hcl::parse_hcl; + use molysite::json::parse_json; -#[cfg(feature = "arraynested")] -macro_rules! fixture_tests { + macro_rules! fixture_tests { ($($name:ident: $value:expr,)*) => { $( #[test] @@ -18,79 +19,78 @@ macro_rules! fixture_tests { } )* } -} - -#[cfg(feature = "arraynested")] -fixture_tests! { - test_fixture_assign_deep: ("assign_deep", true), - test_fixture_basic: ("basic", true), - test_fixture_basic_int_string: ("basic_int_string", true), - test_fixture_basic_squish: ("basic_squish", true), - //test_fixture_block_assign: ("block_assign", false), - test_fixture_decode_policy: ("decode_policy", true), - test_fixture_decode_tf_variable: ("decode_tf_variable", true), - test_fixture_empty: ("empty", true), - test_fixture_escape: ("escape", true), - test_fixture_escape_backslash: ("escape_backslash", true), - test_fixture_flat: ("flat", true), - test_fixture_float: ("float", true), - //test_fixture_git_crypt: ("git_crypt", false), - test_fixture_list_of_lists: ("list_of_lists", true), - test_fixture_list_of_maps: ("list_of_maps", true), - test_fixture_multiline: ("multiline", true), - //test_fixture_multiline_bad: ("multiline_bad", false), - test_fixture_multiline_indented: ("multiline_indented", true), - //test_fixture_multiline_literal: ("multiline_literal", false), - test_fixture_multiline_literal_with_hil: ("multiline_literal_with_hil", true), - test_fixture_multiline_no_eof: ("multiline_no_eof", true), - test_fixture_multiline_no_hanging_indent: ("multiline_no_hanging_indent", true), - //test_fixture_multiline_no_marker: ("multiline_no_marker", false), - test_fixture_nested_block_comment: ("nested_block_comment", true), - //test_fixture_nested_provider_bad: ("nested_provider_bad", false), - test_fixture_object_with_bool: ("object_with_bool", true), - test_fixture_scientific: ("scientific", true), - test_fixture_slice_expand: ("slice_expand", true), - //test_fixture_structure2: ("structure2", false), - test_fixture_structure: ("structure", true), - test_fixture_structure_flatmap: ("structure_flatmap", true), - test_fixture_structure_list: ("structure_list", true), - test_fixture_structure_multi: ("structure_multi", true), - test_fixture_terraform_heroku: ("terraform_heroku", true), - test_fixture_tfvars: ("tfvars", true), - //test_fixture_unterminated_block_comment: ("unterminated_block_comment", false), - //test_fixture_unterminated_brace: ("unterminated_brace", false), -} + } -#[cfg(feature = "arraynested")] -#[allow(dead_code)] -fn test_fixture(case: &str, expect_pass: bool) { - let mut hcl = String::new(); - let mut json = String::new(); + fixture_tests! { + test_fixture_assign_deep: ("assign_deep", true), + test_fixture_basic: ("basic", true), + test_fixture_basic_int_string: ("basic_int_string", true), + test_fixture_basic_squish: ("basic_squish", true), + //test_fixture_block_assign: ("block_assign", false), + test_fixture_decode_policy: ("decode_policy", true), + test_fixture_decode_tf_variable: ("decode_tf_variable", true), + test_fixture_empty: ("empty", true), + test_fixture_escape: ("escape", true), + test_fixture_escape_backslash: ("escape_backslash", true), + test_fixture_flat: ("flat", true), + test_fixture_float: ("float", true), + //test_fixture_git_crypt: ("git_crypt", false), + test_fixture_list_of_lists: ("list_of_lists", true), + test_fixture_list_of_maps: ("list_of_maps", true), + test_fixture_multiline: ("multiline", true), + //test_fixture_multiline_bad: ("multiline_bad", false), + test_fixture_multiline_indented: ("multiline_indented", true), + //test_fixture_multiline_literal: ("multiline_literal", false), + test_fixture_multiline_literal_with_hil: ("multiline_literal_with_hil", true), + test_fixture_multiline_no_eof: ("multiline_no_eof", true), + test_fixture_multiline_no_hanging_indent: ("multiline_no_hanging_indent", true), + //test_fixture_multiline_no_marker: ("multiline_no_marker", false), + test_fixture_nested_block_comment: ("nested_block_comment", true), + //test_fixture_nested_provider_bad: ("nested_provider_bad", false), + test_fixture_object_with_bool: ("object_with_bool", true), + test_fixture_scientific: ("scientific", true), + test_fixture_slice_expand: ("slice_expand", true), + //test_fixture_structure2: ("structure2", false), + test_fixture_structure: ("structure", true), + test_fixture_structure_flatmap: ("structure_flatmap", true), + test_fixture_structure_list: ("structure_list", true), + test_fixture_structure_multi: ("structure_multi", true), + test_fixture_terraform_heroku: ("terraform_heroku", true), + test_fixture_tfvars: ("tfvars", true), + //test_fixture_unterminated_block_comment: ("unterminated_block_comment", false), + //test_fixture_unterminated_brace: ("unterminated_brace", false), + } - let hcl_path = format!("tests/test-fixtures/{}.hcl", case); - let json_path = format!("tests/test-fixtures/{}.hcl.json", case); + #[allow(dead_code)] + fn test_fixture(case: &str, expect_pass: bool) { + let mut hcl = String::new(); + let mut json = String::new(); - let path = Path::new(&hcl_path); - let mut file = File::open(&path).unwrap(); - file.read_to_string(&mut hcl).unwrap(); + let hcl_path = format!("tests/test-fixtures/{}.hcl", case); + let json_path = format!("tests/test-fixtures/{}.hcl.json", case); - if expect_pass { - let path = Path::new(&json_path); + let path = Path::new(&hcl_path); let mut file = File::open(&path).unwrap(); - file.read_to_string(&mut json).unwrap(); - } + file.read_to_string(&mut hcl).unwrap(); - let parsed_hcl = parse_hcl(&hcl); - if let Ok(parsed_hcl) = parsed_hcl { if expect_pass { - let parsed_json = parse_json(&json).unwrap(); - assert_eq!(parsed_hcl, parsed_json); - } else { - panic!("Expected failure") + let path = Path::new(&json_path); + let mut file = File::open(&path).unwrap(); + file.read_to_string(&mut json).unwrap(); } - } else { - if expect_pass { - panic!("Expected success") + + let parsed_hcl = parse_hcl(&hcl); + if let Ok(parsed_hcl) = parsed_hcl { + if expect_pass { + let parsed_json = parse_json(&json).unwrap(); + assert_eq!(parsed_hcl, parsed_json); + } else { + panic!("Expected failure") + } + } else { + if expect_pass { + panic!("Expected success") + } } } } From b56de101c2f1ed537af15423bbcab9bba50470c8 Mon Sep 17 00:00:00 2001 From: eV Date: Sat, 23 Mar 2019 01:33:28 +0000 Subject: [PATCH 6/7] add travis --- .travis.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..112c58f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,22 @@ +language: rust +branches: + only: + - master +matrix: + include: + - name: "stable rust" + rust: stable + before_script: + - rustup component add rustfmt + script: + - cargo fmt -- --check + - cargo build + - cargo test + - name: "nightly rust + rustfmt" + rust: nightly + script: + - cargo fmt -- --check + - cargo build + - cargo test + allow_failures: + - name: "stable rust" From e7ec86883cbe8cdc2ebcd2a4c65ec65912478e68 Mon Sep 17 00:00:00 2001 From: eV Date: Sat, 23 Mar 2019 01:38:31 +0000 Subject: [PATCH 7/7] add rustfmt for nightly --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 112c58f..d247577 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,8 @@ matrix: - cargo test - name: "nightly rust + rustfmt" rust: nightly + before_script: + - rustup component add rustfmt --toolchain nightly-x86_64-unknown-linux-gnu script: - cargo fmt -- --check - cargo build