From 0b71e45072c7fed7d844fa14fd5665d851ed6de0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Thu, 4 Mar 2021 00:35:13 -0600 Subject: [PATCH] Preserve order when serializing/deserialize json by default. (#3126) --- Cargo.lock | 21 +- crates/nu-json/Cargo.toml | 14 +- crates/nu-json/src/builder.rs | 18 +- crates/nu-json/src/de.rs | 314 ++--- crates/nu-json/src/error.rs | 85 +- crates/nu-json/src/forward.rs | 38 - crates/nu-json/src/lib.rs | 3 - crates/nu-json/src/ser.rs | 616 +++++----- crates/nu-json/src/util.rs | 5 + crates/nu-json/src/value.rs | 1100 ++++++++---------- crates/nu-json/tests/main.rs | 214 ++++ crates/nu-test-support/src/fs.rs | 4 + tests/assets/nu_json/charset_result.hjson | 5 + tests/assets/nu_json/charset_result.json | 5 + tests/assets/nu_json/charset_test.hjson | 6 + tests/assets/nu_json/comments_result.hjson | 26 + tests/assets/nu_json/comments_result.json | 26 + tests/assets/nu_json/comments_test.hjson | 48 + tests/assets/nu_json/empty_result.hjson | 3 + tests/assets/nu_json/empty_result.json | 3 + tests/assets/nu_json/empty_test.hjson | 3 + tests/assets/nu_json/failCharset1_test.hjson | 4 + tests/assets/nu_json/failJSON02_test.json | 1 + tests/assets/nu_json/failJSON05_test.json | 1 + tests/assets/nu_json/failJSON06_test.json | 1 + tests/assets/nu_json/failJSON07_test.json | 1 + tests/assets/nu_json/failJSON08_test.json | 1 + tests/assets/nu_json/failJSON10_test.json | 1 + tests/assets/nu_json/failJSON11_test.json | 1 + tests/assets/nu_json/failJSON12_test.json | 1 + tests/assets/nu_json/failJSON13_test.json | 1 + tests/assets/nu_json/failJSON14_test.json | 1 + tests/assets/nu_json/failJSON15_test.json | 1 + tests/assets/nu_json/failJSON16_test.json | 1 + tests/assets/nu_json/failJSON17_test.json | 1 + tests/assets/nu_json/failJSON19_test.json | 1 + tests/assets/nu_json/failJSON20_test.json | 1 + tests/assets/nu_json/failJSON21_test.json | 1 + tests/assets/nu_json/failJSON22_test.json | 1 + tests/assets/nu_json/failJSON23_test.json | 1 + tests/assets/nu_json/failJSON24_test.json | 1 + tests/assets/nu_json/failJSON26_test.json | 1 + tests/assets/nu_json/failJSON28_test.json | 2 + tests/assets/nu_json/failJSON29_test.json | 1 + tests/assets/nu_json/failJSON30_test.json | 1 + tests/assets/nu_json/failJSON31_test.json | 1 + tests/assets/nu_json/failJSON32_test.json | 1 + tests/assets/nu_json/failJSON33_test.json | 1 + tests/assets/nu_json/failJSON34_test.json | 2 + tests/assets/nu_json/failKey1_test.hjson | 4 + tests/assets/nu_json/failKey2_test.hjson | 4 + tests/assets/nu_json/failKey3_test.hjson | 4 + tests/assets/nu_json/failKey4_test.hjson | 4 + tests/assets/nu_json/failMLStr1_test.hjson | 3 + tests/assets/nu_json/failObj1_test.hjson | 6 + tests/assets/nu_json/failObj2_test.hjson | 6 + tests/assets/nu_json/failObj3_test.hjson | 7 + tests/assets/nu_json/failStr1a_test.hjson | 4 + tests/assets/nu_json/failStr1b_test.hjson | 4 + tests/assets/nu_json/failStr1c_test.hjson | 5 + tests/assets/nu_json/failStr1d_test.hjson | 5 + tests/assets/nu_json/failStr2a_test.hjson | 4 + tests/assets/nu_json/failStr2b_test.hjson | 4 + tests/assets/nu_json/failStr2c_test.hjson | 5 + tests/assets/nu_json/failStr2d_test.hjson | 5 + tests/assets/nu_json/failStr3a_test.hjson | 4 + tests/assets/nu_json/failStr3b_test.hjson | 4 + tests/assets/nu_json/failStr3c_test.hjson | 5 + tests/assets/nu_json/failStr3d_test.hjson | 5 + tests/assets/nu_json/failStr4a_test.hjson | 4 + tests/assets/nu_json/failStr4b_test.hjson | 4 + tests/assets/nu_json/failStr4c_test.hjson | 5 + tests/assets/nu_json/failStr4d_test.hjson | 5 + tests/assets/nu_json/failStr5a_test.hjson | 4 + tests/assets/nu_json/failStr5b_test.hjson | 4 + tests/assets/nu_json/failStr5c_test.hjson | 5 + tests/assets/nu_json/failStr5d_test.hjson | 5 + tests/assets/nu_json/failStr6a_test.hjson | 4 + tests/assets/nu_json/failStr6b_test.hjson | 4 + tests/assets/nu_json/failStr6c_test.hjson | 6 + tests/assets/nu_json/failStr6d_test.hjson | 6 + tests/assets/nu_json/kan_result.hjson | 48 + tests/assets/nu_json/kan_result.json | 45 + tests/assets/nu_json/kan_test.hjson | 49 + tests/assets/nu_json/keys_result.hjson | 34 + tests/assets/nu_json/keys_result.json | 34 + tests/assets/nu_json/keys_test.hjson | 48 + tests/assets/nu_json/oa_result.hjson | 13 + tests/assets/nu_json/oa_result.json | 13 + tests/assets/nu_json/oa_test.hjson | 13 + tests/assets/nu_json/pass1_result.hjson | 78 ++ tests/assets/nu_json/pass1_result.json | 75 ++ tests/assets/nu_json/pass1_test.json | 58 + tests/assets/nu_json/pass2_result.hjson | 39 + tests/assets/nu_json/pass2_result.json | 39 + tests/assets/nu_json/pass2_test.json | 1 + tests/assets/nu_json/pass3_result.hjson | 7 + tests/assets/nu_json/pass3_result.json | 6 + tests/assets/nu_json/pass3_test.json | 6 + tests/assets/nu_json/pass4_result.hjson | 1 + tests/assets/nu_json/pass4_result.json | 1 + tests/assets/nu_json/pass4_test.json | 2 + tests/assets/nu_json/passSingle_result.hjson | 1 + tests/assets/nu_json/passSingle_result.json | 1 + tests/assets/nu_json/passSingle_test.hjson | 1 + tests/assets/nu_json/root_result.hjson | 7 + tests/assets/nu_json/root_result.json | 6 + tests/assets/nu_json/root_test.hjson | 6 + tests/assets/nu_json/stringify1_result.hjson | 49 + tests/assets/nu_json/stringify1_result.json | 47 + tests/assets/nu_json/stringify1_test.hjson | 50 + tests/assets/nu_json/strings_result.hjson | 75 ++ tests/assets/nu_json/strings_result.json | 55 + tests/assets/nu_json/strings_test.hjson | 80 ++ tests/assets/nu_json/testlist.txt | 75 ++ tests/assets/nu_json/trail_result.hjson | 3 + tests/assets/nu_json/trail_result.json | 3 + tests/assets/nu_json/trail_test.hjson | 2 + 118 files changed, 2487 insertions(+), 1322 deletions(-) delete mode 100644 crates/nu-json/src/forward.rs create mode 100644 crates/nu-json/tests/main.rs create mode 100644 tests/assets/nu_json/charset_result.hjson create mode 100644 tests/assets/nu_json/charset_result.json create mode 100644 tests/assets/nu_json/charset_test.hjson create mode 100644 tests/assets/nu_json/comments_result.hjson create mode 100644 tests/assets/nu_json/comments_result.json create mode 100644 tests/assets/nu_json/comments_test.hjson create mode 100644 tests/assets/nu_json/empty_result.hjson create mode 100644 tests/assets/nu_json/empty_result.json create mode 100644 tests/assets/nu_json/empty_test.hjson create mode 100644 tests/assets/nu_json/failCharset1_test.hjson create mode 100644 tests/assets/nu_json/failJSON02_test.json create mode 100644 tests/assets/nu_json/failJSON05_test.json create mode 100644 tests/assets/nu_json/failJSON06_test.json create mode 100644 tests/assets/nu_json/failJSON07_test.json create mode 100644 tests/assets/nu_json/failJSON08_test.json create mode 100644 tests/assets/nu_json/failJSON10_test.json create mode 100644 tests/assets/nu_json/failJSON11_test.json create mode 100644 tests/assets/nu_json/failJSON12_test.json create mode 100644 tests/assets/nu_json/failJSON13_test.json create mode 100644 tests/assets/nu_json/failJSON14_test.json create mode 100644 tests/assets/nu_json/failJSON15_test.json create mode 100644 tests/assets/nu_json/failJSON16_test.json create mode 100644 tests/assets/nu_json/failJSON17_test.json create mode 100644 tests/assets/nu_json/failJSON19_test.json create mode 100644 tests/assets/nu_json/failJSON20_test.json create mode 100644 tests/assets/nu_json/failJSON21_test.json create mode 100644 tests/assets/nu_json/failJSON22_test.json create mode 100644 tests/assets/nu_json/failJSON23_test.json create mode 100644 tests/assets/nu_json/failJSON24_test.json create mode 100644 tests/assets/nu_json/failJSON26_test.json create mode 100644 tests/assets/nu_json/failJSON28_test.json create mode 100644 tests/assets/nu_json/failJSON29_test.json create mode 100644 tests/assets/nu_json/failJSON30_test.json create mode 100644 tests/assets/nu_json/failJSON31_test.json create mode 100644 tests/assets/nu_json/failJSON32_test.json create mode 100644 tests/assets/nu_json/failJSON33_test.json create mode 100644 tests/assets/nu_json/failJSON34_test.json create mode 100644 tests/assets/nu_json/failKey1_test.hjson create mode 100644 tests/assets/nu_json/failKey2_test.hjson create mode 100644 tests/assets/nu_json/failKey3_test.hjson create mode 100644 tests/assets/nu_json/failKey4_test.hjson create mode 100644 tests/assets/nu_json/failMLStr1_test.hjson create mode 100644 tests/assets/nu_json/failObj1_test.hjson create mode 100644 tests/assets/nu_json/failObj2_test.hjson create mode 100644 tests/assets/nu_json/failObj3_test.hjson create mode 100644 tests/assets/nu_json/failStr1a_test.hjson create mode 100644 tests/assets/nu_json/failStr1b_test.hjson create mode 100644 tests/assets/nu_json/failStr1c_test.hjson create mode 100644 tests/assets/nu_json/failStr1d_test.hjson create mode 100644 tests/assets/nu_json/failStr2a_test.hjson create mode 100644 tests/assets/nu_json/failStr2b_test.hjson create mode 100644 tests/assets/nu_json/failStr2c_test.hjson create mode 100644 tests/assets/nu_json/failStr2d_test.hjson create mode 100644 tests/assets/nu_json/failStr3a_test.hjson create mode 100644 tests/assets/nu_json/failStr3b_test.hjson create mode 100644 tests/assets/nu_json/failStr3c_test.hjson create mode 100644 tests/assets/nu_json/failStr3d_test.hjson create mode 100644 tests/assets/nu_json/failStr4a_test.hjson create mode 100644 tests/assets/nu_json/failStr4b_test.hjson create mode 100644 tests/assets/nu_json/failStr4c_test.hjson create mode 100644 tests/assets/nu_json/failStr4d_test.hjson create mode 100644 tests/assets/nu_json/failStr5a_test.hjson create mode 100644 tests/assets/nu_json/failStr5b_test.hjson create mode 100644 tests/assets/nu_json/failStr5c_test.hjson create mode 100644 tests/assets/nu_json/failStr5d_test.hjson create mode 100644 tests/assets/nu_json/failStr6a_test.hjson create mode 100644 tests/assets/nu_json/failStr6b_test.hjson create mode 100644 tests/assets/nu_json/failStr6c_test.hjson create mode 100644 tests/assets/nu_json/failStr6d_test.hjson create mode 100644 tests/assets/nu_json/kan_result.hjson create mode 100644 tests/assets/nu_json/kan_result.json create mode 100644 tests/assets/nu_json/kan_test.hjson create mode 100644 tests/assets/nu_json/keys_result.hjson create mode 100644 tests/assets/nu_json/keys_result.json create mode 100644 tests/assets/nu_json/keys_test.hjson create mode 100644 tests/assets/nu_json/oa_result.hjson create mode 100644 tests/assets/nu_json/oa_result.json create mode 100644 tests/assets/nu_json/oa_test.hjson create mode 100644 tests/assets/nu_json/pass1_result.hjson create mode 100644 tests/assets/nu_json/pass1_result.json create mode 100644 tests/assets/nu_json/pass1_test.json create mode 100644 tests/assets/nu_json/pass2_result.hjson create mode 100644 tests/assets/nu_json/pass2_result.json create mode 100644 tests/assets/nu_json/pass2_test.json create mode 100644 tests/assets/nu_json/pass3_result.hjson create mode 100644 tests/assets/nu_json/pass3_result.json create mode 100644 tests/assets/nu_json/pass3_test.json create mode 100644 tests/assets/nu_json/pass4_result.hjson create mode 100644 tests/assets/nu_json/pass4_result.json create mode 100644 tests/assets/nu_json/pass4_test.json create mode 100644 tests/assets/nu_json/passSingle_result.hjson create mode 100644 tests/assets/nu_json/passSingle_result.json create mode 100644 tests/assets/nu_json/passSingle_test.hjson create mode 100644 tests/assets/nu_json/root_result.hjson create mode 100644 tests/assets/nu_json/root_result.json create mode 100644 tests/assets/nu_json/root_test.hjson create mode 100644 tests/assets/nu_json/stringify1_result.hjson create mode 100644 tests/assets/nu_json/stringify1_result.json create mode 100644 tests/assets/nu_json/stringify1_test.hjson create mode 100644 tests/assets/nu_json/strings_result.hjson create mode 100644 tests/assets/nu_json/strings_result.json create mode 100644 tests/assets/nu_json/strings_test.hjson create mode 100644 tests/assets/nu_json/testlist.txt create mode 100644 tests/assets/nu_json/trail_result.hjson create mode 100644 tests/assets/nu_json/trail_result.json create mode 100644 tests/assets/nu_json/trail_test.hjson diff --git a/Cargo.lock b/Cargo.lock index 6220a7869..e0eec758e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2771,7 +2771,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd" dependencies = [ "serde 0.8.23", - "serde_test", + "serde_test 0.8.23", ] [[package]] @@ -2779,6 +2779,10 @@ name = "linked-hash-map" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +dependencies = [ + "serde 1.0.123", + "serde_test 1.0.123", +] [[package]] name = "lock_api" @@ -3496,10 +3500,14 @@ dependencies = [ name = "nu-json" version = "0.27.2" dependencies = [ + "dunce", "lazy_static 1.4.0", + "linked-hash-map 0.5.4", + "nu-test-support", "num-traits 0.2.14", "regex 1.4.3", - "serde 0.8.23", + "serde 1.0.123", + "serde_json", ] [[package]] @@ -5386,6 +5394,15 @@ dependencies = [ "serde 0.8.23", ] +[[package]] +name = "serde_test" +version = "1.0.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38145a8510bdf71d9a8cceeb57664049538446e77f24648328bdbcf22dc7e169" +dependencies = [ + "serde 1.0.123", +] + [[package]] name = "serde_urlencoded" version = "0.6.1" diff --git a/crates/nu-json/Cargo.toml b/crates/nu-json/Cargo.toml index 2a7b9a283..8d105a529 100644 --- a/crates/nu-json/Cargo.toml +++ b/crates/nu-json/Cargo.toml @@ -8,8 +8,18 @@ version = "0.27.2" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +preserve_order = ["linked-hash-map", "linked-hash-map/serde_impl"] +default = ["preserve_order"] + [dependencies] -lazy_static = "1" +serde = "1.0" num-traits = "0.2.14" regex = "^1.0" -serde = "^0.8.0" +lazy_static = "1" +linked-hash-map = { version = "0.5", optional = true } + +[dev-dependencies] +nu-test-support = { version = "0.27.2", path = "../nu-test-support" } +serde_json = "1.0.39" +dunce = "1.0.1" \ No newline at end of file diff --git a/crates/nu-json/src/builder.rs b/crates/nu-json/src/builder.rs index 5452a1211..a69eee28c 100644 --- a/crates/nu-json/src/builder.rs +++ b/crates/nu-json/src/builder.rs @@ -1,13 +1,3 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - use serde::ser; use crate::value::{self, Map, Value}; @@ -36,7 +26,8 @@ impl ArrayBuilder { /// Insert a value into the array. pub fn push(mut self, v: T) -> ArrayBuilder { - self.array.push(value::to_value(&v)); + self.array + .push(value::to_value(&v).expect("failed to serialize")); self } @@ -91,7 +82,10 @@ impl ObjectBuilder { S: Into, V: ser::Serialize, { - self.object.insert(key.into(), value::to_value(&value)); + self.object.insert( + key.into(), + value::to_value(&value).expect("failed to serialize"), + ); self } diff --git a/crates/nu-json/src/de.rs b/crates/nu-json/src/de.rs index ce14bbdf3..12bcf2db2 100644 --- a/crates/nu-json/src/de.rs +++ b/crates/nu-json/src/de.rs @@ -74,9 +74,9 @@ where matches!(ch, b'{' | b'}' | b'[' | b']' | b',' | b':') } - fn parse_keyname(&mut self, mut visitor: V) -> Result + fn parse_keyname<'de, V>(&mut self, visitor: V) -> Result where - V: de::Visitor, + V: de::Visitor<'de>, { // quotes for keys are optional in Hjson // unless they include {}[],: or whitespace. @@ -117,9 +117,9 @@ where } } - fn parse_value(&mut self, mut visitor: V) -> Result + fn parse_value<'de, V>(&mut self, visitor: V) -> Result where - V: de::Visitor, + V: de::Visitor<'de>, { self.rdr.parse_whitespace()?; @@ -134,18 +134,18 @@ where } State::Root => { self.state = State::Normal; - return visitor.visit_map(MapVisitor::new(self, true)); + return self.visit_map(true, visitor); } _ => {} } - let value = match self.rdr.peek_or_null()? { + match self.rdr.peek_or_null()? { /* b'-' => { self.rdr.eat_char(); self.parse_integer(false, visitor) } - b'0' ..= b'9' => { + b'0' ... b'9' => { self.parse_integer(true, visitor) } */ @@ -157,20 +157,45 @@ where } b'[' => { self.rdr.eat_char(); - visitor.visit_seq(SeqVisitor::new(self)) + let ret = visitor.visit_seq(SeqVisitor::new(self))?; + self.rdr.parse_whitespace()?; + match self.rdr.next_char()? { + Some(b']') => Ok(ret), + Some(_) => Err(self.rdr.error(ErrorCode::TrailingCharacters)), + None => Err(self.rdr.error(ErrorCode::EOFWhileParsingList)), + } } b'{' => { self.rdr.eat_char(); - visitor.visit_map(MapVisitor::new(self, false)) + self.visit_map(false, visitor) } b'\x00' => Err(self.rdr.error(ErrorCode::ExpectedSomeValue)), _ => self.parse_tfnns(visitor), - }; + } + } - match value { - Ok(value) => Ok(value), - Err(Error::Syntax(code, _, _)) => Err(self.rdr.error(code)), - Err(err) => Err(err), + fn visit_map<'de, V>(&mut self, root: bool, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + let ret = visitor.visit_map(MapVisitor::new(self, root))?; + self.rdr.parse_whitespace()?; + match self.rdr.next_char()? { + Some(b'}') => { + if !root { + Ok(ret) + } else { + Err(self.rdr.error(ErrorCode::TrailingCharacters)) + } // todo + } + Some(_) => Err(self.rdr.error(ErrorCode::TrailingCharacters)), + None => { + if root { + Ok(ret) + } else { + Err(self.rdr.error(ErrorCode::EOFWhileParsingObject)) + } + } } } @@ -184,9 +209,9 @@ where Ok(()) } - fn parse_tfnns(&mut self, mut visitor: V) -> Result + fn parse_tfnns<'de, V>(&mut self, visitor: V) -> Result where - V: de::Visitor, + V: de::Visitor<'de>, { // Hjson strings can be quoteless // returns string, true, false, or null. @@ -244,7 +269,7 @@ where } _ => { if chf == b'-' || (b'0'..=b'9').contains(&chf) { - let mut pn = ParseNumber::new(self.str_buf.iter().cloned()); + let mut pn = ParseNumber::new(self.str_buf.iter().copied()); match pn.parse(false) { Ok(Number::F64(v)) => { self.rdr.uneat_char(ch); @@ -274,7 +299,7 @@ where } self.str_buf.push(ch); - if self.str_buf == vec![b'\''; 3] { + if self.str_buf == b"'''" { return self.parse_ml_string(visitor); } } @@ -283,7 +308,7 @@ where fn decode_hex_escape(&mut self) -> Result { let mut i = 0; let mut n = 0u16; - while i < 4 && !(self.rdr.eof()?) { + while i < 4 && !self.rdr.eof()? { n = match self.rdr.next_char_or_null()? { c @ b'0'..=b'9' => n * 16_u16 + ((c as u16) - (b'0' as u16)), b'a' | b'A' => n * 16_u16 + 10_u16, @@ -326,9 +351,9 @@ where Ok(()) } - fn parse_ml_string(&mut self, mut visitor: V) -> Result + fn parse_ml_string<'de, V>(&mut self, visitor: V) -> Result where - V: de::Visitor, + V: de::Visitor<'de>, { self.str_buf.clear(); @@ -464,11 +489,7 @@ where }, }; - // FIXME: this allocation is required in order to be compatible with stable - // rust, which doesn't support encoding a `char` into a stack buffer. - let mut buf = String::new(); - buf.push(c); - self.str_buf.extend(buf.bytes()); + self.str_buf.extend(c.encode_utf8(&mut [0; 4]).as_bytes()); } _ => { return Err(self.rdr.error(ErrorCode::InvalidEscape)); @@ -493,25 +514,27 @@ where } } -impl de::Deserializer for Deserializer +impl<'de, 'a, Iter> de::Deserializer<'de> for &'a mut Deserializer where Iter: Iterator, { type Error = Error; #[inline] - fn deserialize(&mut self, visitor: V) -> Result + fn deserialize_any(self, visitor: V) -> Result where - V: de::Visitor, + V: de::Visitor<'de>, { + if let State::Root = self.state {} + self.parse_value(visitor) } /// Parses a `null` as a None, and any other values as a `Some(...)`. #[inline] - fn deserialize_option(&mut self, mut visitor: V) -> Result + fn deserialize_option(self, visitor: V) -> Result where - V: de::Visitor, + V: de::Visitor<'de>, { self.rdr.parse_whitespace()?; @@ -527,42 +550,17 @@ where /// Parses a newtype struct as the underlying value. #[inline] - fn deserialize_newtype_struct(&mut self, _name: &str, mut visitor: V) -> Result + fn deserialize_newtype_struct(self, _name: &str, visitor: V) -> Result where - V: de::Visitor, + V: de::Visitor<'de>, { visitor.visit_newtype_struct(self) } - forward_to_deserialize! { - deserialize_bool(); - deserialize_usize(); - deserialize_u8(); - deserialize_u16(); - deserialize_u32(); - deserialize_u64(); - deserialize_isize(); - deserialize_i8(); - deserialize_i16(); - deserialize_i32(); - deserialize_i64(); - deserialize_f32(); - deserialize_f64(); - deserialize_char(); - deserialize_str(); - deserialize_string(); - deserialize_unit(); - deserialize_seq(); - deserialize_seq_fixed_size(len: usize); - deserialize_bytes(); - deserialize_map(); - deserialize_unit_struct(name: &'static str); - deserialize_tuple_struct(name: &'static str, len: usize); - deserialize_struct(name: &'static str, fields: &'static [&'static str]); - deserialize_struct_field(); - deserialize_tuple(len: usize); - deserialize_enum(name: &'static str, variants: &'static [&'static str]); - deserialize_ignored_any(); + serde::forward_to_deserialize_any! { + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string + bytes byte_buf unit unit_struct seq tuple map + tuple_struct struct enum identifier ignored_any } } @@ -576,15 +574,15 @@ impl<'a, Iter: Iterator> SeqVisitor<'a, Iter> { } } -impl<'a, Iter> de::SeqVisitor for SeqVisitor<'a, Iter> +impl<'de, 'a, Iter> de::SeqAccess<'de> for SeqVisitor<'a, Iter> where Iter: Iterator, { type Error = Error; - fn visit(&mut self) -> Result> + fn next_element_seed(&mut self, seed: T) -> Result> where - T: de::Deserialize, + T: de::DeserializeSeed<'de>, { self.de.rdr.parse_whitespace()?; @@ -598,7 +596,7 @@ where } } - let value = de::Deserialize::deserialize(self.de)?; + let value = seed.deserialize(&mut *self.de)?; // in Hjson the comma is optional and trailing commas are allowed self.de.rdr.parse_whitespace()?; @@ -609,16 +607,6 @@ where Ok(Some(value)) } - - fn end(&mut self) -> Result<()> { - self.de.rdr.parse_whitespace()?; - - match self.de.rdr.next_char()? { - Some(b']') => Ok(()), - Some(_) => Err(self.de.rdr.error(ErrorCode::TrailingCharacters)), - None => Err(self.de.rdr.error(ErrorCode::EOFWhileParsingList)), - } - } } struct MapVisitor<'a, Iter: 'a + Iterator> { @@ -637,15 +625,15 @@ impl<'a, Iter: Iterator> MapVisitor<'a, Iter> { } } -impl<'a, Iter> de::MapVisitor for MapVisitor<'a, Iter> +impl<'de, 'a, Iter> de::MapAccess<'de> for MapVisitor<'a, Iter> where Iter: Iterator, { type Error = Error; - fn visit_key(&mut self) -> Result> + fn next_key_seed(&mut self, seed: K) -> Result> where - K: de::Deserialize, + K: de::DeserializeSeed<'de>, { self.de.rdr.parse_whitespace()?; @@ -676,146 +664,51 @@ where } else { State::Keyname }; - Ok(Some(de::Deserialize::deserialize(self.de)?)) + Ok(Some(seed.deserialize(&mut *self.de)?)) } None => Err(self.de.rdr.error(ErrorCode::EOFWhileParsingValue)), } } - fn visit_value(&mut self) -> Result + fn next_value_seed(&mut self, seed: V) -> Result where - V: de::Deserialize, + V: de::DeserializeSeed<'de>, { self.de.parse_object_colon()?; - de::Deserialize::deserialize(self.de) - } - - fn end(&mut self) -> Result<()> { - self.de.rdr.parse_whitespace()?; - - match self.de.rdr.next_char()? { - Some(b'}') => { - if !self.root { - Ok(()) - } else { - Err(self.de.rdr.error(ErrorCode::TrailingCharacters)) - } // todo - } - Some(_) => Err(self.de.rdr.error(ErrorCode::TrailingCharacters)), - None => { - if self.root { - Ok(()) - } else { - Err(self.de.rdr.error(ErrorCode::EOFWhileParsingObject)) - } - } - } - } - - fn missing_field(&mut self, field: &'static str) -> Result - where - V: de::Deserialize, - { - struct MissingFieldDeserializer(&'static str); - - impl de::Deserializer for MissingFieldDeserializer { - type Error = de::value::Error; - - fn deserialize(&mut self, _visitor: V) -> std::result::Result - where - V: de::Visitor, - { - let &mut MissingFieldDeserializer(field) = self; - Err(de::value::Error::MissingField(field)) - } - - fn deserialize_option( - &mut self, - mut visitor: V, - ) -> std::result::Result - where - V: de::Visitor, - { - visitor.visit_none() - } - - forward_to_deserialize! { - deserialize_bool(); - deserialize_usize(); - deserialize_u8(); - deserialize_u16(); - deserialize_u32(); - deserialize_u64(); - deserialize_isize(); - deserialize_i8(); - deserialize_i16(); - deserialize_i32(); - deserialize_i64(); - deserialize_f32(); - deserialize_f64(); - deserialize_char(); - deserialize_str(); - deserialize_string(); - deserialize_unit(); - deserialize_seq(); - deserialize_seq_fixed_size(len: usize); - deserialize_bytes(); - deserialize_map(); - deserialize_unit_struct(name: &'static str); - deserialize_newtype_struct(name: &'static str); - deserialize_tuple_struct(name: &'static str, len: usize); - deserialize_struct(name: &'static str, fields: &'static [&'static str]); - deserialize_struct_field(); - deserialize_tuple(len: usize); - deserialize_enum(name: &'static str, variants: &'static [&'static str]); - deserialize_ignored_any(); - } - } - - let mut de = MissingFieldDeserializer(field); - Ok(de::Deserialize::deserialize(&mut de)?) + Ok(seed.deserialize(&mut *self.de)?) } } -impl de::VariantVisitor for Deserializer +impl<'de, 'a, Iter> de::VariantAccess<'de> for &'a mut Deserializer where Iter: Iterator, { type Error = Error; - fn visit_variant(&mut self) -> Result - where - V: de::Deserialize, - { - let val = de::Deserialize::deserialize(self)?; - self.parse_object_colon()?; - Ok(val) - } - - fn visit_unit(&mut self) -> Result<()> { + fn unit_variant(self) -> Result<()> { de::Deserialize::deserialize(self) } - fn visit_newtype(&mut self) -> Result + fn newtype_variant_seed(self, seed: T) -> Result where - T: de::Deserialize, + T: de::DeserializeSeed<'de>, { - de::Deserialize::deserialize(self) + seed.deserialize(self) } - fn visit_tuple(&mut self, _len: usize, visitor: V) -> Result + fn tuple_variant(self, _len: usize, visitor: V) -> Result where - V: de::Visitor, + V: de::Visitor<'de>, { - de::Deserializer::deserialize(self, visitor) + de::Deserializer::deserialize_any(self, visitor) } - fn visit_struct(&mut self, _fields: &'static [&'static str], visitor: V) -> Result + fn struct_variant(self, _fields: &'static [&'static str], visitor: V) -> Result where - V: de::Visitor, + V: de::Visitor<'de>, { - de::Deserializer::deserialize(self, visitor) + de::Deserializer::deserialize_any(self, visitor) } } @@ -825,7 +718,7 @@ where pub struct StreamDeserializer where Iter: Iterator, - T: de::Deserialize, + T: de::DeserializeOwned, { deser: Deserializer, _marker: PhantomData, @@ -834,7 +727,7 @@ where impl StreamDeserializer where Iter: Iterator, - T: de::Deserialize, + T: de::DeserializeOwned, { /// Returns an `Iterator` of decoded Hjson values from an iterator over /// `Iterator`. @@ -849,7 +742,7 @@ where impl Iterator for StreamDeserializer where Iter: Iterator, - T: de::Deserialize, + T: de::DeserializeOwned, { type Item = Result; @@ -879,9 +772,10 @@ where pub fn from_iter(iter: I) -> Result where I: Iterator>, - T: de::Deserialize, + T: de::DeserializeOwned, { let fold: io::Result> = iter.collect(); + if let Err(e) = fold { return Err(Error::Io(e)); } @@ -893,43 +787,31 @@ where // todo: add compile switch // deserialize and make sure the whole stream has been consumed - let mut de = Deserializer::new_for_root(bytes.iter().cloned()); - let value = match de::Deserialize::deserialize(&mut de).and_then(|x| { - de.end()?; - Ok(x) - }) { - Ok(v) => Ok(v), - Err(_) => { - let mut de2 = Deserializer::new(bytes.iter().cloned()); - match de::Deserialize::deserialize(&mut de2).and_then(|x| { - de2.end()?; - Ok(x) - }) { - Ok(v) => Ok(v), - Err(e) => Err(e), - } - } - }; + let mut de = Deserializer::new_for_root(bytes.iter().copied()); + de::Deserialize::deserialize(&mut de) + .and_then(|x| de.end().map(|()| x)) + .or_else(|_| { + let mut de2 = Deserializer::new(bytes.iter().copied()); + de::Deserialize::deserialize(&mut de2).and_then(|x| de2.end().map(|()| x)) + }) /* without legacy support: // deserialize and make sure the whole stream has been consumed let mut de = Deserializer::new(bytes.iter().map(|b| *b)); let value = match de::Deserialize::deserialize(&mut de) - .and_then(|x| { de.end()); Ok(x) }) + .and_then(|x| { try!(de.end()); Ok(x) }) { Ok(v) => Ok(v), Err(e) => Err(e), }; */ - - value } /// Decodes a Hjson value from a `std::io::Read`. pub fn from_reader(rdr: R) -> Result where R: io::Read, - T: de::Deserialize, + T: de::DeserializeOwned, { from_iter(rdr.bytes()) } @@ -937,7 +819,7 @@ where /// Decodes a Hjson value from a byte slice `&[u8]`. pub fn from_slice(v: &[u8]) -> Result where - T: de::Deserialize, + T: de::DeserializeOwned, { from_iter(v.iter().map(|byte| Ok(*byte))) } @@ -945,7 +827,7 @@ where /// Decodes a Hjson value from a `&str`. pub fn from_str(s: &str) -> Result where - T: de::Deserialize, + T: de::DeserializeOwned, { from_slice(s.as_bytes()) } diff --git a/crates/nu-json/src/error.rs b/crates/nu-json/src/error.rs index a20bf5f6f..f42812980 100644 --- a/crates/nu-json/src/error.rs +++ b/crates/nu-json/src/error.rs @@ -18,24 +18,6 @@ pub enum ErrorCode { /// Catchall for syntax error messages Custom(String), - /// Incorrect type from value - InvalidType(de::Type), - - /// Incorrect value - InvalidValue(String), - - /// Invalid length - InvalidLength(usize), - - /// Unknown variant in an enum. - UnknownVariant(String), - - /// Unknown field in struct. - UnknownField(String), - - /// Struct is missing a field. - MissingField(&'static str), - /// EOF while parsing a list. EOFWhileParsingList, @@ -94,12 +76,6 @@ impl fmt::Debug for ErrorCode { match *self { ErrorCode::Custom(ref msg) => write!(f, "{}", msg), - ErrorCode::InvalidType(ref ty) => write!(f, "invalid type: {:?}", ty), - ErrorCode::InvalidValue(ref msg) => write!(f, "invalid value: {}", msg), - ErrorCode::InvalidLength(ref len) => write!(f, "invalid value length {}", len), - ErrorCode::UnknownVariant(ref variant) => write!(f, "unknown variant \"{}\"", variant), - ErrorCode::UnknownField(ref field) => write!(f, "unknown field \"{}\"", field), - ErrorCode::MissingField(ref field) => write!(f, "missing field \"{}\"", field), ErrorCode::EOFWhileParsingList => "EOF while parsing a list".fmt(f), ErrorCode::EOFWhileParsingObject => "EOF while parsing an object".fmt(f), ErrorCode::EOFWhileParsingString => "EOF while parsing a string".fmt(f), @@ -173,69 +149,16 @@ impl From for Error { } } -impl From for Error { - fn from(error: de::value::Error) -> Error { - match error { - de::value::Error::Custom(e) => Error::Syntax(ErrorCode::Custom(e), 0, 0), - de::value::Error::EndOfStream => de::Error::end_of_stream(), - de::value::Error::InvalidType(ty) => Error::Syntax(ErrorCode::InvalidType(ty), 0, 0), - de::value::Error::InvalidValue(msg) => { - Error::Syntax(ErrorCode::InvalidValue(msg), 0, 0) - } - de::value::Error::InvalidLength(len) => { - Error::Syntax(ErrorCode::InvalidLength(len), 0, 0) - } - de::value::Error::UnknownVariant(variant) => { - Error::Syntax(ErrorCode::UnknownVariant(variant), 0, 0) - } - de::value::Error::UnknownField(field) => { - Error::Syntax(ErrorCode::UnknownField(field), 0, 0) - } - de::value::Error::MissingField(field) => { - Error::Syntax(ErrorCode::MissingField(field), 0, 0) - } - } - } -} - impl de::Error for Error { - fn custom>(msg: T) -> Error { - Error::Syntax(ErrorCode::Custom(msg.into()), 0, 0) - } - - fn end_of_stream() -> Error { - Error::Syntax(ErrorCode::EOFWhileParsingValue, 0, 0) - } - - fn invalid_type(ty: de::Type) -> Error { - Error::Syntax(ErrorCode::InvalidType(ty), 0, 0) - } - - fn invalid_value(msg: &str) -> Error { - Error::Syntax(ErrorCode::InvalidValue(msg.to_owned()), 0, 0) - } - - fn invalid_length(len: usize) -> Error { - Error::Syntax(ErrorCode::InvalidLength(len), 0, 0) - } - - fn unknown_variant(variant: &str) -> Error { - Error::Syntax(ErrorCode::UnknownVariant(String::from(variant)), 0, 0) - } - - fn unknown_field(field: &str) -> Error { - Error::Syntax(ErrorCode::UnknownField(String::from(field)), 0, 0) - } - - fn missing_field(field: &'static str) -> Error { - Error::Syntax(ErrorCode::MissingField(field), 0, 0) + fn custom(msg: T) -> Error { + Error::Syntax(ErrorCode::Custom(msg.to_string()), 0, 0) } } impl ser::Error for Error { /// Raised when there is general error when deserializing a type. - fn custom>(msg: T) -> Error { - Error::Syntax(ErrorCode::Custom(msg.into()), 0, 0) + fn custom(msg: T) -> Error { + Error::Syntax(ErrorCode::Custom(msg.to_string()), 0, 0) } } diff --git a/crates/nu-json/src/forward.rs b/crates/nu-json/src/forward.rs deleted file mode 100644 index ba36c547e..000000000 --- a/crates/nu-json/src/forward.rs +++ /dev/null @@ -1,38 +0,0 @@ -#[macro_export] -/// Create a function to forward a specific serialize call to the generic deserialize -macro_rules! forward_to_deserialize { - ($( - $name:ident ( $( $arg:ident : $ty:ty ),* ); - )*) => { - $( - forward_to_deserialize!{ - func: $name ( $( $arg: $ty ),* ); - } - )* - }; - - (func: deserialize_enum ( $( $arg:ident : $ty:ty ),* );) => { - fn deserialize_enum( - &mut self, - $(_: $ty,)* - _visitor: V, - ) -> ::std::result::Result - where V: ::serde::de::EnumVisitor - { - Err(::serde::de::Error::invalid_type(::serde::de::Type::Enum)) - } - }; - - (func: $name:ident ( $( $arg:ident : $ty:ty ),* );) => { - #[inline] - fn $name( - &mut self, - $(_: $ty,)* - visitor: V, - ) -> ::std::result::Result - where V: ::serde::de::Visitor - { - self.deserialize(visitor) - } - }; -} diff --git a/crates/nu-json/src/lib.rs b/crates/nu-json/src/lib.rs index e069bb3f1..6a196c527 100644 --- a/crates/nu-json/src/lib.rs +++ b/crates/nu-json/src/lib.rs @@ -5,9 +5,6 @@ pub use self::error::{Error, ErrorCode, Result}; pub use self::ser::{to_string, to_vec, to_writer, Serializer}; pub use self::value::{from_value, to_value, Map, Value}; -#[macro_use] -mod forward; - pub mod builder; pub mod de; pub mod error; diff --git a/crates/nu-json/src/ser.rs b/crates/nu-json/src/ser.rs index 56cbdccec..74a1728d5 100644 --- a/crates/nu-json/src/ser.rs +++ b/crates/nu-json/src/ser.rs @@ -59,23 +59,30 @@ pub enum State { Rest, } -impl ser::Serializer for Serializer +#[doc(hidden)] +pub struct Compound<'a, W, F> { + ser: &'a mut Serializer, + state: State, +} + +impl<'a, W, F> ser::Serializer for &'a mut Serializer where W: io::Write, F: Formatter, { + type Ok = (); type Error = Error; - type SeqState = State; - type TupleState = State; - type TupleStructState = State; - type TupleVariantState = State; - type MapState = State; - type StructState = State; - type StructVariantState = State; + type SerializeSeq = Compound<'a, W, F>; + type SerializeTuple = Compound<'a, W, F>; + type SerializeTupleStruct = Compound<'a, W, F>; + type SerializeTupleVariant = Compound<'a, W, F>; + type SerializeMap = Compound<'a, W, F>; + type SerializeStruct = Compound<'a, W, F>; + type SerializeStructVariant = Compound<'a, W, F>; #[inline] - fn serialize_bool(&mut self, value: bool) -> Result<()> { + fn serialize_bool(self, value: bool) -> Result<()> { self.formatter.start_value(&mut self.writer)?; if value { self.writer.write_all(b"true").map_err(From::from) @@ -85,115 +92,103 @@ where } #[inline] - fn serialize_isize(&mut self, value: isize) -> Result<()> { + fn serialize_i8(self, value: i8) -> Result<()> { self.formatter.start_value(&mut self.writer)?; write!(&mut self.writer, "{}", value).map_err(From::from) } #[inline] - fn serialize_i8(&mut self, value: i8) -> Result<()> { + fn serialize_i16(self, value: i16) -> Result<()> { self.formatter.start_value(&mut self.writer)?; write!(&mut self.writer, "{}", value).map_err(From::from) } #[inline] - fn serialize_i16(&mut self, value: i16) -> Result<()> { + fn serialize_i32(self, value: i32) -> Result<()> { self.formatter.start_value(&mut self.writer)?; write!(&mut self.writer, "{}", value).map_err(From::from) } #[inline] - fn serialize_i32(&mut self, value: i32) -> Result<()> { + fn serialize_i64(self, value: i64) -> Result<()> { self.formatter.start_value(&mut self.writer)?; write!(&mut self.writer, "{}", value).map_err(From::from) } #[inline] - fn serialize_i64(&mut self, value: i64) -> Result<()> { + fn serialize_u8(self, value: u8) -> Result<()> { self.formatter.start_value(&mut self.writer)?; write!(&mut self.writer, "{}", value).map_err(From::from) } #[inline] - fn serialize_usize(&mut self, value: usize) -> Result<()> { + fn serialize_u16(self, value: u16) -> Result<()> { self.formatter.start_value(&mut self.writer)?; write!(&mut self.writer, "{}", value).map_err(From::from) } #[inline] - fn serialize_u8(&mut self, value: u8) -> Result<()> { + fn serialize_u32(self, value: u32) -> Result<()> { self.formatter.start_value(&mut self.writer)?; write!(&mut self.writer, "{}", value).map_err(From::from) } #[inline] - fn serialize_u16(&mut self, value: u16) -> Result<()> { + fn serialize_u64(self, value: u64) -> Result<()> { self.formatter.start_value(&mut self.writer)?; write!(&mut self.writer, "{}", value).map_err(From::from) } #[inline] - fn serialize_u32(&mut self, value: u32) -> Result<()> { - self.formatter.start_value(&mut self.writer)?; - write!(&mut self.writer, "{}", value).map_err(From::from) - } - - #[inline] - fn serialize_u64(&mut self, value: u64) -> Result<()> { - self.formatter.start_value(&mut self.writer)?; - write!(&mut self.writer, "{}", value).map_err(From::from) - } - - #[inline] - fn serialize_f32(&mut self, value: f32) -> Result<()> { + fn serialize_f32(self, value: f32) -> Result<()> { self.formatter.start_value(&mut self.writer)?; fmt_f32_or_null(&mut self.writer, if value == -0f32 { 0f32 } else { value }) .map_err(From::from) } #[inline] - fn serialize_f64(&mut self, value: f64) -> Result<()> { + fn serialize_f64(self, value: f64) -> Result<()> { self.formatter.start_value(&mut self.writer)?; fmt_f64_or_null(&mut self.writer, if value == -0f64 { 0f64 } else { value }) .map_err(From::from) } #[inline] - fn serialize_char(&mut self, value: char) -> Result<()> { + fn serialize_char(self, value: char) -> Result<()> { self.formatter.start_value(&mut self.writer)?; escape_char(&mut self.writer, value).map_err(From::from) } #[inline] - fn serialize_str(&mut self, value: &str) -> Result<()> { + fn serialize_str(self, value: &str) -> Result<()> { quote_str(&mut self.writer, &mut self.formatter, value).map_err(From::from) } #[inline] - fn serialize_bytes(&mut self, value: &[u8]) -> Result<()> { - let mut state = self.serialize_seq(Some(value.len()))?; + fn serialize_bytes(self, value: &[u8]) -> Result<()> { + let mut seq = self.serialize_seq(Some(value.len()))?; for byte in value { - self.serialize_seq_elt(&mut state, byte)?; + ser::SerializeSeq::serialize_element(&mut seq, byte)? } - self.serialize_seq_end(state) + ser::SerializeSeq::end(seq) } #[inline] - fn serialize_unit(&mut self) -> Result<()> { + fn serialize_unit(self) -> Result<()> { self.formatter.start_value(&mut self.writer)?; self.writer.write_all(b"null").map_err(From::from) } #[inline] - fn serialize_unit_struct(&mut self, _name: &'static str) -> Result<()> { + fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { self.serialize_unit() } #[inline] fn serialize_unit_variant( - &mut self, + self, _name: &'static str, - _variant_index: usize, + _variant_index: u32, variant: &'static str, ) -> Result<()> { self.serialize_str(variant) @@ -201,127 +196,80 @@ where /// Serialize newtypes without an object wrapper. #[inline] - fn serialize_newtype_struct(&mut self, _name: &'static str, value: T) -> Result<()> + fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> where - T: ser::Serialize, + T: ?Sized + ser::Serialize, { value.serialize(self) } #[inline] fn serialize_newtype_variant( - &mut self, + self, _name: &'static str, - _variant_index: usize, + _variant_index: u32, variant: &'static str, - value: T, + value: &T, ) -> Result<()> where - T: ser::Serialize, + T: ?Sized + ser::Serialize, { self.formatter.open(&mut self.writer, b'{')?; self.formatter.comma(&mut self.writer, true)?; escape_key(&mut self.writer, variant)?; self.formatter.colon(&mut self.writer)?; - value.serialize(self)?; + value.serialize(&mut *self)?; self.formatter.close(&mut self.writer, b'}') } #[inline] - fn serialize_none(&mut self) -> Result<()> { + fn serialize_none(self) -> Result<()> { self.serialize_unit() } #[inline] - fn serialize_some(&mut self, value: V) -> Result<()> + fn serialize_some(self, value: &V) -> Result<()> where - V: ser::Serialize, + V: ?Sized + ser::Serialize, { value.serialize(self) } #[inline] - fn serialize_seq(&mut self, len: Option) -> Result { - if len == Some(0) { + fn serialize_seq(self, len: Option) -> Result { + let state = if len == Some(0) { self.formatter.start_value(&mut self.writer)?; self.writer.write_all(b"[]")?; - Ok(State::Empty) + State::Empty } else { self.formatter.open(&mut self.writer, b'[')?; - Ok(State::First) - } + State::First + }; + Ok(Compound { ser: self, state }) } #[inline] - fn serialize_seq_elt(&mut self, state: &mut State, value: T) -> Result<()> - where - T: ser::Serialize, - { - self.formatter - .comma(&mut self.writer, *state == State::First)?; - *state = State::Rest; - value.serialize(self) - } - - #[inline] - fn serialize_seq_end(&mut self, state: State) -> Result<()> { - match state { - State::Empty => Ok(()), - _ => self.formatter.close(&mut self.writer, b']'), - } - } - - #[inline] - fn serialize_seq_fixed_size(&mut self, size: usize) -> Result { - self.serialize_seq(Some(size)) - } - - #[inline] - fn serialize_tuple(&mut self, len: usize) -> Result { + fn serialize_tuple(self, len: usize) -> Result { self.serialize_seq(Some(len)) } #[inline] - fn serialize_tuple_elt( - &mut self, - state: &mut State, - value: T, - ) -> Result<()> { - self.serialize_seq_elt(state, value) - } - - #[inline] - fn serialize_tuple_end(&mut self, state: State) -> Result<()> { - self.serialize_seq_end(state) - } - - #[inline] - fn serialize_tuple_struct(&mut self, _name: &'static str, len: usize) -> Result { + fn serialize_tuple_struct( + self, + _name: &'static str, + len: usize, + ) -> Result { self.serialize_seq(Some(len)) } - #[inline] - fn serialize_tuple_struct_elt( - &mut self, - state: &mut State, - value: T, - ) -> Result<()> { - self.serialize_seq_elt(state, value) - } - - #[inline] - fn serialize_tuple_struct_end(&mut self, state: State) -> Result<()> { - self.serialize_seq_end(state) - } - #[inline] fn serialize_tuple_variant( - &mut self, + self, _name: &'static str, - _variant_index: usize, + _variant_index: u32, variant: &'static str, len: usize, - ) -> Result { + ) -> Result { self.formatter.open(&mut self.writer, b'{')?; self.formatter.comma(&mut self.writer, true)?; escape_key(&mut self.writer, variant)?; @@ -330,106 +278,208 @@ where } #[inline] - fn serialize_tuple_variant_elt( - &mut self, - state: &mut State, - value: T, - ) -> Result<()> { - self.serialize_seq_elt(state, value) - } - - #[inline] - fn serialize_tuple_variant_end(&mut self, state: State) -> Result<()> { - self.serialize_seq_end(state)?; - self.formatter.close(&mut self.writer, b'}') - } - - #[inline] - fn serialize_map(&mut self, len: Option) -> Result { - if len == Some(0) { + fn serialize_map(self, len: Option) -> Result { + let state = if len == Some(0) { self.formatter.start_value(&mut self.writer)?; self.writer.write_all(b"{}")?; - Ok(State::Empty) + State::Empty } else { self.formatter.open(&mut self.writer, b'{')?; - Ok(State::First) - } + State::First + }; + Ok(Compound { ser: self, state }) } #[inline] - fn serialize_map_key(&mut self, state: &mut State, key: T) -> Result<()> { - self.formatter - .comma(&mut self.writer, *state == State::First)?; - *state = State::Rest; - - key.serialize(&mut MapKeySerializer { ser: self })?; - - self.formatter.colon(&mut self.writer) - } - - #[inline] - fn serialize_map_value(&mut self, _: &mut State, value: T) -> Result<()> { - value.serialize(self) - } - - #[inline] - fn serialize_map_end(&mut self, state: State) -> Result<()> { - match state { - State::Empty => Ok(()), - _ => self.formatter.close(&mut self.writer, b'}'), - } - } - - #[inline] - fn serialize_struct(&mut self, _name: &'static str, len: usize) -> Result { + fn serialize_struct(self, _name: &'static str, len: usize) -> Result { self.serialize_map(Some(len)) } - #[inline] - fn serialize_struct_elt( - &mut self, - state: &mut State, - key: &'static str, - value: V, - ) -> Result<()> { - self.serialize_map_key(state, key)?; - self.serialize_map_value(state, value) - } - - #[inline] - fn serialize_struct_end(&mut self, state: State) -> Result<()> { - self.serialize_map_end(state) - } - #[inline] fn serialize_struct_variant( - &mut self, + self, _name: &'static str, - _variant_index: usize, + _variant_index: u32, variant: &'static str, len: usize, - ) -> Result { + ) -> Result { self.formatter.open(&mut self.writer, b'{')?; self.formatter.comma(&mut self.writer, true)?; escape_key(&mut self.writer, variant)?; self.formatter.colon(&mut self.writer)?; self.serialize_map(Some(len)) } +} - #[inline] - fn serialize_struct_variant_elt( - &mut self, - state: &mut State, - key: &'static str, - value: V, - ) -> Result<()> { - self.serialize_struct_elt(state, key, value) +impl<'a, W, F> ser::SerializeSeq for Compound<'a, W, F> +where + W: io::Write, + F: Formatter, +{ + type Ok = (); + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<()> + where + T: serde::Serialize, + { + self.ser + .formatter + .comma(&mut self.ser.writer, self.state == State::First)?; + self.state = State::Rest; + value.serialize(&mut *self.ser) } - #[inline] - fn serialize_struct_variant_end(&mut self, state: State) -> Result<()> { - self.serialize_struct_end(state)?; - self.formatter.close(&mut self.writer, b'}') + fn end(self) -> Result { + match self.state { + State::Empty => Ok(()), + _ => self.ser.formatter.close(&mut self.ser.writer, b']'), + } + } +} + +impl<'a, W, F> ser::SerializeTuple for Compound<'a, W, F> +where + W: io::Write, + F: Formatter, +{ + type Ok = (); + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<()> + where + T: serde::Serialize, + { + ser::SerializeSeq::serialize_element(self, value) + } + + fn end(self) -> Result { + ser::SerializeSeq::end(self) + } +} + +impl<'a, W, F> ser::SerializeTupleStruct for Compound<'a, W, F> +where + W: io::Write, + F: Formatter, +{ + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> Result<()> + where + T: serde::Serialize, + { + ser::SerializeSeq::serialize_element(self, value) + } + + fn end(self) -> Result { + ser::SerializeSeq::end(self) + } +} + +impl<'a, W, F> ser::SerializeTupleVariant for Compound<'a, W, F> +where + W: io::Write, + F: Formatter, +{ + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> Result<()> + where + T: serde::Serialize, + { + ser::SerializeSeq::serialize_element(self, value) + } + + fn end(self) -> Result { + match self.state { + State::Empty => {} + _ => self.ser.formatter.close(&mut self.ser.writer, b']')?, + } + self.ser.formatter.close(&mut self.ser.writer, b'}') + } +} + +impl<'a, W, F> ser::SerializeMap for Compound<'a, W, F> +where + W: io::Write, + F: Formatter, +{ + type Ok = (); + type Error = Error; + + fn serialize_key(&mut self, key: &T) -> Result<()> + where + T: serde::Serialize, + { + self.ser + .formatter + .comma(&mut self.ser.writer, self.state == State::First)?; + self.state = State::Rest; + + key.serialize(MapKeySerializer { ser: self.ser })?; + + self.ser.formatter.colon(&mut self.ser.writer) + } + + fn serialize_value(&mut self, value: &T) -> Result<()> + where + T: serde::Serialize, + { + value.serialize(&mut *self.ser) + } + + fn end(self) -> Result { + match self.state { + State::Empty => Ok(()), + _ => self.ser.formatter.close(&mut self.ser.writer, b'}'), + } + } +} + +impl<'a, W, F> ser::SerializeStruct for Compound<'a, W, F> +where + W: io::Write, + F: Formatter, +{ + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> + where + T: serde::Serialize, + { + ser::SerializeMap::serialize_entry(self, key, value) + } + + fn end(self) -> Result { + ser::SerializeMap::end(self) + } +} + +impl<'a, W, F> ser::SerializeStructVariant for Compound<'a, W, F> +where + W: io::Write, + F: Formatter, +{ + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> + where + T: serde::Serialize, + { + ser::SerializeStruct::serialize_field(self, key, value) + } + + fn end(self) -> Result { + match self.state { + State::Empty => {} + _ => self.ser.formatter.close(&mut self.ser.writer, b'}')?, + } + self.ser.formatter.close(&mut self.ser.writer, b'}') } } @@ -442,251 +492,163 @@ where W: io::Write, F: Formatter, { + type Ok = (); type Error = Error; #[inline] - fn serialize_str(&mut self, value: &str) -> Result<()> { + fn serialize_str(self, value: &str) -> Result<()> { escape_key(&mut self.ser.writer, value).map_err(From::from) } - type SeqState = (); - type TupleState = (); - type TupleStructState = (); - type TupleVariantState = (); - type MapState = (); - type StructState = (); - type StructVariantState = (); + type SerializeSeq = ser::Impossible<(), Error>; + type SerializeTuple = ser::Impossible<(), Error>; + type SerializeTupleStruct = ser::Impossible<(), Error>; + type SerializeTupleVariant = ser::Impossible<(), Error>; + type SerializeMap = ser::Impossible<(), Error>; + type SerializeStruct = ser::Impossible<(), Error>; + type SerializeStructVariant = ser::Impossible<(), Error>; - fn serialize_bool(&mut self, _value: bool) -> Result<()> { + fn serialize_bool(self, _value: bool) -> Result<()> { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_isize(&mut self, _value: isize) -> Result<()> { + fn serialize_i8(self, _value: i8) -> Result<()> { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_i8(&mut self, _value: i8) -> Result<()> { + fn serialize_i16(self, _value: i16) -> Result<()> { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_i16(&mut self, _value: i16) -> Result<()> { + fn serialize_i32(self, _value: i32) -> Result<()> { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_i32(&mut self, _value: i32) -> Result<()> { + fn serialize_i64(self, _value: i64) -> Result<()> { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_i64(&mut self, _value: i64) -> Result<()> { + fn serialize_u8(self, _value: u8) -> Result<()> { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_usize(&mut self, _value: usize) -> Result<()> { + fn serialize_u16(self, _value: u16) -> Result<()> { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_u8(&mut self, _value: u8) -> Result<()> { + fn serialize_u32(self, _value: u32) -> Result<()> { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_u16(&mut self, _value: u16) -> Result<()> { + fn serialize_u64(self, _value: u64) -> Result<()> { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_u32(&mut self, _value: u32) -> Result<()> { + fn serialize_f32(self, _value: f32) -> Result<()> { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_u64(&mut self, _value: u64) -> Result<()> { + fn serialize_f64(self, _value: f64) -> Result<()> { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_f32(&mut self, _value: f32) -> Result<()> { + fn serialize_char(self, _value: char) -> Result<()> { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_f64(&mut self, _value: f64) -> Result<()> { + fn serialize_bytes(self, _value: &[u8]) -> Result<()> { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_char(&mut self, _value: char) -> Result<()> { + fn serialize_unit(self) -> Result<()> { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_bytes(&mut self, _value: &[u8]) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_unit(&mut self) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_unit_struct(&mut self, _name: &'static str) -> Result<()> { + fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } fn serialize_unit_variant( - &mut self, + self, _name: &'static str, - _variant_index: usize, + _variant_index: u32, _variant: &'static str, ) -> Result<()> { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_newtype_struct(&mut self, _name: &'static str, _value: T) -> Result<()> + fn serialize_newtype_struct(self, _name: &'static str, _value: &T) -> Result<()> where - T: ser::Serialize, + T: ?Sized + ser::Serialize, { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } fn serialize_newtype_variant( - &mut self, + self, _name: &'static str, - _variant_index: usize, + _variant_index: u32, _variant: &'static str, - _value: T, + _value: &T, ) -> Result<()> where - T: ser::Serialize, + T: ?Sized + ser::Serialize, { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_none(&mut self) -> Result<()> { + fn serialize_none(self) -> Result<()> { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_some(&mut self, _value: T) -> Result<()> + fn serialize_some(self, _value: &T) -> Result<()> where - T: ser::Serialize, + T: ?Sized + ser::Serialize, { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_seq(&mut self, _len: Option) -> Result<()> { + fn serialize_seq(self, _len: Option) -> Result { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_seq_elt(&mut self, _state: &mut (), _value: T) -> Result<()> - where - T: ser::Serialize, - { + fn serialize_tuple(self, _len: usize) -> Result { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_seq_end(&mut self, _state: ()) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_seq_fixed_size(&mut self, _size: usize) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_tuple(&mut self, _len: usize) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_tuple_elt(&mut self, _state: &mut (), _value: T) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_tuple_end(&mut self, _state: ()) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_tuple_struct(&mut self, _name: &'static str, _len: usize) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_tuple_struct_elt( - &mut self, - _state: &mut (), - _value: T, - ) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_tuple_struct_end(&mut self, _state: ()) -> Result<()> { + fn serialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } fn serialize_tuple_variant( - &mut self, + self, _name: &'static str, - _variant_index: usize, + _variant_index: u32, _variant: &'static str, _len: usize, - ) -> Result<()> { + ) -> Result { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_tuple_variant_elt( - &mut self, - _state: &mut (), - _value: T, - ) -> Result<()> { + fn serialize_map(self, _len: Option) -> Result { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } - fn serialize_tuple_variant_end(&mut self, _state: ()) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_map(&mut self, _len: Option) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_map_key(&mut self, _state: &mut (), _key: T) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_map_value(&mut self, _state: &mut (), _value: T) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_map_end(&mut self, _state: ()) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_struct(&mut self, _name: &'static str, _len: usize) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_struct_elt( - &mut self, - _state: &mut (), - _key: &'static str, - _value: V, - ) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_struct_end(&mut self, _state: ()) -> Result<()> { + fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } fn serialize_struct_variant( - &mut self, + self, _name: &'static str, - _variant_index: usize, + _variant_index: u32, _variant: &'static str, _len: usize, - ) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_struct_variant_elt( - &mut self, - _state: &mut (), - _key: &'static str, - _value: V, - ) -> Result<()> { - Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) - } - - fn serialize_struct_variant_end(&mut self, _state: ()) -> Result<()> { + ) -> Result { Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)) } } diff --git a/crates/nu-json/src/util.rs b/crates/nu-json/src/util.rs index 3e1b2ddc2..5f650b109 100644 --- a/crates/nu-json/src/util.rs +++ b/crates/nu-json/src/util.rs @@ -58,6 +58,10 @@ where Ok(Some(self.ch[idx])) } + // pub fn peek_next_or_null(&mut self, idx: usize) -> Result { + // Ok(try!(self.peek_next(idx)).unwrap_or(b'\x00')) + // } + pub fn peek(&mut self) -> Result> { self.peek_next(0) } @@ -234,6 +238,7 @@ impl> ParseNumber { if self.rdr.peek_or_null()? == b'0' { self.result.push(self.rdr.eat_char()); has_value = true; + // There can be only one leading '0'. if let b'0'..=b'9' = self.rdr.peek_or_null()? { return Err(Error::Syntax(ErrorCode::InvalidNumber, 0, 0)); diff --git a/crates/nu-json/src/value.rs b/crates/nu-json/src/value.rs index 66cd78479..850abdfbc 100644 --- a/crates/nu-json/src/value.rs +++ b/crates/nu-json/src/value.rs @@ -16,6 +16,8 @@ use serde::ser; use crate::error::{Error, ErrorCode}; +type Result = std::result::Result; + /// Represents a key/value type. #[cfg(not(feature = "preserve_order"))] pub type Map = BTreeMap; @@ -30,10 +32,18 @@ pub type MapIntoIter = btree_map::IntoIter; #[cfg(feature = "preserve_order")] pub type MapIntoIter = linked_hash_map::IntoIter; -#[cfg(not(feature = "preserve_order"))] -type MapVisitor = de::impls::BTreeMapVisitor; -#[cfg(feature = "preserve_order")] -type MapVisitor = linked_hash_map::serde::LinkedHashMapVisitor; +fn map_with_capacity(size: Option) -> Map { + #[cfg(not(feature = "preserve_order"))] + { + let _ = size; + BTreeMap::new() + } + + #[cfg(feature = "preserve_order")] + { + LinkedHashMap::with_capacity(size.unwrap_or(0)) + } +} /// Represents a Hjson/JSON value #[derive(Clone, PartialEq)] @@ -289,11 +299,24 @@ impl Value { _ => None, } } + + fn as_unexpected(&self) -> de::Unexpected<'_> { + match *self { + Value::Null => de::Unexpected::Unit, + Value::Bool(v) => de::Unexpected::Bool(v), + Value::I64(v) => de::Unexpected::Signed(v), + Value::U64(v) => de::Unexpected::Unsigned(v), + Value::F64(v) => de::Unexpected::Float(v), + Value::String(ref v) => de::Unexpected::Str(v), + Value::Array(_) => de::Unexpected::Seq, + Value::Object(_) => de::Unexpected::Map, + } + } } impl ser::Serialize for Value { #[inline] - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, { @@ -310,24 +333,28 @@ impl ser::Serialize for Value { } } -impl de::Deserialize for Value { +impl<'de> de::Deserialize<'de> for Value { #[inline] - fn deserialize(deserializer: &mut D) -> Result + fn deserialize(deserializer: D) -> Result where - D: de::Deserializer, + D: de::Deserializer<'de>, { struct ValueVisitor; - impl de::Visitor for ValueVisitor { + impl<'de> de::Visitor<'de> for ValueVisitor { type Value = Value; + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("a json value") + } + #[inline] - fn visit_bool(&mut self, value: bool) -> Result { + fn visit_bool(self, value: bool) -> Result { Ok(Value::Bool(value)) } #[inline] - fn visit_i64(&mut self, value: i64) -> Result { + fn visit_i64(self, value: i64) -> Result { if value < 0 { Ok(Value::I64(value)) } else { @@ -336,17 +363,17 @@ impl de::Deserialize for Value { } #[inline] - fn visit_u64(&mut self, value: u64) -> Result { + fn visit_u64(self, value: u64) -> Result { Ok(Value::U64(value)) } #[inline] - fn visit_f64(&mut self, value: f64) -> Result { + fn visit_f64(self, value: f64) -> Result { Ok(Value::F64(value)) } #[inline] - fn visit_str(&mut self, value: &str) -> Result + fn visit_str(self, value: &str) -> Result where E: de::Error, { @@ -354,48 +381,59 @@ impl de::Deserialize for Value { } #[inline] - fn visit_string(&mut self, value: String) -> Result { + fn visit_string(self, value: String) -> Result { Ok(Value::String(value)) } #[inline] - fn visit_none(&mut self) -> Result { + fn visit_none(self) -> Result { Ok(Value::Null) } #[inline] - fn visit_some(&mut self, deserializer: &mut D) -> Result + fn visit_some(self, deserializer: D) -> Result where - D: de::Deserializer, + D: de::Deserializer<'de>, { de::Deserialize::deserialize(deserializer) } #[inline] - fn visit_unit(&mut self) -> Result { + fn visit_unit(self) -> Result { Ok(Value::Null) } #[inline] - fn visit_seq(&mut self, visitor: V) -> Result + fn visit_seq(self, mut seq: A) -> Result where - V: de::SeqVisitor, + A: de::SeqAccess<'de>, { - let values = de::impls::VecVisitor::new().visit_seq(visitor)?; - Ok(Value::Array(values)) + let mut v = match seq.size_hint() { + Some(cap) => Vec::with_capacity(cap), + None => Vec::new(), + }; + + while let Some(el) = seq.next_element()? { + v.push(el) + } + + Ok(Value::Array(v)) } #[inline] - fn visit_map(&mut self, visitor: V) -> Result + fn visit_map(self, mut map: A) -> Result where - V: de::MapVisitor, + A: de::MapAccess<'de>, { - let values = MapVisitor::new().visit_map(visitor)?; + let mut values = map_with_capacity(map.size_hint()); + while let Some((k, v)) = map.next_entry()? { + values.insert(k, v); + } Ok(Value::Object(values)) } } - deserializer.deserialize(ValueVisitor) + deserializer.deserialize_any(ValueVisitor) } } @@ -438,882 +476,646 @@ impl fmt::Display for Value { impl str::FromStr for Value { type Err = Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { super::de::from_str(s) } } /// Create a `serde::Serializer` that serializes a `Serialize`e into a `Value`. -pub struct Serializer { - value: Value, -} - -impl Serializer { - /// Construct a new `Serializer`. - pub fn new() -> Serializer { - Serializer { value: Value::Null } - } - - /// Unwrap the `Serializer` and return the `Value`. - pub fn unwrap(self) -> Value { - self.value - } -} - -impl Default for Serializer { - fn default() -> Self { - Serializer::new() - } -} - -#[doc(hidden)] -pub struct TupleVariantState { - name: String, - vec: Vec, -} - -#[doc(hidden)] -pub struct StructVariantState { - name: String, - map: Map, -} - -#[doc(hidden)] -pub struct MapState { - map: Map, - next_key: Option, -} +#[derive(Default)] +pub struct Serializer; impl ser::Serializer for Serializer { + type Ok = Value; type Error = Error; - type SeqState = Vec; - type TupleState = Vec; - type TupleStructState = Vec; - type TupleVariantState = TupleVariantState; - type MapState = MapState; - type StructState = MapState; - type StructVariantState = StructVariantState; + type SerializeSeq = SerializeVec; + type SerializeTuple = SerializeVec; + type SerializeTupleStruct = SerializeVec; + type SerializeTupleVariant = SerializeTupleVariant; + type SerializeMap = SerializeMap; + type SerializeStruct = SerializeMap; + type SerializeStructVariant = SerializeStructVariant; #[inline] - fn serialize_bool(&mut self, value: bool) -> Result<(), Error> { - self.value = Value::Bool(value); - Ok(()) + fn serialize_bool(self, value: bool) -> Result { + Ok(Value::Bool(value)) } #[inline] - fn serialize_isize(&mut self, value: isize) -> Result<(), Error> { + fn serialize_i8(self, value: i8) -> Result { self.serialize_i64(value as i64) } #[inline] - fn serialize_i8(&mut self, value: i8) -> Result<(), Error> { + fn serialize_i16(self, value: i16) -> Result { self.serialize_i64(value as i64) } #[inline] - fn serialize_i16(&mut self, value: i16) -> Result<(), Error> { + fn serialize_i32(self, value: i32) -> Result { self.serialize_i64(value as i64) } - #[inline] - fn serialize_i32(&mut self, value: i32) -> Result<(), Error> { - self.serialize_i64(value as i64) - } - - fn serialize_i64(&mut self, value: i64) -> Result<(), Error> { - if value < 0 { - self.value = Value::I64(value); + fn serialize_i64(self, value: i64) -> Result { + let v = if value < 0 { + Value::I64(value) } else { - self.value = Value::U64(value as u64); - } - Ok(()) + Value::U64(value as u64) + }; + Ok(v) } #[inline] - fn serialize_usize(&mut self, value: usize) -> Result<(), Error> { + fn serialize_u8(self, value: u8) -> Result { self.serialize_u64(value as u64) } #[inline] - fn serialize_u8(&mut self, value: u8) -> Result<(), Error> { + fn serialize_u16(self, value: u16) -> Result { self.serialize_u64(value as u64) } #[inline] - fn serialize_u16(&mut self, value: u16) -> Result<(), Error> { + fn serialize_u32(self, value: u32) -> Result { self.serialize_u64(value as u64) } #[inline] - fn serialize_u32(&mut self, value: u32) -> Result<(), Error> { - self.serialize_u64(value as u64) + fn serialize_u64(self, value: u64) -> Result { + Ok(Value::U64(value)) } #[inline] - fn serialize_u64(&mut self, value: u64) -> Result<(), Error> { - self.value = Value::U64(value); - Ok(()) - } - - #[inline] - fn serialize_f32(&mut self, value: f32) -> Result<(), Error> { + fn serialize_f32(self, value: f32) -> Result { self.serialize_f64(value as f64) } #[inline] - fn serialize_f64(&mut self, value: f64) -> Result<(), Error> { - self.value = Value::F64(value); - Ok(()) + fn serialize_f64(self, value: f64) -> Result { + Ok(Value::F64(value)) } #[inline] - fn serialize_char(&mut self, value: char) -> Result<(), Error> { + fn serialize_char(self, value: char) -> Result { let mut s = String::new(); s.push(value); self.serialize_str(&s) } #[inline] - fn serialize_str(&mut self, value: &str) -> Result<(), Error> { - self.value = Value::String(String::from(value)); - Ok(()) + fn serialize_str(self, value: &str) -> Result { + Ok(Value::String(String::from(value))) } - fn serialize_bytes(&mut self, value: &[u8]) -> Result<(), Error> { + fn serialize_bytes(self, value: &[u8]) -> Result { let mut state = self.serialize_seq(Some(value.len()))?; for byte in value { - self.serialize_seq_elt(&mut state, byte)?; + ser::SerializeSeq::serialize_element(&mut state, byte)?; } - self.serialize_seq_end(state) + ser::SerializeSeq::end(state) } #[inline] - fn serialize_unit(&mut self) -> Result<(), Error> { - Ok(()) + fn serialize_unit(self) -> Result { + Ok(Value::Null) } #[inline] - fn serialize_unit_struct(&mut self, _name: &'static str) -> Result<(), Error> { + fn serialize_unit_struct(self, _name: &'static str) -> Result { self.serialize_unit() } #[inline] fn serialize_unit_variant( - &mut self, + self, _name: &'static str, - _variant_index: usize, + _variant_index: u32, variant: &'static str, - ) -> Result<(), Error> { + ) -> Result { self.serialize_str(variant) } #[inline] - fn serialize_newtype_struct(&mut self, _name: &'static str, value: T) -> Result<(), Error> + fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result where - T: ser::Serialize, + T: ?Sized + ser::Serialize, { value.serialize(self) } fn serialize_newtype_variant( - &mut self, + self, _name: &'static str, - _variant_index: usize, + _variant_index: u32, variant: &'static str, - value: T, - ) -> Result<(), Error> + value: &T, + ) -> Result where - T: ser::Serialize, + T: ?Sized + ser::Serialize, { let mut values = Map::new(); - values.insert(String::from(variant), to_value(&value)); - self.value = Value::Object(values); - Ok(()) + values.insert(String::from(variant), to_value(&value)?); + Ok(Value::Object(values)) } #[inline] - fn serialize_none(&mut self) -> Result<(), Error> { + fn serialize_none(self) -> Result { self.serialize_unit() } #[inline] - fn serialize_some(&mut self, value: V) -> Result<(), Error> + fn serialize_some(self, value: &V) -> Result where - V: ser::Serialize, + V: ?Sized + ser::Serialize, { value.serialize(self) } - fn serialize_seq(&mut self, len: Option) -> Result, Error> { - Ok(Vec::with_capacity(len.unwrap_or(0))) + #[inline] + fn serialize_seq(self, len: Option) -> Result { + Ok(SerializeVec { + vec: Vec::with_capacity(len.unwrap_or(0)), + }) } - fn serialize_seq_elt( - &mut self, - state: &mut Vec, - value: T, - ) -> Result<(), Error> - where - T: ser::Serialize, - { - state.push(to_value(&value)); - Ok(()) - } - - fn serialize_seq_end(&mut self, state: Vec) -> Result<(), Error> { - self.value = Value::Array(state); - Ok(()) - } - - fn serialize_seq_fixed_size(&mut self, size: usize) -> Result, Error> { - self.serialize_seq(Some(size)) - } - - fn serialize_tuple(&mut self, len: usize) -> Result, Error> { + #[inline] + fn serialize_tuple(self, len: usize) -> Result { self.serialize_seq(Some(len)) } - fn serialize_tuple_elt( - &mut self, - state: &mut Vec, - value: T, - ) -> Result<(), Error> { - self.serialize_seq_elt(state, value) - } - - fn serialize_tuple_end(&mut self, state: Vec) -> Result<(), Error> { - self.serialize_seq_end(state) - } - + #[inline] fn serialize_tuple_struct( - &mut self, + self, _name: &'static str, len: usize, - ) -> Result, Error> { + ) -> Result { self.serialize_seq(Some(len)) } - fn serialize_tuple_struct_elt( - &mut self, - state: &mut Vec, - value: T, - ) -> Result<(), Error> { - self.serialize_seq_elt(state, value) - } - - fn serialize_tuple_struct_end(&mut self, state: Vec) -> Result<(), Error> { - self.serialize_seq_end(state) - } - + #[inline] fn serialize_tuple_variant( - &mut self, + self, _name: &'static str, - _variant_index: usize, + _variant_index: u32, variant: &'static str, len: usize, - ) -> Result { - Ok(TupleVariantState { - name: String::from(variant), + ) -> Result { + Ok(SerializeTupleVariant { + name: variant, vec: Vec::with_capacity(len), }) } - fn serialize_tuple_variant_elt( - &mut self, - state: &mut TupleVariantState, - value: T, - ) -> Result<(), Error> { - state.vec.push(to_value(&value)); - Ok(()) - } - - fn serialize_tuple_variant_end(&mut self, state: TupleVariantState) -> Result<(), Error> { - let mut object = Map::new(); - - object.insert(state.name, Value::Array(state.vec)); - - self.value = Value::Object(object); - Ok(()) - } - - fn serialize_map(&mut self, _len: Option) -> Result { - Ok(MapState { - map: Map::new(), + #[inline] + fn serialize_map(self, len: Option) -> Result { + Ok(SerializeMap { + map: map_with_capacity(len), next_key: None, }) } - fn serialize_map_key( - &mut self, - state: &mut MapState, - key: T, - ) -> Result<(), Error> { - match to_value(&key) { - Value::String(s) => state.next_key = Some(s), + #[inline] + fn serialize_struct( + self, + _name: &'static str, + len: usize, + ) -> Result { + self.serialize_map(Some(len)) + } + + #[inline] + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + len: usize, + ) -> Result { + Ok(SerializeStructVariant { + name: variant, + map: map_with_capacity(Some(len)), + }) + } +} + +#[doc(hidden)] +pub struct SerializeVec { + vec: Vec, +} + +#[doc(hidden)] +pub struct SerializeTupleVariant { + name: &'static str, + vec: Vec, +} + +#[doc(hidden)] +pub struct SerializeMap { + map: Map, + next_key: Option, +} + +#[doc(hidden)] +pub struct SerializeStructVariant { + name: &'static str, + map: Map, +} + +impl ser::SerializeSeq for SerializeVec { + type Ok = Value; + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<()> + where + T: ?Sized + ser::Serialize, + { + self.vec.push(to_value(&value)?); + Ok(()) + } + + fn end(self) -> Result { + Ok(Value::Array(self.vec)) + } +} + +impl ser::SerializeTuple for SerializeVec { + type Ok = Value; + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<()> + where + T: ?Sized + ser::Serialize, + { + ser::SerializeSeq::serialize_element(self, value) + } + + fn end(self) -> Result { + ser::SerializeSeq::end(self) + } +} + +impl ser::SerializeTupleStruct for SerializeVec { + type Ok = Value; + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> Result<()> + where + T: ?Sized + ser::Serialize, + { + ser::SerializeSeq::serialize_element(self, value) + } + + fn end(self) -> Result { + ser::SerializeSeq::end(self) + } +} + +impl ser::SerializeTupleVariant for SerializeTupleVariant { + type Ok = Value; + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> Result<()> + where + T: ?Sized + ser::Serialize, + { + self.vec.push(to_value(&value)?); + Ok(()) + } + + fn end(self) -> Result { + let mut object = Map::new(); + + object.insert(self.name.to_owned(), Value::Array(self.vec)); + + Ok(Value::Object(object)) + } +} + +impl ser::SerializeMap for SerializeMap { + type Ok = Value; + type Error = Error; + + fn serialize_key(&mut self, key: &T) -> Result<()> + where + T: ?Sized + ser::Serialize, + { + match to_value(key)? { + Value::String(s) => self.next_key = Some(s), _ => return Err(Error::Syntax(ErrorCode::KeyMustBeAString, 0, 0)), }; Ok(()) } - fn serialize_map_value( - &mut self, - state: &mut MapState, - value: T, - ) -> Result<(), Error> { - match state.next_key.take() { - Some(key) => state.map.insert(key, to_value(&value)), - None => { - return Err(Error::Syntax( - ErrorCode::Custom( - "serialize_map_value without \ - matching serialize_map_key" - .to_owned(), - ), - 0, - 0, - )); - } - }; + fn serialize_value(&mut self, value: &T) -> Result<()> + where + T: ?Sized + ser::Serialize, + { + let key = self.next_key.take(); + // Panic because this indicates a bug in the program rather than an + // expected failure. + let key = key.expect("serialize_value called before serialize_key"); + self.map.insert(key, to_value(value)?); Ok(()) } - fn serialize_map_end(&mut self, state: MapState) -> Result<(), Error> { - self.value = Value::Object(state.map); - Ok(()) - } - - fn serialize_struct(&mut self, _name: &'static str, len: usize) -> Result { - self.serialize_map(Some(len)) - } - - fn serialize_struct_elt( - &mut self, - state: &mut MapState, - key: &'static str, - value: V, - ) -> Result<(), Error> { - self.serialize_map_key(state, key)?; - self.serialize_map_value(state, value) - } - - fn serialize_struct_end(&mut self, state: MapState) -> Result<(), Error> { - self.serialize_map_end(state) - } - - fn serialize_struct_variant( - &mut self, - _name: &'static str, - _variant_index: usize, - variant: &'static str, - _len: usize, - ) -> Result { - Ok(StructVariantState { - name: String::from(variant), - map: Map::new(), - }) - } - - fn serialize_struct_variant_elt( - &mut self, - state: &mut StructVariantState, - key: &'static str, - value: V, - ) -> Result<(), Error> { - state.map.insert(String::from(key), to_value(&value)); - Ok(()) - } - - fn serialize_struct_variant_end(&mut self, state: StructVariantState) -> Result<(), Error> { - let mut object = Map::new(); - - object.insert(state.name, Value::Object(state.map)); - - self.value = Value::Object(object); - Ok(()) + fn end(self) -> Result { + Ok(Value::Object(self.map)) } } -/// Creates a `serde::Deserializer` from a `Value` object. -pub struct Deserializer { - value: Option, -} +impl ser::SerializeStruct for SerializeMap { + type Ok = Value; + type Error = Error; -impl Deserializer { - /// Creates a new deserializer instance for deserializing the specified Hjson value. - pub fn new(value: Value) -> Deserializer { - Deserializer { value: Some(value) } + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> + where + T: ?Sized + ser::Serialize, + { + ser::SerializeMap::serialize_entry(self, key, value) + } + + fn end(self) -> Result { + ser::SerializeMap::end(self) } } -impl de::Deserializer for Deserializer { +impl ser::SerializeStructVariant for SerializeStructVariant { + type Ok = Value; + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> + where + T: ?Sized + ser::Serialize, + { + self.map.insert(key.to_owned(), to_value(&value)?); + Ok(()) + } + + fn end(self) -> Result { + let mut object = map_with_capacity(Some(1)); + + object.insert(self.name.to_owned(), Value::Object(self.map)); + + Ok(Value::Object(object)) + } +} + +impl<'de> de::Deserializer<'de> for Value { type Error = Error; #[inline] - fn deserialize(&mut self, mut visitor: V) -> Result + fn deserialize_any(self, visitor: V) -> Result where - V: de::Visitor, + V: de::Visitor<'de>, { - let value = match self.value.take() { - Some(value) => value, - None => { - return Err(de::Error::end_of_stream()); - } - }; - - match value { + match self { Value::Null => visitor.visit_unit(), Value::Bool(v) => visitor.visit_bool(v), Value::I64(v) => visitor.visit_i64(v), Value::U64(v) => visitor.visit_u64(v), Value::F64(v) => visitor.visit_f64(v), Value::String(v) => visitor.visit_string(v), - Value::Array(v) => { - let len = v.len(); - visitor.visit_seq(SeqDeserializer { - de: self, - iter: v.into_iter(), - len, - }) - } - Value::Object(v) => { - let len = v.len(); - visitor.visit_map(MapDeserializer { - de: self, - iter: v.into_iter(), - value: None, - len, - }) - } + Value::Array(v) => visitor.visit_seq(SeqDeserializer { + iter: v.into_iter(), + }), + Value::Object(v) => visitor.visit_map(MapDeserializer { + iter: v.into_iter(), + value: None, + }), } } #[inline] - fn deserialize_option(&mut self, mut visitor: V) -> Result + fn deserialize_option(self, visitor: V) -> Result where - V: de::Visitor, + V: de::Visitor<'de>, { - match self.value { - Some(Value::Null) => visitor.visit_none(), - Some(_) => visitor.visit_some(self), - None => Err(de::Error::end_of_stream()), + match self { + Value::Null => visitor.visit_none(), + _ => visitor.visit_some(self), } } #[inline] fn deserialize_enum( - &mut self, + self, _name: &str, _variants: &'static [&'static str], - mut visitor: V, - ) -> Result + visitor: V, + ) -> Result where - V: de::EnumVisitor, + V: de::Visitor<'de>, { - let (variant, value) = match self.value.take() { - Some(Value::Object(value)) => { + let (variant, value) = match self { + Value::Object(value) => { let mut iter = value.into_iter(); let (variant, value) = match iter.next() { Some(v) => v, None => { - return Err(de::Error::invalid_type(de::Type::VariantName)); + return Err(de::Error::invalid_type( + de::Unexpected::Map, + &"map with a single key", + )); } }; // enums are encoded in json as maps with a single key:value pair if iter.next().is_some() { - return Err(de::Error::invalid_type(de::Type::Map)); + return Err(de::Error::invalid_type( + de::Unexpected::Map, + &"map with a single key", + )); } (variant, Some(value)) } - Some(Value::String(variant)) => (variant, None), - Some(_) => { - return Err(de::Error::invalid_type(de::Type::Enum)); - } - None => { - return Err(de::Error::end_of_stream()); + Value::String(variant) => (variant, None), + val => { + return Err(de::Error::invalid_type( + val.as_unexpected(), + &"string or map", + )) } }; - visitor.visit(VariantDeserializer { - de: self, - val: value, - variant: Some(Value::String(variant)), - }) + visitor.visit_enum(EnumDeserializer { value, variant }) } #[inline] fn deserialize_newtype_struct( - &mut self, + self, _name: &'static str, - mut visitor: V, + visitor: V, ) -> Result where - V: de::Visitor, + V: de::Visitor<'de>, { visitor.visit_newtype_struct(self) } - forward_to_deserialize! { - deserialize_bool(); - deserialize_usize(); - deserialize_u8(); - deserialize_u16(); - deserialize_u32(); - deserialize_u64(); - deserialize_isize(); - deserialize_i8(); - deserialize_i16(); - deserialize_i32(); - deserialize_i64(); - deserialize_f32(); - deserialize_f64(); - deserialize_char(); - deserialize_str(); - deserialize_string(); - deserialize_unit(); - deserialize_seq(); - deserialize_seq_fixed_size(len: usize); - deserialize_bytes(); - deserialize_map(); - deserialize_unit_struct(name: &'static str); - deserialize_tuple_struct(name: &'static str, len: usize); - deserialize_struct(name: &'static str, fields: &'static [&'static str]); - deserialize_struct_field(); - deserialize_tuple(len: usize); - deserialize_ignored_any(); + serde::forward_to_deserialize_any! { + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string + bytes byte_buf unit unit_struct seq tuple + tuple_struct map struct identifier ignored_any } } -struct VariantDeserializer<'a> { - de: &'a mut Deserializer, - val: Option, - variant: Option, +struct EnumDeserializer { + variant: String, + value: Option, } -impl<'a> de::VariantVisitor for VariantDeserializer<'a> { +impl<'de> de::EnumAccess<'de> for EnumDeserializer { type Error = Error; - fn visit_variant(&mut self) -> Result - where - V: de::Deserialize, - { - let variant = self.variant.take().expect("variant is missing"); - de::Deserialize::deserialize(&mut Deserializer::new(variant)) - } + type Variant = VariantDeserializer; - fn visit_unit(&mut self) -> Result<(), Error> { - match self.val.take() { - Some(val) => de::Deserialize::deserialize(&mut Deserializer::new(val)), + fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error> + where + V: de::DeserializeSeed<'de>, + { + let variant = de::IntoDeserializer::into_deserializer(self.variant); + let visitor = VariantDeserializer { val: self.value }; + seed.deserialize(variant).map(|v| (v, visitor)) + } +} + +struct VariantDeserializer { + val: Option, +} + +impl<'de, 'a> de::VariantAccess<'de> for VariantDeserializer { + type Error = Error; + + fn unit_variant(self) -> Result<()> { + match self.val { + Some(val) => de::Deserialize::deserialize(val), None => Ok(()), } } - fn visit_newtype(&mut self) -> Result + fn newtype_variant_seed(self, seed: T) -> Result where - T: de::Deserialize, + T: de::DeserializeSeed<'de>, { - let val = self.val.take().expect("val is missing"); - de::Deserialize::deserialize(&mut Deserializer::new(val)) + match self.val { + Some(value) => seed.deserialize(value), + None => Err(serde::de::Error::invalid_type( + de::Unexpected::UnitVariant, + &"newtype variant", + )), + } } - fn visit_tuple(&mut self, _len: usize, visitor: V) -> Result + fn tuple_variant(self, _len: usize, visitor: V) -> Result where - V: de::Visitor, + V: de::Visitor<'de>, { - let val = self.val.take().expect("val is missing"); + let val = self.val.expect("val is missing"); if let Value::Array(fields) = val { - de::Deserializer::deserialize( - &mut SeqDeserializer { - de: self.de, - len: fields.len(), - iter: fields.into_iter(), - }, - visitor, - ) + visitor.visit_seq(SeqDeserializer { + iter: fields.into_iter(), + }) } else { - Err(de::Error::invalid_type(de::Type::Tuple)) + Err(de::Error::invalid_type(val.as_unexpected(), &visitor)) } } - fn visit_struct( - &mut self, - _fields: &'static [&'static str], - visitor: V, - ) -> Result + fn struct_variant(self, _fields: &'static [&'static str], visitor: V) -> Result where - V: de::Visitor, + V: de::Visitor<'de>, { - let val = self.val.take().expect("val is missing"); - if let Value::Object(fields) = val { - de::Deserializer::deserialize( - &mut MapDeserializer { - de: self.de, - len: fields.len(), - iter: fields.into_iter(), - value: None, - }, - visitor, - ) - } else { - Err(de::Error::invalid_type(de::Type::Struct)) + match self.val { + Some(Value::Object(fields)) => visitor.visit_map(MapDeserializer { + iter: fields.into_iter(), + value: None, + }), + Some(other) => Err(de::Error::invalid_type( + other.as_unexpected(), + &"struct variant", + )), + None => Err(de::Error::invalid_type( + de::Unexpected::UnitVariant, + &"struct variant", + )), } } } -struct SeqDeserializer<'a> { - de: &'a mut Deserializer, +struct SeqDeserializer { iter: vec::IntoIter, - len: usize, } -impl<'a> de::Deserializer for SeqDeserializer<'a> { +impl<'de> de::SeqAccess<'de> for SeqDeserializer { type Error = Error; - #[inline] - fn deserialize(&mut self, mut visitor: V) -> Result + fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> where - V: de::Visitor, - { - if self.len == 0 { - visitor.visit_unit() - } else { - visitor.visit_seq(self) - } - } - - forward_to_deserialize! { - deserialize_bool(); - deserialize_usize(); - deserialize_u8(); - deserialize_u16(); - deserialize_u32(); - deserialize_u64(); - deserialize_isize(); - deserialize_i8(); - deserialize_i16(); - deserialize_i32(); - deserialize_i64(); - deserialize_f32(); - deserialize_f64(); - deserialize_char(); - deserialize_str(); - deserialize_string(); - deserialize_unit(); - deserialize_option(); - deserialize_seq(); - deserialize_seq_fixed_size(len: usize); - deserialize_bytes(); - deserialize_map(); - deserialize_unit_struct(name: &'static str); - deserialize_newtype_struct(name: &'static str); - deserialize_tuple_struct(name: &'static str, len: usize); - deserialize_struct(name: &'static str, fields: &'static [&'static str]); - deserialize_struct_field(); - deserialize_tuple(len: usize); - deserialize_enum(name: &'static str, variants: &'static [&'static str]); - deserialize_ignored_any(); - } -} - -impl<'a> de::SeqVisitor for SeqDeserializer<'a> { - type Error = Error; - - fn visit(&mut self) -> Result, Error> - where - T: de::Deserialize, + T: de::DeserializeSeed<'de>, { match self.iter.next() { - Some(value) => { - self.len -= 1; - self.de.value = Some(value); - Ok(Some(de::Deserialize::deserialize(self.de)?)) - } + Some(value) => Ok(Some(seed.deserialize(value)?)), None => Ok(None), } } - fn end(&mut self) -> Result<(), Error> { - if self.len == 0 { - Ok(()) - } else { - Err(de::Error::invalid_length(self.len)) + fn size_hint(&self) -> Option { + match self.iter.size_hint() { + (lower, Some(upper)) if lower == upper => Some(upper), + _ => None, } } - - fn size_hint(&self) -> (usize, Option) { - (self.len, Some(self.len)) - } } -struct MapDeserializer<'a> { - de: &'a mut Deserializer, +struct MapDeserializer { iter: MapIntoIter, value: Option, - len: usize, } -impl<'a> de::MapVisitor for MapDeserializer<'a> { +impl<'de, 'a> de::MapAccess<'de> for MapDeserializer { type Error = Error; - fn visit_key(&mut self) -> Result, Error> + fn next_key_seed(&mut self, seed: K) -> Result> where - T: de::Deserialize, + K: de::DeserializeSeed<'de>, { match self.iter.next() { Some((key, value)) => { - self.len -= 1; self.value = Some(value); - self.de.value = Some(Value::String(key)); - Ok(Some(de::Deserialize::deserialize(self.de)?)) + Ok(Some(seed.deserialize(Value::String(key))?)) } None => Ok(None), } } - fn visit_value(&mut self) -> Result + fn next_value_seed(&mut self, seed: V) -> Result where - T: de::Deserialize, + V: de::DeserializeSeed<'de>, { let value = self.value.take().expect("value is missing"); - self.de.value = Some(value); - de::Deserialize::deserialize(self.de) + Ok(seed.deserialize(value)?) } - fn end(&mut self) -> Result<(), Error> { - if self.len == 0 { - Ok(()) - } else { - Err(de::Error::invalid_length(self.len)) + fn size_hint(&self) -> Option { + match self.iter.size_hint() { + (lower, Some(upper)) if lower == upper => Some(upper), + _ => None, } } - - fn missing_field(&mut self, field: &'static str) -> Result - where - V: de::Deserialize, - { - struct MissingFieldDeserializer(&'static str); - - impl de::Deserializer for MissingFieldDeserializer { - type Error = de::value::Error; - - fn deserialize(&mut self, _visitor: V) -> Result - where - V: de::Visitor, - { - let &mut MissingFieldDeserializer(field) = self; - Err(de::value::Error::MissingField(field)) - } - - fn deserialize_option(&mut self, mut visitor: V) -> Result - where - V: de::Visitor, - { - visitor.visit_none() - } - - forward_to_deserialize! { - deserialize_bool(); - deserialize_usize(); - deserialize_u8(); - deserialize_u16(); - deserialize_u32(); - deserialize_u64(); - deserialize_isize(); - deserialize_i8(); - deserialize_i16(); - deserialize_i32(); - deserialize_i64(); - deserialize_f32(); - deserialize_f64(); - deserialize_char(); - deserialize_str(); - deserialize_string(); - deserialize_unit(); - deserialize_seq(); - deserialize_seq_fixed_size(len: usize); - deserialize_bytes(); - deserialize_map(); - deserialize_unit_struct(name: &'static str); - deserialize_newtype_struct(name: &'static str); - deserialize_tuple_struct(name: &'static str, len: usize); - deserialize_struct(name: &'static str, fields: &'static [&'static str]); - deserialize_struct_field(); - deserialize_tuple(len: usize); - deserialize_enum(name: &'static str, variants: &'static [&'static str]); - deserialize_ignored_any(); - } - } - - let mut de = MissingFieldDeserializer(field); - Ok(de::Deserialize::deserialize(&mut de)?) - } - - fn size_hint(&self) -> (usize, Option) { - (self.len, Some(self.len)) - } } -impl<'a> de::Deserializer for MapDeserializer<'a> { - type Error = Error; - - #[inline] - fn deserialize(&mut self, mut visitor: V) -> Result - where - V: de::Visitor, - { - visitor.visit_map(self) - } - - forward_to_deserialize! { - deserialize_bool(); - deserialize_usize(); - deserialize_u8(); - deserialize_u16(); - deserialize_u32(); - deserialize_u64(); - deserialize_isize(); - deserialize_i8(); - deserialize_i16(); - deserialize_i32(); - deserialize_i64(); - deserialize_f32(); - deserialize_f64(); - deserialize_char(); - deserialize_str(); - deserialize_string(); - deserialize_unit(); - deserialize_option(); - deserialize_seq(); - deserialize_seq_fixed_size(len: usize); - deserialize_bytes(); - deserialize_map(); - deserialize_unit_struct(name: &'static str); - deserialize_newtype_struct(name: &'static str); - deserialize_tuple_struct(name: &'static str, len: usize); - deserialize_struct(name: &'static str, fields: &'static [&'static str]); - deserialize_struct_field(); - deserialize_tuple(len: usize); - deserialize_enum(name: &'static str, variants: &'static [&'static str]); - deserialize_ignored_any(); - } -} - -pub fn to_value(value: &T) -> Value +pub fn to_value(value: &T) -> Result where T: ser::Serialize, { - let mut ser = Serializer::new(); - value.serialize(&mut ser).expect("failed to serialize"); - ser.unwrap() + value.serialize(Serializer) } /// Shortcut function to decode a Hjson `Value` into a `T` -pub fn from_value(value: Value) -> Result +pub fn from_value(value: Value) -> Result where - T: de::Deserialize, + T: de::DeserializeOwned, { - let mut de = Deserializer::new(value); - de::Deserialize::deserialize(&mut de) + de::Deserialize::deserialize(value) } /// A trait for converting values to Hjson @@ -1327,7 +1129,7 @@ where T: ser::Serialize, { fn to_json(&self) -> Value { - to_value(&self) + to_value(&self).expect("failed to serialize") } } @@ -1338,12 +1140,28 @@ mod test { #[test] fn number_deserialize() { - let v: Value = from_str("{\"a\":1}").expect("Internal error: json parsing"); - let vo = v.as_object().expect("Internal error: json parsing"); - assert_eq!(vo["a"].as_u64().expect("Internal error: json parsing"), 1); + let v: Value = from_str("{\"a\":1}").unwrap(); + let vo = v.as_object().unwrap(); + assert_eq!(vo["a"].as_u64().unwrap(), 1); - let v: Value = from_str("{\"a\":-1}").expect("Internal error: json parsing"); - let vo = v.as_object().expect("Internal error: json parsing"); - assert_eq!(vo["a"].as_i64().expect("Internal error: json parsing"), -1); + let v: Value = from_str("{\"a\":-1}").unwrap(); + let vo = v.as_object().unwrap(); + assert_eq!(vo["a"].as_i64().unwrap(), -1); + + let v: Value = from_str("{\"a\":1.1}").unwrap(); + let vo = v.as_object().unwrap(); + assert_eq!(vo["a"].as_f64().unwrap(), 1.1); + + let v: Value = from_str("{\"a\":-1.1}").unwrap(); + let vo = v.as_object().unwrap(); + assert_eq!(vo["a"].as_f64().unwrap(), -1.1); + + let v: Value = from_str("{\"a\":1e6}").unwrap(); + let vo = v.as_object().unwrap(); + assert_eq!(vo["a"].as_f64().unwrap(), 1e6); + + let v: Value = from_str("{\"a\":-1e6}").unwrap(); + let vo = v.as_object().unwrap(); + assert_eq!(vo["a"].as_f64().unwrap(), -1e6); } } diff --git a/crates/nu-json/tests/main.rs b/crates/nu-json/tests/main.rs new file mode 100644 index 000000000..c06d83e0c --- /dev/null +++ b/crates/nu-json/tests/main.rs @@ -0,0 +1,214 @@ +extern crate nu_json; +extern crate nu_test_support; +extern crate serde; +extern crate serde_json; + +use nu_json::Value; +use regex::Regex; +use std::fs; +use std::io; +use std::path::{Path, PathBuf}; + +fn txt(text: &str) -> String { + let out = String::from_utf8_lossy(text.as_bytes()); + + #[cfg(windows)] + { + out.replace("\r\n", "").replace("\n", "") + } + + #[cfg(not(windows))] + { + out.to_string() + } +} + +fn hjson_expectations() -> PathBuf { + let assets = nu_test_support::fs::assets().join("nu_json"); + + dunce::canonicalize(assets.clone()).unwrap_or_else(|e| { + panic!( + "Couldn't canonicalize hjson assets path {}: {:?}", + assets.display(), + e + ) + }) +} + +fn get_test_content(name: &str) -> io::Result { + let expectations = hjson_expectations(); + + let mut p = format!("{}/{}_test.hjson", expectations.display(), name); + + if !Path::new(&p).exists() { + p = format!("{}/{}_test.json", expectations.display(), name); + } + + fs::read_to_string(&p) +} + +fn get_result_content(name: &str) -> io::Result<(String, String)> { + let expectations = hjson_expectations(); + + let p1 = format!("{}/{}_result.json", expectations.display(), name); + let p2 = format!("{}/{}_result.hjson", expectations.display(), name); + + Ok((fs::read_to_string(&p1)?, fs::read_to_string(&p2)?)) +} + +macro_rules! run_test { + // {{ is a workaround for rust stable + ($v: ident, $list: expr, $fix: expr) => {{ + let name = stringify!($v); + $list.push(format!("{}_test", name)); + println!("- running {}", name); + let should_fail = name.starts_with("fail"); + let test_content = get_test_content(name).unwrap(); + let data: nu_json::Result = nu_json::from_str(&test_content); + assert!(should_fail == data.is_err()); + + if !should_fail { + let udata = data.unwrap(); + let (rjson, rhjson) = get_result_content(name).unwrap(); + let rjson = txt(&rjson); + let rhjson = txt(&rhjson); + let actual_hjson = nu_json::to_string(&udata).unwrap(); + let actual_hjson = txt(&actual_hjson); + let actual_json = $fix(serde_json::to_string_pretty(&udata).unwrap()); + let actual_json = txt(&actual_json); + if rhjson != actual_hjson { + println!( + "{:?}\n---hjson expected\n{}\n---hjson actual\n{}\n---\n", + name, rhjson, actual_hjson + ); + } + if rjson != actual_json { + println!( + "{:?}\n---json expected\n{}\n---json actual\n{}\n---\n", + name, rjson, actual_json + ); + } + assert!(rhjson == actual_hjson && rjson == actual_json); + } + }}; +} + +// add fixes where rust's json differs from javascript + +fn std_fix(json: String) -> String { + // serde_json serializes integers with a superfluous .0 suffix + let re = Regex::new(r"(?m)(?P\d)\.0(?P,?)$").unwrap(); + re.replace_all(&json, "$d$s").to_string() +} + +fn fix_kan(json: String) -> String { + std_fix(json).replace(" -0,", " 0,") +} + +fn fix_pass1(json: String) -> String { + std_fix(json) + .replace("1.23456789e34", "1.23456789e+34") + .replace("2.3456789012e76", "2.3456789012e+76") +} + +#[test] +fn test_hjson() { + let mut done: Vec = Vec::new(); + + println!(""); + run_test!(charset, done, std_fix); + run_test!(comments, done, std_fix); + run_test!(empty, done, std_fix); + run_test!(failCharset1, done, std_fix); + run_test!(failJSON02, done, std_fix); + run_test!(failJSON05, done, std_fix); + run_test!(failJSON06, done, std_fix); + run_test!(failJSON07, done, std_fix); + run_test!(failJSON08, done, std_fix); + run_test!(failJSON10, done, std_fix); + run_test!(failJSON11, done, std_fix); + run_test!(failJSON12, done, std_fix); + run_test!(failJSON13, done, std_fix); + run_test!(failJSON14, done, std_fix); + run_test!(failJSON15, done, std_fix); + run_test!(failJSON16, done, std_fix); + run_test!(failJSON17, done, std_fix); + run_test!(failJSON19, done, std_fix); + run_test!(failJSON20, done, std_fix); + run_test!(failJSON21, done, std_fix); + run_test!(failJSON22, done, std_fix); + run_test!(failJSON23, done, std_fix); + run_test!(failJSON24, done, std_fix); + run_test!(failJSON26, done, std_fix); + run_test!(failJSON28, done, std_fix); + run_test!(failJSON29, done, std_fix); + run_test!(failJSON30, done, std_fix); + run_test!(failJSON31, done, std_fix); + run_test!(failJSON32, done, std_fix); + run_test!(failJSON33, done, std_fix); + run_test!(failJSON34, done, std_fix); + run_test!(failKey1, done, std_fix); + run_test!(failKey2, done, std_fix); + run_test!(failKey3, done, std_fix); + run_test!(failKey4, done, std_fix); + run_test!(failMLStr1, done, std_fix); + run_test!(failObj1, done, std_fix); + run_test!(failObj2, done, std_fix); + run_test!(failObj3, done, std_fix); + run_test!(failStr1a, done, std_fix); + run_test!(failStr1b, done, std_fix); + run_test!(failStr1c, done, std_fix); + run_test!(failStr1d, done, std_fix); + run_test!(failStr2a, done, std_fix); + run_test!(failStr2b, done, std_fix); + run_test!(failStr2c, done, std_fix); + run_test!(failStr2d, done, std_fix); + run_test!(failStr3a, done, std_fix); + run_test!(failStr3b, done, std_fix); + run_test!(failStr3c, done, std_fix); + run_test!(failStr3d, done, std_fix); + run_test!(failStr4a, done, std_fix); + run_test!(failStr4b, done, std_fix); + run_test!(failStr4c, done, std_fix); + run_test!(failStr4d, done, std_fix); + run_test!(failStr5a, done, std_fix); + run_test!(failStr5b, done, std_fix); + run_test!(failStr5c, done, std_fix); + run_test!(failStr5d, done, std_fix); + run_test!(failStr6a, done, std_fix); + run_test!(failStr6b, done, std_fix); + run_test!(failStr6c, done, std_fix); + run_test!(failStr6d, done, std_fix); + run_test!(kan, done, fix_kan); + run_test!(keys, done, std_fix); + run_test!(oa, done, std_fix); + run_test!(pass1, done, fix_pass1); + run_test!(pass2, done, std_fix); + run_test!(pass3, done, std_fix); + run_test!(pass4, done, std_fix); + run_test!(passSingle, done, std_fix); + run_test!(root, done, std_fix); + run_test!(stringify1, done, std_fix); + run_test!(strings, done, std_fix); + run_test!(trail, done, std_fix); + + // check if we include all assets + let paths = fs::read_dir(hjson_expectations()).unwrap(); + + let all = paths + .map(|item| String::from(item.unwrap().path().file_stem().unwrap().to_str().unwrap())) + .filter(|x| x.contains("_test")) + .collect::>(); + + let missing = all + .into_iter() + .filter(|x| done.iter().find(|y| &x == y) == None) + .collect::>(); + + if missing.len() > 0 { + for item in missing { + println!("missing: {}", item); + } + assert!(false); + } +} diff --git a/crates/nu-test-support/src/fs.rs b/crates/nu-test-support/src/fs.rs index 43ea4faa6..c9ef2fa24 100644 --- a/crates/nu-test-support/src/fs.rs +++ b/crates/nu-test-support/src/fs.rs @@ -256,6 +256,10 @@ pub fn fixtures() -> PathBuf { root().join("tests/fixtures") } +pub fn assets() -> PathBuf { + root().join("tests/assets") +} + pub fn in_directory(str: impl AsRef) -> String { let path = str.as_ref(); let path = if path.is_relative() { diff --git a/tests/assets/nu_json/charset_result.hjson b/tests/assets/nu_json/charset_result.hjson new file mode 100644 index 000000000..d1573b616 --- /dev/null +++ b/tests/assets/nu_json/charset_result.hjson @@ -0,0 +1,5 @@ +{ + ql-ascii: ! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ + js-ascii: ! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ + ml-ascii: ! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ +} \ No newline at end of file diff --git a/tests/assets/nu_json/charset_result.json b/tests/assets/nu_json/charset_result.json new file mode 100644 index 000000000..6357d96db --- /dev/null +++ b/tests/assets/nu_json/charset_result.json @@ -0,0 +1,5 @@ +{ + "ql-ascii": "! \"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", + "js-ascii": "! \"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", + "ml-ascii": "! \"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" +} \ No newline at end of file diff --git a/tests/assets/nu_json/charset_test.hjson b/tests/assets/nu_json/charset_test.hjson new file mode 100644 index 000000000..7527b1e45 --- /dev/null +++ b/tests/assets/nu_json/charset_test.hjson @@ -0,0 +1,6 @@ +ql-ascii: ! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ +js-ascii: "! \"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" +ml-ascii: + ''' + ! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ + ''' diff --git a/tests/assets/nu_json/comments_result.hjson b/tests/assets/nu_json/comments_result.hjson new file mode 100644 index 000000000..a99ce23db --- /dev/null +++ b/tests/assets/nu_json/comments_result.hjson @@ -0,0 +1,26 @@ +{ + foo1: This is a string value. # part of the string + foo2: This is a string value. + bar1: This is a string value. // part of the string + bar2: This is a string value. + foobar1: This is a string value./* part of the string */ + foobar2: This is a string value. + rem1: "# test" + rem2: "// test" + rem3: "/* test */" + num1: 0 + num2: 0 + num3: 2 + true1: true + true2: true + true3: true + false1: false + false2: false + false3: false + null1: null + null2: null + null3: null + str1: 00 # part of the string + str2: 00.0 // part of the string + str3: 02 /* part of the string */ +} \ No newline at end of file diff --git a/tests/assets/nu_json/comments_result.json b/tests/assets/nu_json/comments_result.json new file mode 100644 index 000000000..e247803e6 --- /dev/null +++ b/tests/assets/nu_json/comments_result.json @@ -0,0 +1,26 @@ +{ + "foo1": "This is a string value. # part of the string", + "foo2": "This is a string value.", + "bar1": "This is a string value. // part of the string", + "bar2": "This is a string value.", + "foobar1": "This is a string value./* part of the string */", + "foobar2": "This is a string value.", + "rem1": "# test", + "rem2": "// test", + "rem3": "/* test */", + "num1": 0, + "num2": 0, + "num3": 2, + "true1": true, + "true2": true, + "true3": true, + "false1": false, + "false2": false, + "false3": false, + "null1": null, + "null2": null, + "null3": null, + "str1": "00 # part of the string", + "str2": "00.0 // part of the string", + "str3": "02 /* part of the string */" +} \ No newline at end of file diff --git a/tests/assets/nu_json/comments_test.hjson b/tests/assets/nu_json/comments_test.hjson new file mode 100644 index 000000000..7f1dfdcab --- /dev/null +++ b/tests/assets/nu_json/comments_test.hjson @@ -0,0 +1,48 @@ +// test +# all +// comment +/* +styles +*/ +# with lf + + + +# ! + +{ + # hjson style comment + foo1: This is a string value. # part of the string + foo2: "This is a string value." # a comment + + // js style comment + bar1: This is a string value. // part of the string + bar2: "This is a string value." // a comment + + /* js block style comments */foobar1:/* more */This is a string value./* part of the string */ + /* js block style comments */foobar2:/* more */"This is a string value."/* a comment */ + + rem1: "# test" + rem2: "// test" + rem3: "/* test */" + + num1: 0 # comment + num2: 0.0 // comment + num3: 2 /* comment */ + + true1: true # comment + true2: true // comment + true3: true /* comment */ + + false1: false # comment + false2: false // comment + false3: false /* comment */ + + null1: null # comment + null2: null // comment + null3: null /* comment */ + + str1: 00 # part of the string + str2: 00.0 // part of the string + str3: 02 /* part of the string */ +} diff --git a/tests/assets/nu_json/empty_result.hjson b/tests/assets/nu_json/empty_result.hjson new file mode 100644 index 000000000..a75b45b28 --- /dev/null +++ b/tests/assets/nu_json/empty_result.hjson @@ -0,0 +1,3 @@ +{ + "": empty +} \ No newline at end of file diff --git a/tests/assets/nu_json/empty_result.json b/tests/assets/nu_json/empty_result.json new file mode 100644 index 000000000..47f710fe2 --- /dev/null +++ b/tests/assets/nu_json/empty_result.json @@ -0,0 +1,3 @@ +{ + "": "empty" +} \ No newline at end of file diff --git a/tests/assets/nu_json/empty_test.hjson b/tests/assets/nu_json/empty_test.hjson new file mode 100644 index 000000000..ac97a9d7c --- /dev/null +++ b/tests/assets/nu_json/empty_test.hjson @@ -0,0 +1,3 @@ +{ + "": empty +} diff --git a/tests/assets/nu_json/failCharset1_test.hjson b/tests/assets/nu_json/failCharset1_test.hjson new file mode 100644 index 000000000..da280393b --- /dev/null +++ b/tests/assets/nu_json/failCharset1_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid \u char + char: "\uxxxx" +} diff --git a/tests/assets/nu_json/failJSON02_test.json b/tests/assets/nu_json/failJSON02_test.json new file mode 100644 index 000000000..6b7c11e5a --- /dev/null +++ b/tests/assets/nu_json/failJSON02_test.json @@ -0,0 +1 @@ +["Unclosed array" \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON05_test.json b/tests/assets/nu_json/failJSON05_test.json new file mode 100644 index 000000000..ddf3ce3d2 --- /dev/null +++ b/tests/assets/nu_json/failJSON05_test.json @@ -0,0 +1 @@ +["double extra comma",,] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON06_test.json b/tests/assets/nu_json/failJSON06_test.json new file mode 100644 index 000000000..ed91580e1 --- /dev/null +++ b/tests/assets/nu_json/failJSON06_test.json @@ -0,0 +1 @@ +[ , "<-- missing value"] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON07_test.json b/tests/assets/nu_json/failJSON07_test.json new file mode 100644 index 000000000..8a96af3e4 --- /dev/null +++ b/tests/assets/nu_json/failJSON07_test.json @@ -0,0 +1 @@ +["Comma after the close"], \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON08_test.json b/tests/assets/nu_json/failJSON08_test.json new file mode 100644 index 000000000..b28479c6e --- /dev/null +++ b/tests/assets/nu_json/failJSON08_test.json @@ -0,0 +1 @@ +["Extra close"]] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON10_test.json b/tests/assets/nu_json/failJSON10_test.json new file mode 100644 index 000000000..5d8c0047b --- /dev/null +++ b/tests/assets/nu_json/failJSON10_test.json @@ -0,0 +1 @@ +{"Extra value after close": true} "misplaced quoted value" \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON11_test.json b/tests/assets/nu_json/failJSON11_test.json new file mode 100644 index 000000000..76eb95b45 --- /dev/null +++ b/tests/assets/nu_json/failJSON11_test.json @@ -0,0 +1 @@ +{"Illegal expression": 1 + 2} \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON12_test.json b/tests/assets/nu_json/failJSON12_test.json new file mode 100644 index 000000000..77580a452 --- /dev/null +++ b/tests/assets/nu_json/failJSON12_test.json @@ -0,0 +1 @@ +{"Illegal invocation": alert()} \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON13_test.json b/tests/assets/nu_json/failJSON13_test.json new file mode 100644 index 000000000..379406b59 --- /dev/null +++ b/tests/assets/nu_json/failJSON13_test.json @@ -0,0 +1 @@ +{"Numbers cannot have leading zeroes": 013} \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON14_test.json b/tests/assets/nu_json/failJSON14_test.json new file mode 100644 index 000000000..0ed366b38 --- /dev/null +++ b/tests/assets/nu_json/failJSON14_test.json @@ -0,0 +1 @@ +{"Numbers cannot be hex": 0x14} \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON15_test.json b/tests/assets/nu_json/failJSON15_test.json new file mode 100644 index 000000000..fc8376b60 --- /dev/null +++ b/tests/assets/nu_json/failJSON15_test.json @@ -0,0 +1 @@ +["Illegal backslash escape: \x15"] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON16_test.json b/tests/assets/nu_json/failJSON16_test.json new file mode 100644 index 000000000..3fe21d4b5 --- /dev/null +++ b/tests/assets/nu_json/failJSON16_test.json @@ -0,0 +1 @@ +[\naked] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON17_test.json b/tests/assets/nu_json/failJSON17_test.json new file mode 100644 index 000000000..62b9214ae --- /dev/null +++ b/tests/assets/nu_json/failJSON17_test.json @@ -0,0 +1 @@ +["Illegal backslash escape: \017"] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON19_test.json b/tests/assets/nu_json/failJSON19_test.json new file mode 100644 index 000000000..3b9c46fa9 --- /dev/null +++ b/tests/assets/nu_json/failJSON19_test.json @@ -0,0 +1 @@ +{"Missing colon" null} \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON20_test.json b/tests/assets/nu_json/failJSON20_test.json new file mode 100644 index 000000000..27c1af3e7 --- /dev/null +++ b/tests/assets/nu_json/failJSON20_test.json @@ -0,0 +1 @@ +{"Double colon":: null} \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON21_test.json b/tests/assets/nu_json/failJSON21_test.json new file mode 100644 index 000000000..62474573b --- /dev/null +++ b/tests/assets/nu_json/failJSON21_test.json @@ -0,0 +1 @@ +{"Comma instead of colon", null} \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON22_test.json b/tests/assets/nu_json/failJSON22_test.json new file mode 100644 index 000000000..a7752581b --- /dev/null +++ b/tests/assets/nu_json/failJSON22_test.json @@ -0,0 +1 @@ +["Colon instead of comma": false] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON23_test.json b/tests/assets/nu_json/failJSON23_test.json new file mode 100644 index 000000000..494add1ca --- /dev/null +++ b/tests/assets/nu_json/failJSON23_test.json @@ -0,0 +1 @@ +["Bad value", truth] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON24_test.json b/tests/assets/nu_json/failJSON24_test.json new file mode 100644 index 000000000..caff239bf --- /dev/null +++ b/tests/assets/nu_json/failJSON24_test.json @@ -0,0 +1 @@ +['single quote'] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON26_test.json b/tests/assets/nu_json/failJSON26_test.json new file mode 100644 index 000000000..845d26a6a --- /dev/null +++ b/tests/assets/nu_json/failJSON26_test.json @@ -0,0 +1 @@ +["tab\ character\ in\ string\ "] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON28_test.json b/tests/assets/nu_json/failJSON28_test.json new file mode 100644 index 000000000..621a0101c --- /dev/null +++ b/tests/assets/nu_json/failJSON28_test.json @@ -0,0 +1,2 @@ +["line\ +break"] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON29_test.json b/tests/assets/nu_json/failJSON29_test.json new file mode 100644 index 000000000..47ec421bb --- /dev/null +++ b/tests/assets/nu_json/failJSON29_test.json @@ -0,0 +1 @@ +[0e] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON30_test.json b/tests/assets/nu_json/failJSON30_test.json new file mode 100644 index 000000000..8ab0bc4b8 --- /dev/null +++ b/tests/assets/nu_json/failJSON30_test.json @@ -0,0 +1 @@ +[0e+] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON31_test.json b/tests/assets/nu_json/failJSON31_test.json new file mode 100644 index 000000000..1cce602b5 --- /dev/null +++ b/tests/assets/nu_json/failJSON31_test.json @@ -0,0 +1 @@ +[0e+-1] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON32_test.json b/tests/assets/nu_json/failJSON32_test.json new file mode 100644 index 000000000..45cba7396 --- /dev/null +++ b/tests/assets/nu_json/failJSON32_test.json @@ -0,0 +1 @@ +{"Comma instead if closing brace": true, \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON33_test.json b/tests/assets/nu_json/failJSON33_test.json new file mode 100644 index 000000000..ca5eb19dc --- /dev/null +++ b/tests/assets/nu_json/failJSON33_test.json @@ -0,0 +1 @@ +["mismatch"} \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON34_test.json b/tests/assets/nu_json/failJSON34_test.json new file mode 100644 index 000000000..921436427 --- /dev/null +++ b/tests/assets/nu_json/failJSON34_test.json @@ -0,0 +1,2 @@ +A quoteless string is OK, +but two must be contained in an array. diff --git a/tests/assets/nu_json/failKey1_test.hjson b/tests/assets/nu_json/failKey1_test.hjson new file mode 100644 index 000000000..0026d2a1a --- /dev/null +++ b/tests/assets/nu_json/failKey1_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid name + wrong name: 0 +} diff --git a/tests/assets/nu_json/failKey2_test.hjson b/tests/assets/nu_json/failKey2_test.hjson new file mode 100644 index 000000000..4b5771a7d --- /dev/null +++ b/tests/assets/nu_json/failKey2_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid name + {name: 0 +} diff --git a/tests/assets/nu_json/failKey3_test.hjson b/tests/assets/nu_json/failKey3_test.hjson new file mode 100644 index 000000000..3443a87f4 --- /dev/null +++ b/tests/assets/nu_json/failKey3_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid name + key,name: 0 +} diff --git a/tests/assets/nu_json/failKey4_test.hjson b/tests/assets/nu_json/failKey4_test.hjson new file mode 100644 index 000000000..a7e9ce2ae --- /dev/null +++ b/tests/assets/nu_json/failKey4_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid name + : 0 +} diff --git a/tests/assets/nu_json/failMLStr1_test.hjson b/tests/assets/nu_json/failMLStr1_test.hjson new file mode 100644 index 000000000..ca6410720 --- /dev/null +++ b/tests/assets/nu_json/failMLStr1_test.hjson @@ -0,0 +1,3 @@ +{ + # invalid multiline string + ml: ''' diff --git a/tests/assets/nu_json/failObj1_test.hjson b/tests/assets/nu_json/failObj1_test.hjson new file mode 100644 index 000000000..ac7b761fb --- /dev/null +++ b/tests/assets/nu_json/failObj1_test.hjson @@ -0,0 +1,6 @@ +{ + # invalid obj + noDelimiter + { + } +} diff --git a/tests/assets/nu_json/failObj2_test.hjson b/tests/assets/nu_json/failObj2_test.hjson new file mode 100644 index 000000000..cdad47eef --- /dev/null +++ b/tests/assets/nu_json/failObj2_test.hjson @@ -0,0 +1,6 @@ +{ + # invalid obj + noEnd + { + +} diff --git a/tests/assets/nu_json/failObj3_test.hjson b/tests/assets/nu_json/failObj3_test.hjson new file mode 100644 index 000000000..f1176842d --- /dev/null +++ b/tests/assets/nu_json/failObj3_test.hjson @@ -0,0 +1,7 @@ +{ + # missing key + + [ + test + ] +} diff --git a/tests/assets/nu_json/failStr1a_test.hjson b/tests/assets/nu_json/failStr1a_test.hjson new file mode 100644 index 000000000..91b930ca4 --- /dev/null +++ b/tests/assets/nu_json/failStr1a_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: ] +} diff --git a/tests/assets/nu_json/failStr1b_test.hjson b/tests/assets/nu_json/failStr1b_test.hjson new file mode 100644 index 000000000..91da1a8de --- /dev/null +++ b/tests/assets/nu_json/failStr1b_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: ]x +} diff --git a/tests/assets/nu_json/failStr1c_test.hjson b/tests/assets/nu_json/failStr1c_test.hjson new file mode 100644 index 000000000..20a73079f --- /dev/null +++ b/tests/assets/nu_json/failStr1c_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + ] +] diff --git a/tests/assets/nu_json/failStr1d_test.hjson b/tests/assets/nu_json/failStr1d_test.hjson new file mode 100644 index 000000000..555f88a91 --- /dev/null +++ b/tests/assets/nu_json/failStr1d_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + ]x +] diff --git a/tests/assets/nu_json/failStr2a_test.hjson b/tests/assets/nu_json/failStr2a_test.hjson new file mode 100644 index 000000000..5a8b28feb --- /dev/null +++ b/tests/assets/nu_json/failStr2a_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: } +} diff --git a/tests/assets/nu_json/failStr2b_test.hjson b/tests/assets/nu_json/failStr2b_test.hjson new file mode 100644 index 000000000..a7fc00de7 --- /dev/null +++ b/tests/assets/nu_json/failStr2b_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: }x +} diff --git a/tests/assets/nu_json/failStr2c_test.hjson b/tests/assets/nu_json/failStr2c_test.hjson new file mode 100644 index 000000000..1fe46067b --- /dev/null +++ b/tests/assets/nu_json/failStr2c_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + } +] diff --git a/tests/assets/nu_json/failStr2d_test.hjson b/tests/assets/nu_json/failStr2d_test.hjson new file mode 100644 index 000000000..ea8c99adc --- /dev/null +++ b/tests/assets/nu_json/failStr2d_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + }x +] diff --git a/tests/assets/nu_json/failStr3a_test.hjson b/tests/assets/nu_json/failStr3a_test.hjson new file mode 100644 index 000000000..ec5d0ad20 --- /dev/null +++ b/tests/assets/nu_json/failStr3a_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: { +} diff --git a/tests/assets/nu_json/failStr3b_test.hjson b/tests/assets/nu_json/failStr3b_test.hjson new file mode 100644 index 000000000..2d0fff1fb --- /dev/null +++ b/tests/assets/nu_json/failStr3b_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: {x +} diff --git a/tests/assets/nu_json/failStr3c_test.hjson b/tests/assets/nu_json/failStr3c_test.hjson new file mode 100644 index 000000000..2872a4d95 --- /dev/null +++ b/tests/assets/nu_json/failStr3c_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + { +] diff --git a/tests/assets/nu_json/failStr3d_test.hjson b/tests/assets/nu_json/failStr3d_test.hjson new file mode 100644 index 000000000..949502ffc --- /dev/null +++ b/tests/assets/nu_json/failStr3d_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + {x +] diff --git a/tests/assets/nu_json/failStr4a_test.hjson b/tests/assets/nu_json/failStr4a_test.hjson new file mode 100644 index 000000000..941f35bcd --- /dev/null +++ b/tests/assets/nu_json/failStr4a_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: [ +} diff --git a/tests/assets/nu_json/failStr4b_test.hjson b/tests/assets/nu_json/failStr4b_test.hjson new file mode 100644 index 000000000..b7bb23628 --- /dev/null +++ b/tests/assets/nu_json/failStr4b_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: [x +} diff --git a/tests/assets/nu_json/failStr4c_test.hjson b/tests/assets/nu_json/failStr4c_test.hjson new file mode 100644 index 000000000..ee927a475 --- /dev/null +++ b/tests/assets/nu_json/failStr4c_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + [ +] diff --git a/tests/assets/nu_json/failStr4d_test.hjson b/tests/assets/nu_json/failStr4d_test.hjson new file mode 100644 index 000000000..db50a529d --- /dev/null +++ b/tests/assets/nu_json/failStr4d_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + [x +] diff --git a/tests/assets/nu_json/failStr5a_test.hjson b/tests/assets/nu_json/failStr5a_test.hjson new file mode 100644 index 000000000..4093f7a58 --- /dev/null +++ b/tests/assets/nu_json/failStr5a_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: : +} diff --git a/tests/assets/nu_json/failStr5b_test.hjson b/tests/assets/nu_json/failStr5b_test.hjson new file mode 100644 index 000000000..eda96192b --- /dev/null +++ b/tests/assets/nu_json/failStr5b_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: :x +} diff --git a/tests/assets/nu_json/failStr5c_test.hjson b/tests/assets/nu_json/failStr5c_test.hjson new file mode 100644 index 000000000..63280735b --- /dev/null +++ b/tests/assets/nu_json/failStr5c_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + : +] diff --git a/tests/assets/nu_json/failStr5d_test.hjson b/tests/assets/nu_json/failStr5d_test.hjson new file mode 100644 index 000000000..bfaef8e7d --- /dev/null +++ b/tests/assets/nu_json/failStr5d_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + :x +] diff --git a/tests/assets/nu_json/failStr6a_test.hjson b/tests/assets/nu_json/failStr6a_test.hjson new file mode 100644 index 000000000..522333773 --- /dev/null +++ b/tests/assets/nu_json/failStr6a_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: , +} diff --git a/tests/assets/nu_json/failStr6b_test.hjson b/tests/assets/nu_json/failStr6b_test.hjson new file mode 100644 index 000000000..90ad67bf5 --- /dev/null +++ b/tests/assets/nu_json/failStr6b_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: ,x +} diff --git a/tests/assets/nu_json/failStr6c_test.hjson b/tests/assets/nu_json/failStr6c_test.hjson new file mode 100644 index 000000000..81d2af4c9 --- /dev/null +++ b/tests/assets/nu_json/failStr6c_test.hjson @@ -0,0 +1,6 @@ +[ + # invalid quoteless string + # note that if there were a preceding value the comma would + # be allowed/ignored as a separator/trailing comma + , +] diff --git a/tests/assets/nu_json/failStr6d_test.hjson b/tests/assets/nu_json/failStr6d_test.hjson new file mode 100644 index 000000000..c1477293b --- /dev/null +++ b/tests/assets/nu_json/failStr6d_test.hjson @@ -0,0 +1,6 @@ +[ + # invalid quoteless string + # note that if there were a preceding value the comma would + # be allowed/ignored as a separator/trailing comma + ,x +] diff --git a/tests/assets/nu_json/kan_result.hjson b/tests/assets/nu_json/kan_result.hjson new file mode 100644 index 000000000..d2b839add --- /dev/null +++ b/tests/assets/nu_json/kan_result.hjson @@ -0,0 +1,48 @@ +{ + numbers: + [ + 0 + 0 + 0 + 42 + 42.1 + -5 + -5.1 + 1701 + -1701 + 12.345 + -12.345 + ] + native: + [ + true + true + false + false + null + null + ] + strings: + [ + x 0 + .0 + 00 + 01 + 0 0 0 + 42 x + 42.1 asdf + 1.2.3 + -5 0 - + -5.1 -- + 17.01e2 + + -17.01e2 : + 12345e-3 @ + -12345e-3 $ + true true + x true + false false + x false + null null + x null + ] +} \ No newline at end of file diff --git a/tests/assets/nu_json/kan_result.json b/tests/assets/nu_json/kan_result.json new file mode 100644 index 000000000..babb9d4e0 --- /dev/null +++ b/tests/assets/nu_json/kan_result.json @@ -0,0 +1,45 @@ +{ + "numbers": [ + 0, + 0, + 0, + 42, + 42.1, + -5, + -5.1, + 1701, + -1701, + 12.345, + -12.345 + ], + "native": [ + true, + true, + false, + false, + null, + null + ], + "strings": [ + "x 0", + ".0", + "00", + "01", + "0 0 0", + "42 x", + "42.1 asdf", + "1.2.3", + "-5 0 -", + "-5.1 --", + "17.01e2 +", + "-17.01e2 :", + "12345e-3 @", + "-12345e-3 $", + "true true", + "x true", + "false false", + "x false", + "null null", + "x null" + ] +} \ No newline at end of file diff --git a/tests/assets/nu_json/kan_test.hjson b/tests/assets/nu_json/kan_test.hjson new file mode 100644 index 000000000..1e6906a96 --- /dev/null +++ b/tests/assets/nu_json/kan_test.hjson @@ -0,0 +1,49 @@ +{ + # the comma forces a whitespace check + numbers: + [ + 0 + 0 , + -0 + 42 , + 42.1 , + -5 + -5.1 + 17.01e2 + -17.01e2 + 12345e-3 , + -12345e-3 , + ] + native: + [ + true , + true + false , + false + null , + null + ] + strings: + [ + x 0 + .0 + 00 + 01 + 0 0 0 + 42 x + 42.1 asdf + 1.2.3 + -5 0 - + -5.1 -- + 17.01e2 + + -17.01e2 : + 12345e-3 @ + -12345e-3 $ + true true + x true + false false + x false + null null + x null + ] +} diff --git a/tests/assets/nu_json/keys_result.hjson b/tests/assets/nu_json/keys_result.hjson new file mode 100644 index 000000000..876e6c346 --- /dev/null +++ b/tests/assets/nu_json/keys_result.hjson @@ -0,0 +1,34 @@ +{ + unquoted_key: test + _unquoted: test + test-key: test + -test: test + .key: test + trailing: test + trailing2: test + "#c1": test + "foo#bar": test + "//bar": test + "foo//bar": test + "/*foo*/": test + "foo/*foo*/bar": test + "/*": test + "foo/*bar": test + "\"": test + "foo\"bar": test + "'''": test + "foo'''bar": test + ":": test + "foo:bar": test + "{": test + "foo{bar": test + "}": test + "foo}bar": test + "[": test + "foo[bar": test + "]": test + "foo]bar": test + nl1: test + nl2: test + nl3: test +} \ No newline at end of file diff --git a/tests/assets/nu_json/keys_result.json b/tests/assets/nu_json/keys_result.json new file mode 100644 index 000000000..81fa480b2 --- /dev/null +++ b/tests/assets/nu_json/keys_result.json @@ -0,0 +1,34 @@ +{ + "unquoted_key": "test", + "_unquoted": "test", + "test-key": "test", + "-test": "test", + ".key": "test", + "trailing": "test", + "trailing2": "test", + "#c1": "test", + "foo#bar": "test", + "//bar": "test", + "foo//bar": "test", + "/*foo*/": "test", + "foo/*foo*/bar": "test", + "/*": "test", + "foo/*bar": "test", + "\"": "test", + "foo\"bar": "test", + "'''": "test", + "foo'''bar": "test", + ":": "test", + "foo:bar": "test", + "{": "test", + "foo{bar": "test", + "}": "test", + "foo}bar": "test", + "[": "test", + "foo[bar": "test", + "]": "test", + "foo]bar": "test", + "nl1": "test", + "nl2": "test", + "nl3": "test" +} \ No newline at end of file diff --git a/tests/assets/nu_json/keys_test.hjson b/tests/assets/nu_json/keys_test.hjson new file mode 100644 index 000000000..38f560381 --- /dev/null +++ b/tests/assets/nu_json/keys_test.hjson @@ -0,0 +1,48 @@ +{ + # unquoted keys + unquoted_key: test + _unquoted: test + test-key: test + -test: test + .key: test + # trailing spaces in key names are ignored + trailing : test + trailing2 : test + # comment char in key name + "#c1": test + "foo#bar": test + "//bar": test + "foo//bar": test + "/*foo*/": test + "foo/*foo*/bar": test + "/*": test + "foo/*bar": test + # quotes in key name + "\"": test + "foo\"bar": test + "'''": test + "foo'''bar": test + # control char in key name + ":": test + "foo:bar": test + "{": test + "foo{bar": test + "}": test + "foo}bar": test + "[": test + "foo[bar": test + "]": test + "foo]bar": test + # newline + nl1: + test + nl2 + : + test + + nl3 + + : + + test +} diff --git a/tests/assets/nu_json/oa_result.hjson b/tests/assets/nu_json/oa_result.hjson new file mode 100644 index 000000000..db42ac901 --- /dev/null +++ b/tests/assets/nu_json/oa_result.hjson @@ -0,0 +1,13 @@ +[ + a + {} + {} + [] + [] + { + b: 1 + c: [] + d: {} + } + [] +] \ No newline at end of file diff --git a/tests/assets/nu_json/oa_result.json b/tests/assets/nu_json/oa_result.json new file mode 100644 index 000000000..f0955abeb --- /dev/null +++ b/tests/assets/nu_json/oa_result.json @@ -0,0 +1,13 @@ +[ + "a", + {}, + {}, + [], + [], + { + "b": 1, + "c": [], + "d": {} + }, + [] +] \ No newline at end of file diff --git a/tests/assets/nu_json/oa_test.hjson b/tests/assets/nu_json/oa_test.hjson new file mode 100644 index 000000000..35bcdb452 --- /dev/null +++ b/tests/assets/nu_json/oa_test.hjson @@ -0,0 +1,13 @@ +[ + a + {} + {} + [] + [] + { + b: 1 + c: [] + d: {} + } + [] +] diff --git a/tests/assets/nu_json/pass1_result.hjson b/tests/assets/nu_json/pass1_result.hjson new file mode 100644 index 000000000..2a3f4c97f --- /dev/null +++ b/tests/assets/nu_json/pass1_result.hjson @@ -0,0 +1,78 @@ +[ + JSON Test Pattern pass1 + { + "object with 1 member": + [ + array with 1 element + ] + } + {} + [] + -42 + true + false + null + { + integer: 1234567890 + real: -9876.54321 + e: 1.23456789e-13 + E: 1.23456789e+34 + -: 2.3456789012e+76 + zero: 0 + one: 1 + space: " " + quote: '''"''' + backslash: \ + controls: "\b\f\n\r\t" + slash: / & / + alpha: abcdefghijklmnopqrstuvwyz + ALPHA: ABCDEFGHIJKLMNOPQRSTUVWYZ + digit: 0123456789 + 0123456789: digit + special: `1~!@#$%^&*()_+-={':[,]}|;.? + hex: ģ䕧覫췯ꯍ + true: true + false: false + null: null + array: [] + object: {} + address: 50 St. James Street + url: http://www.JSON.org/ + comment: "// /* */": " " + " s p a c e d ": + [ + 1 + 2 + 3 + 4 + 5 + 6 + 7 + ] + compact: + [ + 1 + 2 + 3 + 4 + 5 + 6 + 7 + ] + jsontext: '''{"object with 1 member":["array with 1 element"]}''' + quotes: " " %22 0x22 034 " + "/\\\"쫾몾ꮘﳞ볚\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?": A key can be any string + } + 0.5 + 98.6 + 99.44 + 1066 + 10 + 1 + 0.1 + 1 + 2 + 2 + rosebud +] \ No newline at end of file diff --git a/tests/assets/nu_json/pass1_result.json b/tests/assets/nu_json/pass1_result.json new file mode 100644 index 000000000..69b354d05 --- /dev/null +++ b/tests/assets/nu_json/pass1_result.json @@ -0,0 +1,75 @@ +[ + "JSON Test Pattern pass1", + { + "object with 1 member": [ + "array with 1 element" + ] + }, + {}, + [], + -42, + true, + false, + null, + { + "integer": 1234567890, + "real": -9876.54321, + "e": 1.23456789e-13, + "E": 1.23456789e+34, + "-": 2.3456789012e+76, + "zero": 0, + "one": 1, + "space": " ", + "quote": "\"", + "backslash": "\\", + "controls": "\b\f\n\r\t", + "slash": "/ & /", + "alpha": "abcdefghijklmnopqrstuvwyz", + "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ", + "digit": "0123456789", + "0123456789": "digit", + "special": "`1~!@#$%^&*()_+-={':[,]}|;.?", + "hex": "ģ䕧覫췯ꯍ", + "true": true, + "false": false, + "null": null, + "array": [], + "object": {}, + "address": "50 St. James Street", + "url": "http://www.JSON.org/", + "comment": "// /* */": " ", + " s p a c e d ": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7 + ], + "compact": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7 + ], + "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", + "quotes": "" \" %22 0x22 034 "", + "/\\\"쫾몾ꮘﳞ볚\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?": "A key can be any string" + }, + 0.5, + 98.6, + 99.44, + 1066, + 10, + 1, + 0.1, + 1, + 2, + 2, + "rosebud" +] \ No newline at end of file diff --git a/tests/assets/nu_json/pass1_test.json b/tests/assets/nu_json/pass1_test.json new file mode 100644 index 000000000..61cfd90c9 --- /dev/null +++ b/tests/assets/nu_json/pass1_test.json @@ -0,0 +1,58 @@ +[ + "JSON Test Pattern pass1", + {"object with 1 member":["array with 1 element"]}, + {}, + [], + -42, + true, + false, + null, + { + "integer": 1234567890, + "real": -9876.543210, + "e": 0.123456789e-12, + "E": 1.234567890E+34, + "-": 23456789012E66, + "zero": 0, + "one": 1, + "space": " ", + "quote": "\"", + "backslash": "\\", + "controls": "\b\f\n\r\t", + "slash": "/ & \/", + "alpha": "abcdefghijklmnopqrstuvwyz", + "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ", + "digit": "0123456789", + "0123456789": "digit", + "special": "`1~!@#$%^&*()_+-={':[,]}|;.?", + "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A", + "true": true, + "false": false, + "null": null, + "array":[ ], + "object":{ }, + "address": "50 St. James Street", + "url": "http://www.JSON.org/", + "comment": "// /* */": " ", + " s p a c e d " :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7], + "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", + "quotes": "" \u0022 %22 0x22 034 "", + "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?" +: "A key can be any string" + }, + 0.5 ,98.6 +, +99.44 +, + +1066, +1e1, +0.1e1, +1e-1, +1e00,2e+00,2e-00 +,"rosebud"] \ No newline at end of file diff --git a/tests/assets/nu_json/pass2_result.hjson b/tests/assets/nu_json/pass2_result.hjson new file mode 100644 index 000000000..5a9fd5e82 --- /dev/null +++ b/tests/assets/nu_json/pass2_result.hjson @@ -0,0 +1,39 @@ +[ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + Not too deep + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] +] \ No newline at end of file diff --git a/tests/assets/nu_json/pass2_result.json b/tests/assets/nu_json/pass2_result.json new file mode 100644 index 000000000..2a71f5850 --- /dev/null +++ b/tests/assets/nu_json/pass2_result.json @@ -0,0 +1,39 @@ +[ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + "Not too deep" + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] +] \ No newline at end of file diff --git a/tests/assets/nu_json/pass2_test.json b/tests/assets/nu_json/pass2_test.json new file mode 100644 index 000000000..d3c63c7ad --- /dev/null +++ b/tests/assets/nu_json/pass2_test.json @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]] \ No newline at end of file diff --git a/tests/assets/nu_json/pass3_result.hjson b/tests/assets/nu_json/pass3_result.hjson new file mode 100644 index 000000000..6db3fb61d --- /dev/null +++ b/tests/assets/nu_json/pass3_result.hjson @@ -0,0 +1,7 @@ +{ + "JSON Test Pattern pass3": + { + "The outermost value": must be an object or array. + "In this test": It is an object. + } +} \ No newline at end of file diff --git a/tests/assets/nu_json/pass3_result.json b/tests/assets/nu_json/pass3_result.json new file mode 100644 index 000000000..d98cd2f88 --- /dev/null +++ b/tests/assets/nu_json/pass3_result.json @@ -0,0 +1,6 @@ +{ + "JSON Test Pattern pass3": { + "The outermost value": "must be an object or array.", + "In this test": "It is an object." + } +} \ No newline at end of file diff --git a/tests/assets/nu_json/pass3_test.json b/tests/assets/nu_json/pass3_test.json new file mode 100644 index 000000000..4528d51f1 --- /dev/null +++ b/tests/assets/nu_json/pass3_test.json @@ -0,0 +1,6 @@ +{ + "JSON Test Pattern pass3": { + "The outermost value": "must be an object or array.", + "In this test": "It is an object." + } +} diff --git a/tests/assets/nu_json/pass4_result.hjson b/tests/assets/nu_json/pass4_result.hjson new file mode 100644 index 000000000..9a037142a --- /dev/null +++ b/tests/assets/nu_json/pass4_result.hjson @@ -0,0 +1 @@ +10 \ No newline at end of file diff --git a/tests/assets/nu_json/pass4_result.json b/tests/assets/nu_json/pass4_result.json new file mode 100644 index 000000000..9a037142a --- /dev/null +++ b/tests/assets/nu_json/pass4_result.json @@ -0,0 +1 @@ +10 \ No newline at end of file diff --git a/tests/assets/nu_json/pass4_test.json b/tests/assets/nu_json/pass4_test.json new file mode 100644 index 000000000..069c2ae6b --- /dev/null +++ b/tests/assets/nu_json/pass4_test.json @@ -0,0 +1,2 @@ + +10 diff --git a/tests/assets/nu_json/passSingle_result.hjson b/tests/assets/nu_json/passSingle_result.hjson new file mode 100644 index 000000000..e580fce15 --- /dev/null +++ b/tests/assets/nu_json/passSingle_result.hjson @@ -0,0 +1 @@ +allow quoteless strings \ No newline at end of file diff --git a/tests/assets/nu_json/passSingle_result.json b/tests/assets/nu_json/passSingle_result.json new file mode 100644 index 000000000..1829d36f8 --- /dev/null +++ b/tests/assets/nu_json/passSingle_result.json @@ -0,0 +1 @@ +"allow quoteless strings" \ No newline at end of file diff --git a/tests/assets/nu_json/passSingle_test.hjson b/tests/assets/nu_json/passSingle_test.hjson new file mode 100644 index 000000000..e580fce15 --- /dev/null +++ b/tests/assets/nu_json/passSingle_test.hjson @@ -0,0 +1 @@ +allow quoteless strings \ No newline at end of file diff --git a/tests/assets/nu_json/root_result.hjson b/tests/assets/nu_json/root_result.hjson new file mode 100644 index 000000000..736372f62 --- /dev/null +++ b/tests/assets/nu_json/root_result.hjson @@ -0,0 +1,7 @@ +{ + database: + { + host: 127.0.0.1 + port: 555 + } +} \ No newline at end of file diff --git a/tests/assets/nu_json/root_result.json b/tests/assets/nu_json/root_result.json new file mode 100644 index 000000000..21b01cd00 --- /dev/null +++ b/tests/assets/nu_json/root_result.json @@ -0,0 +1,6 @@ +{ + "database": { + "host": "127.0.0.1", + "port": 555 + } +} \ No newline at end of file diff --git a/tests/assets/nu_json/root_test.hjson b/tests/assets/nu_json/root_test.hjson new file mode 100644 index 000000000..c0acd16ee --- /dev/null +++ b/tests/assets/nu_json/root_test.hjson @@ -0,0 +1,6 @@ +// a object with the root braces omitted +database: +{ + host: 127.0.0.1 + port: 555 +} diff --git a/tests/assets/nu_json/stringify1_result.hjson b/tests/assets/nu_json/stringify1_result.hjson new file mode 100644 index 000000000..77b2eddc1 --- /dev/null +++ b/tests/assets/nu_json/stringify1_result.hjson @@ -0,0 +1,49 @@ +{ + quotes: + { + num1: "1,2" + num2: "-1.1 ," + num3: "1e10 ,2" + num4: "-1e-10," + kw1: "true," + kw2: "false ," + kw3: "null,123" + close1: "1}" + close1b: "1 }" + close2: "1]" + close2b: "1 ]" + close3: "1," + close3b: "1 ," + comment1: "1#str" + comment2: "1//str" + comment3: "1/*str*/" + punc1: "{" + punc1b: "{foo" + punc2: "}" + punc2b: "}foo" + punc3: "[" + punc3b: "[foo" + punc4: "]" + punc4b: "]foo" + punc5: "," + punc5b: ",foo" + punc6: ":" + punc6b: ":foo" + } + noquotes: + { + num0: .1,2 + num1: 1.1.1,2 + num2: -.1, + num3: 1e10e,2 + num4: -1e--10, + kw1: true1, + kw2: false0, + kw3: null0, + close1: a} + close2: a] + comment1: a#str + comment2: a//str + comment3: a/*str*/ + } +} \ No newline at end of file diff --git a/tests/assets/nu_json/stringify1_result.json b/tests/assets/nu_json/stringify1_result.json new file mode 100644 index 000000000..12514f878 --- /dev/null +++ b/tests/assets/nu_json/stringify1_result.json @@ -0,0 +1,47 @@ +{ + "quotes": { + "num1": "1,2", + "num2": "-1.1 ,", + "num3": "1e10 ,2", + "num4": "-1e-10,", + "kw1": "true,", + "kw2": "false ,", + "kw3": "null,123", + "close1": "1}", + "close1b": "1 }", + "close2": "1]", + "close2b": "1 ]", + "close3": "1,", + "close3b": "1 ,", + "comment1": "1#str", + "comment2": "1//str", + "comment3": "1/*str*/", + "punc1": "{", + "punc1b": "{foo", + "punc2": "}", + "punc2b": "}foo", + "punc3": "[", + "punc3b": "[foo", + "punc4": "]", + "punc4b": "]foo", + "punc5": ",", + "punc5b": ",foo", + "punc6": ":", + "punc6b": ":foo" + }, + "noquotes": { + "num0": ".1,2", + "num1": "1.1.1,2", + "num2": "-.1,", + "num3": "1e10e,2", + "num4": "-1e--10,", + "kw1": "true1,", + "kw2": "false0,", + "kw3": "null0,", + "close1": "a}", + "close2": "a]", + "comment1": "a#str", + "comment2": "a//str", + "comment3": "a/*str*/" + } +} \ No newline at end of file diff --git a/tests/assets/nu_json/stringify1_test.hjson b/tests/assets/nu_json/stringify1_test.hjson new file mode 100644 index 000000000..b353bf19c --- /dev/null +++ b/tests/assets/nu_json/stringify1_test.hjson @@ -0,0 +1,50 @@ +// test if stringify produces correct output +{ + quotes: + { + num1: "1,2" + num2: "-1.1 ," + num3: "1e10 ,2" + num4: "-1e-10," + kw1: "true," + kw2: "false ," + kw3: "null,123" + close1: "1}" + close1b: "1 }" + close2: "1]" + close2b: "1 ]" + close3: "1," + close3b: "1 ," + comment1: "1#str" + comment2: "1//str" + comment3: "1/*str*/" + punc1: "{" + punc1b: "{foo" + punc2: "}" + punc2b: "}foo" + punc3: "[" + punc3b: "[foo" + punc4: "]" + punc4b: "]foo" + punc5: "," + punc5b: ",foo" + punc6: ":" + punc6b: ":foo" + } + noquotes: + { + num0: ".1,2" + num1: "1.1.1,2" + num2: "-.1," + num3: "1e10e,2" + num4: "-1e--10," + kw1: "true1," + kw2: "false0," + kw3: "null0," + close1: "a}" + close2: "a]" + comment1: "a#str" + comment2: "a//str" + comment3: "a/*str*/" + } +} diff --git a/tests/assets/nu_json/strings_result.hjson b/tests/assets/nu_json/strings_result.hjson new file mode 100644 index 000000000..6ef06f874 --- /dev/null +++ b/tests/assets/nu_json/strings_result.hjson @@ -0,0 +1,75 @@ +{ + text1: This is a valid string value. + text2: a \ is just a \ + text3: "You need quotes\tfor escapes" + text4a: " untrimmed " + text4b: " untrimmed" + text4c: "untrimmed " + multiline1: + ''' + first line + indented line + last line + ''' + multiline2: + ''' + first line + indented line + last line + ''' + multiline3: + ''' + first line + indented line + last line + + ''' + foo1a: asdf\"'a\s\w + foo1b: asdf\"'a\s\w + foo1c: asdf\"'a\s\w + foo2a: '''"asdf"''' + foo2b: '''"asdf"''' + foo3a: asdf''' + foo3b: "'''asdf" + foo4a: "asdf'''\nasdf" + foo4b: "asdf\n'''asdf" + arr: + [ + one + two + three + four + ] + not: + { + number: 5 + negative: -4.2 + yes: true + no: false + null: null + array: + [ + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + -1 + 0.5 + ] + } + special: + { + true: "true" + false: "false" + null: "null" + one: "1" + two: "2" + minus: "-3" + } +} \ No newline at end of file diff --git a/tests/assets/nu_json/strings_result.json b/tests/assets/nu_json/strings_result.json new file mode 100644 index 000000000..16321ba7a --- /dev/null +++ b/tests/assets/nu_json/strings_result.json @@ -0,0 +1,55 @@ +{ + "text1": "This is a valid string value.", + "text2": "a \\ is just a \\", + "text3": "You need quotes\tfor escapes", + "text4a": " untrimmed ", + "text4b": " untrimmed", + "text4c": "untrimmed ", + "multiline1": "first line\n indented line\nlast line", + "multiline2": "first line\n indented line\nlast line", + "multiline3": "first line\n indented line\nlast line\n", + "foo1a": "asdf\\\"'a\\s\\w", + "foo1b": "asdf\\\"'a\\s\\w", + "foo1c": "asdf\\\"'a\\s\\w", + "foo2a": "\"asdf\"", + "foo2b": "\"asdf\"", + "foo3a": "asdf'''", + "foo3b": "'''asdf", + "foo4a": "asdf'''\nasdf", + "foo4b": "asdf\n'''asdf", + "arr": [ + "one", + "two", + "three", + "four" + ], + "not": { + "number": 5, + "negative": -4.2, + "yes": true, + "no": false, + "null": null, + "array": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 0, + -1, + 0.5 + ] + }, + "special": { + "true": "true", + "false": "false", + "null": "null", + "one": "1", + "two": "2", + "minus": "-3" + } +} \ No newline at end of file diff --git a/tests/assets/nu_json/strings_test.hjson b/tests/assets/nu_json/strings_test.hjson new file mode 100644 index 000000000..616895209 --- /dev/null +++ b/tests/assets/nu_json/strings_test.hjson @@ -0,0 +1,80 @@ +{ + # simple + + text1: This is a valid string value. + text2:a \ is just a \ + + text3: "You need quotes\tfor escapes" + + text4a: " untrimmed " + text4b: " untrimmed" + text4c: "untrimmed " + + # multiline string + + multiline1: + ''' + first line + indented line + last line + ''' + + multiline2: + '''first line + indented line + last line''' + + multiline3: + ''' + first line + indented line + last line + + ''' # trailing lf + + # escapes/no escape + + foo1a: asdf\"'a\s\w + foo1b: '''asdf\"'a\s\w''' + foo1c: "asdf\\\"'a\\s\\w" + + foo2a: "\"asdf\"" + foo2b: '''"asdf"''' + + foo3a: "asdf'''" + foo3b: "'''asdf" + + foo4a: "asdf'''\nasdf" + foo4b: "asdf\n'''asdf" + + # in arrays + arr: + [ + one + two + "three" + '''four''' + ] + + # not strings + not: + { + number: 5 + negative: -4.2 + yes: true + no: false + null: null + array: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, -1, 0.5 ] + } + + # special quoted + special: + { + true: "true" + false: "false" + null: "null" + one: "1" + two: "2" + minus: "-3" + } +} diff --git a/tests/assets/nu_json/testlist.txt b/tests/assets/nu_json/testlist.txt new file mode 100644 index 000000000..debb52c83 --- /dev/null +++ b/tests/assets/nu_json/testlist.txt @@ -0,0 +1,75 @@ +charset_test.hjson +comments_test.hjson +empty_test.hjson +failCharset1_test.hjson +failJSON02_test.json +failJSON05_test.json +failJSON06_test.json +failJSON07_test.json +failJSON08_test.json +failJSON10_test.json +failJSON11_test.json +failJSON12_test.json +failJSON13_test.json +failJSON14_test.json +failJSON15_test.json +failJSON16_test.json +failJSON17_test.json +failJSON19_test.json +failJSON20_test.json +failJSON21_test.json +failJSON22_test.json +failJSON23_test.json +failJSON24_test.json +failJSON26_test.json +failJSON28_test.json +failJSON29_test.json +failJSON30_test.json +failJSON31_test.json +failJSON32_test.json +failJSON33_test.json +failJSON34_test.json +failKey1_test.hjson +failKey2_test.hjson +failKey3_test.hjson +failKey4_test.hjson +failMLStr1_test.hjson +failObj1_test.hjson +failObj2_test.hjson +failObj3_test.hjson +failStr1a_test.hjson +failStr1b_test.hjson +failStr1c_test.hjson +failStr1d_test.hjson +failStr2a_test.hjson +failStr2b_test.hjson +failStr2c_test.hjson +failStr2d_test.hjson +failStr3a_test.hjson +failStr3b_test.hjson +failStr3c_test.hjson +failStr3d_test.hjson +failStr4a_test.hjson +failStr4b_test.hjson +failStr4c_test.hjson +failStr4d_test.hjson +failStr5a_test.hjson +failStr5b_test.hjson +failStr5c_test.hjson +failStr5d_test.hjson +failStr6a_test.hjson +failStr6b_test.hjson +failStr6c_test.hjson +failStr6d_test.hjson +kan_test.hjson +keys_test.hjson +oa_test.hjson +pass1_test.json +pass2_test.json +pass3_test.json +pass4_test.json +passSingle_test.hjson +root_test.hjson +stringify1_test.hjson +strings_test.hjson +trail_test.hjson \ No newline at end of file diff --git a/tests/assets/nu_json/trail_result.hjson b/tests/assets/nu_json/trail_result.hjson new file mode 100644 index 000000000..57ffc716b --- /dev/null +++ b/tests/assets/nu_json/trail_result.hjson @@ -0,0 +1,3 @@ +{ + foo: 0 -- this string starts at 0 and ends at 1, preceding and trailing whitespace is ignored -- 1 +} \ No newline at end of file diff --git a/tests/assets/nu_json/trail_result.json b/tests/assets/nu_json/trail_result.json new file mode 100644 index 000000000..451c8ceb9 --- /dev/null +++ b/tests/assets/nu_json/trail_result.json @@ -0,0 +1,3 @@ +{ + "foo": "0 -- this string starts at 0 and ends at 1, preceding and trailing whitespace is ignored -- 1" +} \ No newline at end of file diff --git a/tests/assets/nu_json/trail_test.hjson b/tests/assets/nu_json/trail_test.hjson new file mode 100644 index 000000000..62d98e98a --- /dev/null +++ b/tests/assets/nu_json/trail_test.hjson @@ -0,0 +1,2 @@ +// the following line contains trailing whitespace: +foo: 0 -- this string starts at 0 and ends at 1, preceding and trailing whitespace is ignored -- 1