From 18a4506ae83eb52869f0f65f642257550111fe74 Mon Sep 17 00:00:00 2001 From: Radu Date: Fri, 30 Sep 2022 15:40:20 -0700 Subject: [PATCH 1/2] Ignore the "ver" meta entry when decoding from JSON Ignore the "ver" meta entry when decoding from JSON Fixes issue https://github.com/j2inn/libhaystack/issues/10 --- Cargo.lock | 37 ++++++------- Cargo.toml | 2 +- src/haystack/encoding/json/decode.rs | 33 +++++++----- .../encoding/zinc/decode/complex/grid.rs | 5 +- src/haystack/encoding/zinc/encode.rs | 7 +-- src/haystack/val/grid.rs | 9 ++++ tests/json/test_grid.rs | 52 ++++++++++++++++++- tests/zinc/test_grid.rs | 15 ++++++ 8 files changed, 119 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bf6fd40..1d443b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -187,26 +187,24 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" +checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", "memoffset", - "once_cell", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -286,14 +284,13 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.48" +version = "0.1.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237a0714f28b1ee39ccec0770ccb544eb02c9ef2c82bb096230eefcffa6468b0" +checksum = "fd911b35d940d2bd0bea0f9100068e5b97b51a1cbe13d13382f132e0365257a0" dependencies = [ "android_system_properties", "core-foundation-sys", "js-sys", - "once_cell", "wasm-bindgen", "winapi", ] @@ -336,13 +333,13 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.133" +version = "0.2.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" +checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" [[package]] name = "libhaystack" -version = "1.0.8" +version = "1.0.9" dependencies = [ "chrono", "chrono-tz", @@ -529,9 +526,9 @@ checksum = "07e2192780e9f8e282049ff9bffcaa28171e1cb0844f49ed5374e518ae6024ec" [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" dependencies = [ "unicode-ident", ] @@ -637,9 +634,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.144" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" [[package]] name = "serde_cbor" @@ -653,9 +650,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.144" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" dependencies = [ "proc-macro2", "quote", @@ -687,9 +684,9 @@ checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "syn" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e" +checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 45db3f8..4b539e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libhaystack" -version = "1.0.8" +version = "1.0.9" description = "Rust implementation of the Haystack 4 data types, defs, filter, units, and encodings" authors = ["J2 Innovations", "Radu Racariu "] edition = "2021" diff --git a/src/haystack/encoding/json/decode.rs b/src/haystack/encoding/json/decode.rs index ced09ea..7f789e2 100644 --- a/src/haystack/encoding/json/decode.rs +++ b/src/haystack/encoding/json/decode.rs @@ -10,6 +10,7 @@ use crate::haystack::val::{ use crate::haystack::timezone::make_date_time_with_tz; use crate::units::get_unit; +use crate::val::GRID_FORMAT_VERSION; use chrono::{Offset, Utc}; use serde::de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Visitor}; @@ -300,52 +301,52 @@ impl<'de> Visitor<'de> for HValVisitor { match kind.as_str() { "number" => match parse_number(&dict) { - Ok(num) => Ok(num), + Ok(val) => Ok(val), Err(err) => Err(A::Error::custom(format!("Invalid Hayson Number. {err}"))), }, "ref" => match parse_ref(&dict) { - Ok(num) => Ok(num), + Ok(val) => Ok(val), Err(err) => Err(A::Error::custom(format!("Invalid Hayson Ref. {err}"))), }, "symbol" => match parse_symbol(&dict) { - Ok(num) => Ok(num), + Ok(val) => Ok(val), Err(err) => Err(A::Error::custom(format!("Invalid Hayson Symbol. {err}"))), }, "uri" => match parse_uri(&dict) { - Ok(num) => Ok(num), + Ok(val) => Ok(val), Err(err) => Err(A::Error::custom(format!("Invalid Hayson Uri. {err}"))), }, "date" => match parse_date(&dict) { - Ok(num) => Ok(num), + Ok(val) => Ok(val), Err(err) => Err(A::Error::custom(format!("Invalid Hayson Date. {err}"))), }, "time" => match parse_time(&dict) { - Ok(num) => Ok(num), + Ok(val) => Ok(val), Err(err) => Err(A::Error::custom(format!("Invalid Hayson Time. {err}"))), }, "dateTime" => match parse_datetime(&dict) { - Ok(num) => Ok(num), + Ok(val) => Ok(val), Err(err) => Err(A::Error::custom(format!("Invalid Hayson DateTime. {err}"))), }, "coord" => match parse_coord(&dict) { - Ok(num) => Ok(num), + Ok(val) => Ok(val), Err(err) => Err(A::Error::custom(format!("Invalid Hayson Coord. {err}"))), }, "xstr" => match parse_xstr(&dict) { - Ok(num) => Ok(num), + Ok(val) => Ok(val), Err(err) => Err(A::Error::custom(format!("Invalid Hayson XStr. {err}"))), }, "grid" => match parse_grid(&dict) { - Ok(num) => Ok(num), + Ok(val) => Ok(val), Err(err) => Err(A::Error::custom(format!("Invalid Hayson Grid. {err}"))), }, _ => Ok(HVal::make_dict(dict)), @@ -469,9 +470,16 @@ fn parse_grid(dict: &Dict) -> Result { match dict.get_list("rows") { Some(rows) => match dict.get_list("cols") { Some(cols) => { - let grid = Grid { - meta: dict.get_dict("meta").cloned(), + let mut grid_ver = GRID_FORMAT_VERSION.to_string(); + let grid = Grid { + meta: dict.get_dict("meta").cloned().map(|mut meta| { + if let Some(ver) = meta.get_str("ver") { + grid_ver = ver.value.to_owned(); + }; + meta.remove("ver"); + meta + }), columns: { let cols: Result, JsonErr> = cols .iter() @@ -508,6 +516,7 @@ fn parse_grid(dict: &Dict) -> Result { .collect(); rows? }, + ver: grid_ver, }; Ok(grid.into()) } diff --git a/src/haystack/encoding/zinc/decode/complex/grid.rs b/src/haystack/encoding/zinc/decode/complex/grid.rs index c9889a3..dda92ad 100644 --- a/src/haystack/encoding/zinc/decode/complex/grid.rs +++ b/src/haystack/encoding/zinc/decode/complex/grid.rs @@ -58,7 +58,7 @@ fn parse_grid_content<'a, 'b: 'a, R: Read>( ) -> Result<(Grid, RowParser<'a, 'b, R>), Error> { let has_nested_grid_start = parse_nested_grid_start(parser)?; - parse_grid_ver(parser)?; + let ver = parse_grid_ver(parser)?; parser.lexer.read()?; @@ -74,6 +74,7 @@ fn parse_grid_content<'a, 'b: 'a, R: Read>( meta: if meta.is_empty() { None } else { Some(meta) }, columns, rows: Vec::default(), + ver, }; Ok(( @@ -432,6 +433,7 @@ mod test { } ], rows: vec![dict! {"id" => Value::make_ref("foo")}], + ver: GRID_FORMAT_VERSION.to_string() }) ) } @@ -472,6 +474,7 @@ mod test { dict! {"id" => Value::make_ref("foo")}, dict! {"id" => Value::make_ref("bar")} ], + ver: GRID_FORMAT_VERSION.to_string() }) ) } diff --git a/src/haystack/encoding/zinc/encode.rs b/src/haystack/encoding/zinc/encode.rs index cdd5e49..aaa7a00 100644 --- a/src/haystack/encoding/zinc/encode.rs +++ b/src/haystack/encoding/zinc/encode.rs @@ -4,14 +4,11 @@ use crate::haystack::val::{ Bool, Column, Coord, Date, DateTime, Dict, Grid, List, Marker, Na, Number, Ref, Remove, Str, - Symbol, Time, Uri, Value, XStr, + Symbol, Time, Uri, Value, XStr, GRID_FORMAT_VERSION, }; use chrono::SecondsFormat; use std::fmt::Display; -/// Zinc encoding version -pub const VER: f32 = 3.0; - /// Zinc encoding trait implemented by scalar and collection types pub trait ToZinc { fn to_zinc(&self, writer: &mut W) -> Result<()>; @@ -282,7 +279,7 @@ impl ZincEncode for Grid { writer.write_all(b"<<\n")?; } - writer.write_fmt(format_args!("ver:\"{:.1}\"\n", VER))?; + writer.write_fmt(format_args!("ver:\"{GRID_FORMAT_VERSION}\"\n"))?; // Grid meta if let Some(meta) = &self.meta { diff --git a/src/haystack/val/grid.rs b/src/haystack/val/grid.rs index d6bc89f..67caf73 100644 --- a/src/haystack/val/grid.rs +++ b/src/haystack/val/grid.rs @@ -18,6 +18,9 @@ pub struct Column { pub meta: Option, } +/// The version of the grid format supported by this library +pub const GRID_FORMAT_VERSION: &str = "3.0"; + /// Haystack Grid /// /// # Example @@ -53,6 +56,8 @@ pub struct Grid { pub columns: Vec, /// List of the row for this `Grid` pub rows: Vec, + /// The version of this grid + pub ver: String, } impl Grid { @@ -65,6 +70,7 @@ impl Grid { meta: None, }], rows: Vec::default(), + ver: GRID_FORMAT_VERSION.to_string(), } } @@ -91,6 +97,7 @@ impl Grid { meta: None, columns, rows, + ver: GRID_FORMAT_VERSION.to_string(), } } @@ -134,6 +141,7 @@ impl Grid { meta: None, }], rows: Vec::default(), + ver: GRID_FORMAT_VERSION.to_string(), } } @@ -164,6 +172,7 @@ impl Default for Grid { columns: Vec::default(), meta: None, rows: Vec::default(), + ver: GRID_FORMAT_VERSION.to_string(), } } } diff --git a/tests/json/test_grid.rs b/tests/json/test_grid.rs index 0d34af1..4890414 100644 --- a/tests/json/test_grid.rs +++ b/tests/json/test_grid.rs @@ -45,6 +45,7 @@ fn test_json_grid_empty_encode() { }, ], rows: recs, + ver: GRID_FORMAT_VERSION.to_string(), }); assert_eq!(value, grid); @@ -53,7 +54,7 @@ fn test_json_grid_empty_encode() { #[test] fn test_json_grid_decode() { let string = concat!( - r#"{"_kind":"grid","meta":{},"#, + r#"{"_kind":"grid","#, r#""cols":[{"name":"dis"},{"name":"equip"},{"name":"navName"},{"name":"site"}],"#, r#""rows":[{"dis":"Site","site":{"_kind":"marker"}},{"equip":{"_kind":"marker"},"navName":"Equip"}]}"# ); @@ -69,7 +70,7 @@ fn test_json_grid_decode() { }, ]; let grid = Value::make_grid(Grid { - meta: Some(Dict::default()), + meta: None, columns: vec![ Column { name: "dis".into(), @@ -89,11 +90,58 @@ fn test_json_grid_decode() { }, ], rows: recs, + ver: GRID_FORMAT_VERSION.to_string(), }); assert_eq!(value, grid); } +#[test] +fn test_json_grid_with_ver() { + let string = concat!( + r#"{"_kind":"grid","meta":{"ver": "3.0"},"#, + r#""cols":[{"name":"dis"}],"#, + r#""rows":[{"dis":"a"},{"dis":"b"}]}"# + ); + + let value: Value = serde_json::from_str(string).expect("Value"); + + let recs = vec![ + dict! { + "dis" => Value::make_str("a") + }, + dict! { + "dis" => Value::make_str("b") + }, + ]; + + assert_eq!( + value, + Grid::make_from_dicts_with_meta(recs.clone(), Dict::default()).into() + ); + + let string = concat!( + r#"{"_kind":"grid","meta":{"ver": "3.0", "foo": 100},"#, + r#""cols":[{"name":"dis"}],"#, + r#""rows":[{"dis":"a"},{"dis":"b"}]}"# + ); + let value: Value = serde_json::from_str(string).expect("Value"); + + assert_eq!( + value, + Grid::make_from_dicts_with_meta(recs.clone(), dict! {"foo" => 100.into()}).into() + ); + + let string = concat!( + r#"{"_kind":"grid","meta":{"ver": "2.0"},"#, + r#""cols":[{"name":"dis"}],"#, + r#""rows":[{"dis":"a"},{"dis":"b"}]}"# + ); + let value: Value = serde_json::from_str(string).expect("Value"); + + assert_ne!(value, Grid::make_from_dicts(recs).into()) +} + #[test] fn test_json_decode_malformed_grid() { let string = concat!( diff --git a/tests/zinc/test_grid.rs b/tests/zinc/test_grid.rs index 6c80e02..8ad0319 100644 --- a/tests/zinc/test_grid.rs +++ b/tests/zinc/test_grid.rs @@ -25,6 +25,21 @@ fn test_zinc_grid_empty_decode() { assert_eq!(value.unwrap(), Value::make_grid(Grid::make_empty())); } +#[test] +fn test_zinc_grid_version_decode() { + let string = concat!(r#"ver:"3.0""#, "\n", r#"empty"#, "\n"); + + let value = from_str(&string).expect("Grid"); + + assert!(matches!(value, Value::Grid(grid) if grid.ver == GRID_FORMAT_VERSION)); + + let string = concat!(r#"ver:"2.0""#, "\n", r#"empty"#, "\n"); + + let value = from_str(&string).expect("Grid"); + + assert!(matches!(value, Value::Grid(grid) if grid.ver == "2.0")); +} + #[test] fn test_zinc_grid_encode() { let recs = vec![ From 3e3ff2d453dddb9b2c63ddb5e46eed78fc22c2cc Mon Sep 17 00:00:00 2001 From: Radu Date: Fri, 30 Sep 2022 15:47:36 -0700 Subject: [PATCH 2/2] Additional tests for JSON ver Add more tests around decoding the grid version. --- tests/json/test_grid.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/json/test_grid.rs b/tests/json/test_grid.rs index 4890414..804d35f 100644 --- a/tests/json/test_grid.rs +++ b/tests/json/test_grid.rs @@ -120,6 +120,8 @@ fn test_json_grid_with_ver() { Grid::make_from_dicts_with_meta(recs.clone(), Dict::default()).into() ); + assert!(matches!(value, Value::Grid(grid) if grid.ver == GRID_FORMAT_VERSION)); + let string = concat!( r#"{"_kind":"grid","meta":{"ver": "3.0", "foo": 100},"#, r#""cols":[{"name":"dis"}],"#, @@ -139,6 +141,7 @@ fn test_json_grid_with_ver() { ); let value: Value = serde_json::from_str(string).expect("Value"); + assert!(matches!(&value, Value::Grid(grid) if grid.ver == "2.0")); assert_ne!(value, Grid::make_from_dicts(recs).into()) }