From a53b5767bcef1af40aa843a257fa30b8901ec8ee Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sat, 26 Oct 2019 00:43:30 +0200 Subject: [PATCH 001/178] Rename, remove no_std --- .cargo/config | 2 ++ Cargo.toml | 27 ++++++++++----------------- src/lib.rs | 3 +-- 3 files changed, 13 insertions(+), 19 deletions(-) create mode 100644 .cargo/config diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 000000000..f9f89a9a4 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,2 @@ +[alias] +wasm = "build --release --target wasm32-unknown-unknown" \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 2aa8992a2..28a235d0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,25 +1,18 @@ [package] -authors = ["Jorge Aparicio "] -categories = ["no-std"] -description = "serde-json for no_std programs" -documentation = "https://docs.rs/serde-json-core" +authors = ["Jorge Aparicio ", "Ethan Frey Date: Sat, 26 Oct 2019 00:55:42 +0200 Subject: [PATCH 002/178] Make errors std::Error, std::Vec not heapless --- src/de/mod.rs | 95 ++++++++++++++++++++++++---------------------- src/ser/mod.rs | 76 ++++++++++++++++--------------------- src/ser/seq.rs | 24 +++--------- src/ser/struct_.rs | 21 +++------- 4 files changed, 94 insertions(+), 122 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 4e268bcdf..cc3d5c4f0 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -1,6 +1,6 @@ //! Deserialize JSON data to a Rust data structure -use core::{fmt, str}; +use std::{fmt, error}; use serde::de::{self, Visitor}; @@ -67,13 +67,59 @@ pub enum Error { __Extensible, } -#[cfg(feature = "std")] -impl ::std::error::Error for Error { +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + None + } + fn description(&self) -> &str { - "" + "(use display)" + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Error::EofWhileParsingList => "EOF while parsing a list.", + Error::EofWhileParsingObject => "EOF while parsing an object.", + Error::EofWhileParsingString => "EOF while parsing a string.", + Error::EofWhileParsingValue => "EOF while parsing a JSON value.", + Error::ExpectedColon => "Expected this character to be a `':'`.", + Error::ExpectedListCommaOrEnd => { + "Expected this character to be either a `','` or\ + a \ + `']'`." + } + Error::ExpectedObjectCommaOrEnd => { + "Expected this character to be either a `','` \ + or a \ + `'}'`." + } + Error::ExpectedSomeIdent => { + "Expected to parse either a `true`, `false`, or a \ + `null`." + } + Error::ExpectedSomeValue => "Expected this character to start a JSON value.", + Error::InvalidNumber => "Invalid number.", + Error::InvalidType => "Invalid type", + Error::InvalidUnicodeCodePoint => "Invalid unicode code point.", + Error::KeyMustBeAString => "Object key is not a string.", + Error::TrailingCharacters => { + "JSON has non-whitespace trailing characters after \ + the \ + value." + } + Error::TrailingComma => "JSON has a comma after the last value in an array or map.", + _ => "Invalid JSON", + } + ) } } + pub(crate) struct Deserializer<'b> { slice: &'b [u8], index: usize, @@ -558,47 +604,6 @@ impl de::Error for Error { } } -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match self { - Error::EofWhileParsingList => "EOF while parsing a list.", - Error::EofWhileParsingObject => "EOF while parsing an object.", - Error::EofWhileParsingString => "EOF while parsing a string.", - Error::EofWhileParsingValue => "EOF while parsing a JSON value.", - Error::ExpectedColon => "Expected this character to be a `':'`.", - Error::ExpectedListCommaOrEnd => { - "Expected this character to be either a `','` or\ - a \ - `']'`." - } - Error::ExpectedObjectCommaOrEnd => { - "Expected this character to be either a `','` \ - or a \ - `'}'`." - } - Error::ExpectedSomeIdent => { - "Expected to parse either a `true`, `false`, or a \ - `null`." - } - Error::ExpectedSomeValue => "Expected this character to start a JSON value.", - Error::InvalidNumber => "Invalid number.", - Error::InvalidType => "Invalid type", - Error::InvalidUnicodeCodePoint => "Invalid unicode code point.", - Error::KeyMustBeAString => "Object key is not a string.", - Error::TrailingCharacters => { - "JSON has non-whitespace trailing characters after \ - the \ - value." - } - Error::TrailingComma => "JSON has a comma after the last value in an array or map.", - _ => "Invalid JSON", - } - ) - } -} /// Deserializes an instance of type `T` from bytes of JSON text pub fn from_slice<'a, T>(v: &'a [u8]) -> Result diff --git a/src/ser/mod.rs b/src/ser/mod.rs index f7d06a462..a4933954c 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -1,10 +1,10 @@ //! Serialize a Rust data structure into JSON data -use core::{fmt, mem}; +use std::{fmt, error}; use serde::ser; -use heapless::{String, Vec}; +use std::vec::Vec; use self::seq::SerializeSeq; use self::struct_::SerializeStruct; @@ -36,10 +36,14 @@ impl From for Error { } } -#[cfg(feature = "std")] -impl ::std::error::Error for Error { + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + None + } + fn description(&self) -> &str { - "" + "(use display)" } } @@ -49,17 +53,12 @@ impl fmt::Display for Error { } } -pub(crate) struct Serializer -where - B: heapless::ArrayLength, +pub(crate) struct Serializer { - buf: Vec, + buf: Vec, } -impl Serializer -where - B: heapless::ArrayLength, -{ +impl Serializer { fn new() -> Self { Serializer { buf: Vec::new() } } @@ -123,18 +122,15 @@ macro_rules! serialize_signed { }}; } -impl<'a, B> ser::Serializer for &'a mut Serializer -where - B: heapless::ArrayLength, -{ +impl<'a> ser::Serializer for &'a mut Serializer { type Ok = (); type Error = Error; - type SerializeSeq = SerializeSeq<'a, B>; - type SerializeTuple = SerializeSeq<'a, B>; + type SerializeSeq = SerializeSeq<'a>; + type SerializeTuple = SerializeSeq<'a>; type SerializeTupleStruct = Unreachable; type SerializeTupleVariant = Unreachable; type SerializeMap = Unreachable; - type SerializeStruct = SerializeStruct<'a, B>; + type SerializeStruct = SerializeStruct<'a>; type SerializeStructVariant = Unreachable; fn serialize_bool(self, v: bool) -> Result { @@ -320,9 +316,8 @@ where } /// Serializes the given data structure as a string of JSON text -pub fn to_string(value: &T) -> Result> +pub fn to_string(value: &T) -> Result where - B: heapless::ArrayLength, T: ser::Serialize + ?Sized, { let mut ser = Serializer::new(); @@ -331,9 +326,8 @@ where } /// Serializes the given data structure as a JSON byte vector -pub fn to_vec(value: &T) -> Result> +pub fn to_vec(value: &T) -> Result> where - B: heapless::ArrayLength, T: ser::Serialize + ?Sized, { let mut ser = Serializer::new(); @@ -421,18 +415,14 @@ impl ser::SerializeStructVariant for Unreachable { mod tests { use serde_derive::Serialize; - use heapless::consts::U128; - - type N = U128; - #[test] fn array() { - assert_eq!(&*crate::to_string::(&[0, 1, 2]).unwrap(), "[0,1,2]"); + assert_eq!(&*crate::to_string(&[0, 1, 2]).unwrap(), "[0,1,2]"); } #[test] fn bool() { - assert_eq!(&*crate::to_string::(&true).unwrap(), "true"); + assert_eq!(&*crate::to_string(&true).unwrap(), "true"); } #[test] @@ -446,19 +436,19 @@ mod tests { } assert_eq!( - &*crate::to_string::(&Type::Boolean).unwrap(), + &*crate::to_string(&Type::Boolean).unwrap(), r#""boolean""# ); assert_eq!( - &*crate::to_string::(&Type::Number).unwrap(), + &*crate::to_string(&Type::Number).unwrap(), r#""number""# ); } #[test] fn str() { - assert_eq!(&*crate::to_string::("hello").unwrap(), r#""hello""#); + assert_eq!(&*crate::to_string("hello").unwrap(), r#""hello""#); } #[test] @@ -469,7 +459,7 @@ mod tests { } assert_eq!( - &*crate::to_string::(&Led { led: true }).unwrap(), + &*crate::to_string(&Led { led: true }).unwrap(), r#"{"led":true}"# ); } @@ -482,22 +472,22 @@ mod tests { } assert_eq!( - &*crate::to_string::(&Temperature { temperature: 127 }).unwrap(), + &*crate::to_string(&Temperature { temperature: 127 }).unwrap(), r#"{"temperature":127}"# ); assert_eq!( - &*crate::to_string::(&Temperature { temperature: 20 }).unwrap(), + &*crate::to_string(&Temperature { temperature: 20 }).unwrap(), r#"{"temperature":20}"# ); assert_eq!( - &*crate::to_string::(&Temperature { temperature: -17 }).unwrap(), + &*crate::to_string(&Temperature { temperature: -17 }).unwrap(), r#"{"temperature":-17}"# ); assert_eq!( - &*crate::to_string::(&Temperature { temperature: -128 }).unwrap(), + &*crate::to_string(&Temperature { temperature: -128 }).unwrap(), r#"{"temperature":-128}"# ); } @@ -510,7 +500,7 @@ mod tests { } assert_eq!( - crate::to_string::(&Property { + crate::to_string(&Property { description: Some("An ambient temperature sensor"), }) .unwrap(), @@ -519,7 +509,7 @@ mod tests { // XXX Ideally this should produce "{}" assert_eq!( - crate::to_string::(&Property { description: None }).unwrap(), + crate::to_string(&Property { description: None }).unwrap(), r#"{"description":null}"# ); } @@ -532,7 +522,7 @@ mod tests { } assert_eq!( - &*crate::to_string::(&Temperature { temperature: 20 }).unwrap(), + &*crate::to_string(&Temperature { temperature: 20 }).unwrap(), r#"{"temperature":20}"# ); } @@ -542,7 +532,7 @@ mod tests { #[derive(Serialize)] struct Empty {} - assert_eq!(&*crate::to_string::(&Empty {}).unwrap(), r#"{}"#); + assert_eq!(&*crate::to_string(&Empty {}).unwrap(), r#"{}"#); #[derive(Serialize)] struct Tuple { @@ -551,7 +541,7 @@ mod tests { } assert_eq!( - &*crate::to_string::(&Tuple { a: true, b: false }).unwrap(), + &*crate::to_string(&Tuple { a: true, b: false }).unwrap(), r#"{"a":true,"b":false}"# ); } diff --git a/src/ser/seq.rs b/src/ser/seq.rs index d260edddf..b0f806127 100644 --- a/src/ser/seq.rs +++ b/src/ser/seq.rs @@ -4,27 +4,18 @@ use heapless::ArrayLength; use crate::ser::{Error, Result, Serializer}; -pub struct SerializeSeq<'a, B> -where - B: ArrayLength, -{ - de: &'a mut Serializer, +pub struct SerializeSeq<'a> { + de: &'a mut Serializer, first: bool, } -impl<'a, B> SerializeSeq<'a, B> -where - B: ArrayLength, -{ - pub(crate) fn new(de: &'a mut Serializer) -> Self { +impl<'a> SerializeSeq<'a> { + pub(crate) fn new(de: &'a mut Serializer) -> Self { SerializeSeq { de, first: true } } } -impl<'a, B> ser::SerializeSeq for SerializeSeq<'a, B> -where - B: ArrayLength, -{ +impl<'a> ser::SerializeSeq for SerializeSeq<'a> { type Ok = (); type Error = Error; @@ -47,10 +38,7 @@ where } } -impl<'a, B> ser::SerializeTuple for SerializeSeq<'a, B> -where - B: ArrayLength, -{ +impl<'a> ser::SerializeTuple for SerializeSeq<'a> { type Ok = (); type Error = Error; diff --git a/src/ser/struct_.rs b/src/ser/struct_.rs index 2ff9421ad..3c6dc34cc 100644 --- a/src/ser/struct_.rs +++ b/src/ser/struct_.rs @@ -1,30 +1,19 @@ use serde::ser; -use heapless::ArrayLength; - use crate::ser::{Error, Result, Serializer}; -pub struct SerializeStruct<'a, B> -where - B: ArrayLength, -{ - de: &'a mut Serializer, +pub struct SerializeStruct<'a> { + de: &'a mut Serializer, first: bool, } -impl<'a, B> SerializeStruct<'a, B> -where - B: ArrayLength, -{ - pub(crate) fn new(de: &'a mut Serializer) -> Self { +impl<'a> SerializeStruct<'a> { + pub(crate) fn new(de: &'a mut Serializer) -> Self { SerializeStruct { de, first: true } } } -impl<'a, B> ser::SerializeStruct for SerializeStruct<'a, B> -where - B: ArrayLength, -{ +impl<'a> ser::SerializeStruct for SerializeStruct<'a> { type Ok = (); type Error = Error; From 719354e2c89f77bef9d09dde2e03b95296334d59 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sat, 26 Oct 2019 01:03:35 +0200 Subject: [PATCH 003/178] clean up error handler in serializer --- src/de/mod.rs | 4 ++-- src/ser/mod.rs | 25 ++++++++++++------------- src/ser/seq.rs | 6 ++---- src/ser/struct_.rs | 10 +++++----- 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index cc3d5c4f0..5d6562099 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -1,6 +1,6 @@ //! Deserialize JSON data to a Rust data structure -use std::{fmt, error}; +use std::{fmt, error, str::from_utf8}; use serde::de::{self, Visitor}; @@ -212,7 +212,7 @@ impl<'a> Deserializer<'a> { Some(b'"') => { let end = self.index; self.eat_char(); - return str::from_utf8(&self.slice[start..end]) + return from_utf8(&self.slice[start..end]) .map_err(|_| Error::InvalidUnicodeCodePoint); } Some(_) => self.eat_char(), diff --git a/src/ser/mod.rs b/src/ser/mod.rs index a4933954c..6a0f701b7 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -68,7 +68,7 @@ impl Serializer { // which take 200+ bytes of ROM / Flash macro_rules! serialize_unsigned { ($self:ident, $N:expr, $v:expr) => {{ - let mut buf: [u8; $N] = unsafe { mem::uninitialized() }; + let mut buf = [0u8; $N]; let mut v = $v; let mut i = $N - 1; @@ -83,7 +83,7 @@ macro_rules! serialize_unsigned { } } - $self.buf.extend_from_slice(&buf[i..])?; + $self.buf.extend_from_slice(&buf[i..]); Ok(()) }}; } @@ -99,7 +99,7 @@ macro_rules! serialize_signed { (false, v as $uxx) }; - let mut buf: [u8; $N] = unsafe { mem::uninitialized() }; + let mut buf = [0u8; $N]; let mut i = $N - 1; loop { buf[i] = (v % 10) as u8 + b'0'; @@ -117,7 +117,7 @@ macro_rules! serialize_signed { } else { i += 1; } - $self.buf.extend_from_slice(&buf[i..])?; + $self.buf.extend_from_slice(&buf[i..]); Ok(()) }}; } @@ -135,11 +135,10 @@ impl<'a> ser::Serializer for &'a mut Serializer { fn serialize_bool(self, v: bool) -> Result { if v { - self.buf.extend_from_slice(b"true")?; + self.buf.extend_from_slice(b"true"); } else { - self.buf.extend_from_slice(b"false")?; + self.buf.extend_from_slice(b"false"); } - Ok(()) } @@ -196,9 +195,9 @@ impl<'a> ser::Serializer for &'a mut Serializer { } fn serialize_str(self, v: &str) -> Result { - self.buf.push(b'"')?; - self.buf.extend_from_slice(v.as_bytes())?; - self.buf.push(b'"')?; + self.buf.push(b'"'); + self.buf.extend_from_slice(v.as_bytes()); + self.buf.push(b'"'); Ok(()) } @@ -207,7 +206,7 @@ impl<'a> ser::Serializer for &'a mut Serializer { } fn serialize_none(self) -> Result { - self.buf.extend_from_slice(b"null")?; + self.buf.extend_from_slice(b"null"); Ok(()) } @@ -260,7 +259,7 @@ impl<'a> ser::Serializer for &'a mut Serializer { } fn serialize_seq(self, _len: Option) -> Result { - self.buf.push(b'[')?; + self.buf.push(b'['); Ok(SerializeSeq::new(self)) } @@ -292,7 +291,7 @@ impl<'a> ser::Serializer for &'a mut Serializer { } fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { - self.buf.push(b'{')?; + self.buf.push(b'{'); Ok(SerializeStruct::new(self)) } diff --git a/src/ser/seq.rs b/src/ser/seq.rs index b0f806127..9e87c52b1 100644 --- a/src/ser/seq.rs +++ b/src/ser/seq.rs @@ -1,7 +1,5 @@ use serde::ser; -use heapless::ArrayLength; - use crate::ser::{Error, Result, Serializer}; pub struct SerializeSeq<'a> { @@ -24,7 +22,7 @@ impl<'a> ser::SerializeSeq for SerializeSeq<'a> { T: ser::Serialize, { if !self.first { - self.de.buf.push(b',')?; + self.de.buf.push(b','); } self.first = false; @@ -33,7 +31,7 @@ impl<'a> ser::SerializeSeq for SerializeSeq<'a> { } fn end(self) -> Result { - self.de.buf.push(b']')?; + self.de.buf.push(b']'); Ok(()) } } diff --git a/src/ser/struct_.rs b/src/ser/struct_.rs index 3c6dc34cc..f646b0cf8 100644 --- a/src/ser/struct_.rs +++ b/src/ser/struct_.rs @@ -23,13 +23,13 @@ impl<'a> ser::SerializeStruct for SerializeStruct<'a> { { // XXX if `value` is `None` we not produce any output for this field if !self.first { - self.de.buf.push(b',')?; + self.de.buf.push(b','); } self.first = false; - self.de.buf.push(b'"')?; - self.de.buf.extend_from_slice(key.as_bytes())?; - self.de.buf.extend_from_slice(b"\":")?; + self.de.buf.push(b'"'); + self.de.buf.extend_from_slice(key.as_bytes()); + self.de.buf.extend_from_slice(b"\":"); value.serialize(&mut *self.de)?; @@ -37,7 +37,7 @@ impl<'a> ser::SerializeStruct for SerializeStruct<'a> { } fn end(self) -> Result { - self.de.buf.push(b'}')?; + self.de.buf.push(b'}'); Ok(()) } } From 70ef3a331fb4cc3b50f81037c7fed5e82942c6c4 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sat, 26 Oct 2019 01:10:00 +0200 Subject: [PATCH 004/178] compiles and tests pass --- src/de/enum_.rs | 5 +---- src/de/map.rs | 10 ++-------- src/de/seq.rs | 5 +---- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/de/enum_.rs b/src/de/enum_.rs index dcc983854..68b5d0b8a 100644 --- a/src/de/enum_.rs +++ b/src/de/enum_.rs @@ -2,10 +2,7 @@ use serde::de; use crate::de::{Deserializer, Error, Result}; -pub(crate) struct UnitVariantAccess<'a, 'b> -where - 'b: 'a, -{ +pub(crate) struct UnitVariantAccess<'a, 'b> { de: &'a mut Deserializer<'b>, } diff --git a/src/de/map.rs b/src/de/map.rs index fc0e77b39..0631edd2d 100644 --- a/src/de/map.rs +++ b/src/de/map.rs @@ -2,10 +2,7 @@ use serde::de::{self, Visitor}; use crate::de::{Deserializer, Error}; -pub struct MapAccess<'a, 'b> -where - 'b: 'a, -{ +pub struct MapAccess<'a, 'b> { de: &'a mut Deserializer<'b>, first: bool, } @@ -60,10 +57,7 @@ impl<'a, 'de> de::MapAccess<'de> for MapAccess<'a, 'de> { } } -struct MapKey<'a, 'b> -where - 'b: 'a, -{ +struct MapKey<'a, 'b> { de: &'a mut Deserializer<'b>, } diff --git a/src/de/seq.rs b/src/de/seq.rs index b809dd165..743076971 100644 --- a/src/de/seq.rs +++ b/src/de/seq.rs @@ -2,10 +2,7 @@ use serde::de; use crate::de::{Deserializer, Error, Result}; -pub(crate) struct SeqAccess<'a, 'b> -where - 'b: 'a, -{ +pub(crate) struct SeqAccess<'a, 'b> { first: bool, de: &'a mut Deserializer<'b>, } From 87a320bee75964df742b916b61feeaabcc5c9ffd Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sat, 26 Oct 2019 01:11:38 +0200 Subject: [PATCH 005/178] cargo fmt --- src/de/enum_.rs | 2 +- src/de/map.rs | 4 ++-- src/de/mod.rs | 4 +--- src/de/seq.rs | 2 +- src/ser/mod.rs | 16 ++++------------ src/ser/seq.rs | 6 +++--- src/ser/struct_.rs | 2 +- 7 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/de/enum_.rs b/src/de/enum_.rs index 68b5d0b8a..7e516878e 100644 --- a/src/de/enum_.rs +++ b/src/de/enum_.rs @@ -2,7 +2,7 @@ use serde::de; use crate::de::{Deserializer, Error, Result}; -pub(crate) struct UnitVariantAccess<'a, 'b> { +pub(crate) struct UnitVariantAccess<'a, 'b> { de: &'a mut Deserializer<'b>, } diff --git a/src/de/map.rs b/src/de/map.rs index 0631edd2d..5af92ec17 100644 --- a/src/de/map.rs +++ b/src/de/map.rs @@ -2,7 +2,7 @@ use serde::de::{self, Visitor}; use crate::de::{Deserializer, Error}; -pub struct MapAccess<'a, 'b> { +pub struct MapAccess<'a, 'b> { de: &'a mut Deserializer<'b>, first: bool, } @@ -57,7 +57,7 @@ impl<'a, 'de> de::MapAccess<'de> for MapAccess<'a, 'de> { } } -struct MapKey<'a, 'b> { +struct MapKey<'a, 'b> { de: &'a mut Deserializer<'b>, } diff --git a/src/de/mod.rs b/src/de/mod.rs index 5d6562099..034fe6d42 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -1,6 +1,6 @@ //! Deserialize JSON data to a Rust data structure -use std::{fmt, error, str::from_utf8}; +use std::{error, fmt, str::from_utf8}; use serde::de::{self, Visitor}; @@ -119,7 +119,6 @@ impl fmt::Display for Error { } } - pub(crate) struct Deserializer<'b> { slice: &'b [u8], index: usize, @@ -604,7 +603,6 @@ impl de::Error for Error { } } - /// Deserializes an instance of type `T` from bytes of JSON text pub fn from_slice<'a, T>(v: &'a [u8]) -> Result where diff --git a/src/de/seq.rs b/src/de/seq.rs index 743076971..f3dbbdca3 100644 --- a/src/de/seq.rs +++ b/src/de/seq.rs @@ -2,7 +2,7 @@ use serde::de; use crate::de::{Deserializer, Error, Result}; -pub(crate) struct SeqAccess<'a, 'b> { +pub(crate) struct SeqAccess<'a, 'b> { first: bool, de: &'a mut Deserializer<'b>, } diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 6a0f701b7..70d123970 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -1,6 +1,6 @@ //! Serialize a Rust data structure into JSON data -use std::{fmt, error}; +use std::{error, fmt}; use serde::ser; @@ -36,7 +36,6 @@ impl From for Error { } } - impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { None @@ -53,8 +52,7 @@ impl fmt::Display for Error { } } -pub(crate) struct Serializer -{ +pub(crate) struct Serializer { buf: Vec, } @@ -434,15 +432,9 @@ mod tests { Number, } - assert_eq!( - &*crate::to_string(&Type::Boolean).unwrap(), - r#""boolean""# - ); + assert_eq!(&*crate::to_string(&Type::Boolean).unwrap(), r#""boolean""#); - assert_eq!( - &*crate::to_string(&Type::Number).unwrap(), - r#""number""# - ); + assert_eq!(&*crate::to_string(&Type::Number).unwrap(), r#""number""#); } #[test] diff --git a/src/ser/seq.rs b/src/ser/seq.rs index 9e87c52b1..dd0c69429 100644 --- a/src/ser/seq.rs +++ b/src/ser/seq.rs @@ -2,7 +2,7 @@ use serde::ser; use crate::ser::{Error, Result, Serializer}; -pub struct SerializeSeq<'a> { +pub struct SerializeSeq<'a> { de: &'a mut Serializer, first: bool, } @@ -13,7 +13,7 @@ impl<'a> SerializeSeq<'a> { } } -impl<'a> ser::SerializeSeq for SerializeSeq<'a> { +impl<'a> ser::SerializeSeq for SerializeSeq<'a> { type Ok = (); type Error = Error; @@ -36,7 +36,7 @@ impl<'a> ser::SerializeSeq for SerializeSeq<'a> { } } -impl<'a> ser::SerializeTuple for SerializeSeq<'a> { +impl<'a> ser::SerializeTuple for SerializeSeq<'a> { type Ok = (); type Error = Error; diff --git a/src/ser/struct_.rs b/src/ser/struct_.rs index f646b0cf8..9165d71b0 100644 --- a/src/ser/struct_.rs +++ b/src/ser/struct_.rs @@ -7,7 +7,7 @@ pub struct SerializeStruct<'a> { first: bool, } -impl<'a> SerializeStruct<'a> { +impl<'a> SerializeStruct<'a> { pub(crate) fn new(de: &'a mut Serializer) -> Self { SerializeStruct { de, first: true } } From 17ab3e65802192868da9633335501c6bd5dc8eff Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sat, 26 Oct 2019 01:12:09 +0200 Subject: [PATCH 006/178] Bump to 0.1.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 28a235d0a..f9aa03f6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/confio/serde-json-wasm" -version = "0.0.2" +version = "0.1.0" [dependencies] serde = {version = "^1.0.80", default-features = false } From 3e0a6a3f7592affdeb300f382d7f59bbf3d3a59c Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sat, 26 Oct 2019 12:40:17 +0200 Subject: [PATCH 007/178] Add support for deserializing String --- Cargo.toml | 2 +- src/de/mod.rs | 30 +++++++++++++++++++++++------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f9aa03f6f..7438c8dab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/confio/serde-json-wasm" version = "0.1.0" [dependencies] -serde = {version = "^1.0.80", default-features = false } +serde = {version = "^1.0.80", default-features = false, features = ["alloc"] } [dev-dependencies] serde_derive = "^1.0.80" diff --git a/src/de/mod.rs b/src/de/mod.rs index 034fe6d42..25408b0a2 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -441,11 +441,20 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { } } - fn deserialize_string(self, _visitor: V) -> Result + fn deserialize_string(self, visitor: V) -> Result where V: Visitor<'de>, { - unreachable!() + let peek = self.parse_whitespace().ok_or(Error::EofWhileParsingValue)?; + + match peek { + b'"' => { + self.eat_char(); + let str = self.parse_str()?.to_string(); + visitor.visit_string(str) + } + _ => Err(Error::InvalidType), + } } fn deserialize_bytes(self, _visitor: V) -> Result @@ -599,6 +608,7 @@ impl de::Error for Error { where T: fmt::Display, { + // TODO unreachable!() } } @@ -761,7 +771,6 @@ mod tests { // See https://iot.mozilla.org/wot/#thing-resource #[test] - #[ignore] fn wot() { #[derive(Debug, Deserialize, PartialEq)] struct Thing<'a> { @@ -789,6 +798,7 @@ mod tests { #[serde(borrow)] description: Option<&'a str>, href: &'a str, + owned: String, } assert_eq!( @@ -801,17 +811,20 @@ mod tests { "type": "number", "unit": "celsius", "description": "An ambient temperature sensor", - "href": "/properties/temperature" + "href": "/properties/temperature", + "owned": "own temperature" }, "humidity": { "type": "number", "unit": "percent", - "href": "/properties/humidity" + "href": "/properties/humidity", + "owned": "own humidity" }, "led": { "type": "boolean", "description": "A red LED", - "href": "/properties/led" + "href": "/properties/led", + "owned": "own led" } } } @@ -821,21 +834,24 @@ mod tests { properties: Properties { temperature: Property { ty: Type::Number, - unit: Some("celcius"), + unit: Some("celsius"), description: Some("An ambient temperature sensor"), href: "/properties/temperature", + owned: "own temperature".to_string(), }, humidity: Property { ty: Type::Number, unit: Some("percent"), description: None, href: "/properties/humidity", + owned: "own humidity".to_string(), }, led: Property { ty: Type::Boolean, unit: None, description: Some("A red LED"), href: "/properties/led", + owned: "own led".to_string(), }, }, ty: Type::Thing, From 2c31a9311afc244d1b09d0a7c731c44cd707f5b8 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sat, 26 Oct 2019 12:49:27 +0200 Subject: [PATCH 008/178] Add custom error message --- src/de/mod.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 25408b0a2..36ef1a0b3 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -63,6 +63,9 @@ pub enum Error { /// JSON has a comma after the last value in an array or map. TrailingComma, + /// Custom error message from serde + Custom(String), + #[doc(hidden)] __Extensible, } @@ -77,6 +80,15 @@ impl error::Error for Error { } } +impl de::Error for Error { + fn custom(msg: T) -> Self + where + T: fmt::Display, + { + Error::Custom(msg.to_string()) + } +} + impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( @@ -113,6 +125,7 @@ impl fmt::Display for Error { value." } Error::TrailingComma => "JSON has a comma after the last value in an array or map.", + Error::Custom(msg) => &msg, _ => "Invalid JSON", } ) @@ -603,16 +616,6 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { } } -impl de::Error for Error { - fn custom(_msg: T) -> Self - where - T: fmt::Display, - { - // TODO - unreachable!() - } -} - /// Deserializes an instance of type `T` from bytes of JSON text pub fn from_slice<'a, T>(v: &'a [u8]) -> Result where From a4feede03fc5d5e068f9668a45ec9e15acd2c4bf Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sat, 26 Oct 2019 20:13:18 +0200 Subject: [PATCH 009/178] Add tests for failure cases, allow whitespace before array --- src/de/mod.rs | 104 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 6 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 290ad5a11..0c4aafa3f 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -529,7 +529,7 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - match self.peek().ok_or(Error::EofWhileParsingValue)? { + match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? { b'[' => { self.eat_char(); let ret = visitor.visit_seq(SeqAccess::new(self))?; @@ -866,6 +866,97 @@ mod tests { ); } + #[test] + fn deserialize_optional_vector() { + #[derive(Debug, Deserialize, PartialEq)] + pub struct Response { + pub log: Option, + pub messages: Vec, + } + + #[derive(Debug, Deserialize, PartialEq)] + #[derive(serde_derive::Serialize)] + pub struct Msg { + pub name: String, + } + + #[derive(Debug, Deserialize, PartialEq)] + pub struct OptIn { + pub name: Option, + } + + let m: Msg = crate::from_str(r#"{ + "name": "one" + }"#).expect("simple"); + assert_eq!(m, Msg{name: "one".to_string()}); + + let o: OptIn = crate::from_str(r#"{ + "name": "two" + }"#).expect("opt"); + assert_eq!(o, OptIn{name: Some("two".to_string())}); + + let res: Response = crate::from_str(r#"{ + "log": "my log", + "messages": [{"name": "one"}] + }"#).expect("fud"); + assert_eq!(res, Response{ + log: Some("my log".to_string()), + messages: vec![Msg{name: "one".to_string()}], + }); + + let res: Response = crate::from_str(r#"{"log": null,"messages": []}"#).expect("fud"); + assert_eq!(res, Response{log: None, messages: Vec::new()}); + } + + + #[test] + fn deserialize_embedded_enum() { + #[derive(Debug, Deserialize, PartialEq)] + #[serde(rename_all = "lowercase")] + pub enum MyResult { + Ok(Response), + Err(String), + } + + #[derive(Debug, Deserialize, PartialEq)] + pub struct Response { + pub log: Option, + pub messages: Vec, + } + + #[derive(Debug, Deserialize, PartialEq)] + pub struct Msg { + pub name: String, + pub amount: Option, + } + + let res: MyResult = crate::from_str(r#"{ + "ok": { + "messages": [{ + "name": "fred", + "amount": "15" + }], + } + }"#).expect("goo"); + assert_eq!(res, MyResult::Ok(Response{log: Some("hello".to_string()), messages: vec![Msg{name: "fred".to_string(), amount: Some("15".to_string())}]})); + + let res: MyResult= crate::from_str(r#"{ + "ok": { + "log": "hello", + "messages": [], + } + }"#).expect("goo"); + assert_eq!(res, MyResult::Ok(Response{log: Some("hello".to_string()), messages: Vec::new()})); + + let res: MyResult = crate::from_str(r#"{ + "ok": { + "log": null, + "messages": [], + } + }"#).expect("goo"); + assert_eq!(res, MyResult::Ok(Response{log: None, messages: Vec::new()})); + } + // See https://iot.mozilla.org/wot/#thing-resource #[test] fn wot() { @@ -895,7 +986,7 @@ mod tests { #[serde(borrow)] description: Option<&'a str>, href: &'a str, - owned: String, + owned: Option, } assert_eq!( @@ -915,10 +1006,11 @@ mod tests { "type": "number", "unit": "percent", "href": "/properties/humidity", - "owned": "own humidity" + "owned": null }, "led": { "type": "boolean", + "unit": null, "description": "A red LED", "href": "/properties/led", "owned": "own led" @@ -934,21 +1026,21 @@ mod tests { unit: Some("celsius"), description: Some("An ambient temperature sensor"), href: "/properties/temperature", - owned: "own temperature".to_string(), + owned: Some("own temperature".to_string()), }, humidity: Property { ty: Type::Number, unit: Some("percent"), description: None, href: "/properties/humidity", - owned: "own humidity".to_string(), + owned: None, }, led: Property { ty: Type::Boolean, unit: None, description: Some("A red LED"), href: "/properties/led", - owned: "own led".to_string(), + owned: Some("own led".to_string()), }, }, ty: Type::Thing, From 0977eeb18e0bdc06047b29934a7591922f5cc5f3 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sat, 26 Oct 2019 20:30:27 +0200 Subject: [PATCH 010/178] Locate test failure - need to parse enum struct --- src/de/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/de/mod.rs b/src/de/mod.rs index 0c4aafa3f..b6e929b4c 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -605,7 +605,10 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { V: Visitor<'de>, { match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? { + // if it is a string enum b'"' => visitor.visit_enum(UnitVariantAccess::new(self)), + // if it is a struct enum + b'{' => Err(Error::Custom("TODO: parse enum struct".to_string())), _ => Err(Error::ExpectedSomeValue), } } From 196681a84dab26ea9d04d28df564a93bb5c55470 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sat, 26 Oct 2019 21:03:43 +0200 Subject: [PATCH 011/178] Add support for deserializing enum structs Slightly modified VariantAccess from serde-json --- src/de/enum_.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ src/de/mod.rs | 23 ++++++++++++++------ src/lib.rs | 2 +- 3 files changed, 74 insertions(+), 7 deletions(-) diff --git a/src/de/enum_.rs b/src/de/enum_.rs index 7e516878e..a369a26f6 100644 --- a/src/de/enum_.rs +++ b/src/de/enum_.rs @@ -53,3 +53,59 @@ impl<'de, 'a> de::VariantAccess<'de> for UnitVariantAccess<'a, 'de> { Err(Error::InvalidType) } } + +pub(crate) struct VariantAccess<'a, 'b> { + de: &'a mut Deserializer<'b>, +} + +impl<'a, 'b> VariantAccess<'a, 'b> { + pub fn new(de: &'a mut Deserializer<'b>) -> Self { + VariantAccess { de: de } + } +} + +impl<'a, 'de> de::EnumAccess<'de> for VariantAccess<'a, 'de> { + type Error = Error; + type Variant = Self; + + fn variant_seed(self, seed: V) -> Result<(V::Value, Self)> + where + V: de::DeserializeSeed<'de>, + { + let val = seed.deserialize(&mut *self.de)?; + self.de.parse_object_colon()?; + Ok((val, self)) + } +} + +impl<'a, 'de> de::VariantAccess<'de> for VariantAccess<'a, 'de> { + type Error = Error; + + fn unit_variant(self) -> Result<()> { +// Err(Error::Custom("unit_variant".to_string())) + unreachable!(); + } + + fn newtype_variant_seed(self, seed: T) -> Result + where + T: de::DeserializeSeed<'de>, + { + seed.deserialize(self.de) + } + + fn tuple_variant(self, _len: usize, _visitor: V) -> Result + where + V: de::Visitor<'de>, + { +// Err(Error::Custom("tuple_variant".to_string())) + unreachable!(); + } + + fn struct_variant(self, _fields: &'static [&'static str], _visitor: V) -> Result + where + V: de::Visitor<'de>, + { +// Err(Error::Custom("TODO: implement struct_variant".to_string())) + unreachable!(); + } +} \ No newline at end of file diff --git a/src/de/mod.rs b/src/de/mod.rs index b6e929b4c..ba3d3093b 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -4,7 +4,7 @@ use std::{error, fmt, str::from_utf8}; use serde::de::{self, Visitor}; -use self::enum_::UnitVariantAccess; +use self::enum_::{UnitVariantAccess, VariantAccess}; use self::map::MapAccess; use self::seq::SeqAccess; @@ -608,8 +608,18 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { // if it is a string enum b'"' => visitor.visit_enum(UnitVariantAccess::new(self)), // if it is a struct enum - b'{' => Err(Error::Custom("TODO: parse enum struct".to_string())), - _ => Err(Error::ExpectedSomeValue), + b'{' => { + self.eat_char(); + let value = visitor.visit_enum(VariantAccess::new(self))?; + match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? { + b'}' => { + self.eat_char(); + Ok(value) + }, + _ => Err(Error::ExpectedSomeValue), + } + }, + _ => Err(Error::ExpectedSomeIdent), } } @@ -935,10 +945,11 @@ mod tests { let res: MyResult = crate::from_str(r#"{ "ok": { + "log": "hello", "messages": [{ "name": "fred", "amount": "15" - }], + }] } }"#).expect("goo"); assert_eq!(res, MyResult::Ok(Response{log: Some("hello".to_string()), messages: vec![Msg{name: "fred".to_string(), amount: Some("15".to_string())}]})); @@ -946,7 +957,7 @@ mod tests { let res: MyResult= crate::from_str(r#"{ "ok": { "log": "hello", - "messages": [], + "messages": [] } }"#).expect("goo"); assert_eq!(res, MyResult::Ok(Response{log: Some("hello".to_string()), messages: Vec::new()})); @@ -954,7 +965,7 @@ mod tests { let res: MyResult = crate::from_str(r#"{ "ok": { "log": null, - "messages": [], + "messages": [] } }"#).expect("goo"); assert_eq!(res, MyResult::Ok(Response{log: None, messages: Vec::new()})); diff --git a/src/lib.rs b/src/lib.rs index 1c61bc6d8..58b2c45ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,7 @@ #![deny(missing_docs)] #![deny(rust_2018_compatibility)] #![deny(rust_2018_idioms)] -#![deny(warnings)] +//#![deny(warnings)] pub mod de; pub mod ser; From a2a4c891f6ea77a5bd4e93d81adb6a0f24f76451 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sat, 26 Oct 2019 22:13:53 +0200 Subject: [PATCH 012/178] Add support for serializing enum structs --- src/ser/mod.rs | 52 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 70d123970..07d825c22 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -235,25 +235,31 @@ impl<'a> ser::Serializer for &'a mut Serializer { fn serialize_newtype_struct( self, _name: &'static str, - _value: &T, + value: &T, ) -> Result where T: ser::Serialize, { - unreachable!() + + value.serialize(&mut *self) } fn serialize_newtype_variant( self, _name: &'static str, _variant_index: u32, - _variant: &'static str, - _value: &T, + variant: &'static str, + value: &T, ) -> Result where T: ser::Serialize, { - unreachable!() + self.buf.push(b'{'); + self.serialize_str(variant)?; + self.buf.push(b':'); + value.serialize(&mut *self)?; + self.buf.push(b'}'); + Ok(()) } fn serialize_seq(self, _len: Option) -> Result { @@ -536,4 +542,40 @@ mod tests { r#"{"a":true,"b":false}"# ); } + + #[test] + fn serialize_embedded_enum() { + #[derive(Debug, Serialize, PartialEq)] + #[serde(rename_all = "lowercase")] + pub enum MyResult { + Ok(Response), + Err(String), + } + + #[derive(Debug, Serialize, PartialEq)] + pub struct Response { + pub log: Option, + pub count: i64, + pub list: Vec, + } + + let json = crate::to_string(&MyResult::Err("some error".to_string())).expect("encode err enum"); + assert_eq!(json, r#"{"err":"some error"}"#.to_string()); + + let json = crate::to_string(&MyResult::Ok(Response { + log: Some("log message".to_string()), + count: 137, + list: Vec::new(), + })).expect("encode ok enum"); + assert_eq!(json, r#"{"ok":{"log":"log message","count":137,"list":[]}}"#.to_string()); + + let json = crate::to_string(&MyResult::Ok(Response { + log: None, + count: 137, + list: vec![18u32, 34, 12], + })).expect("encode ok enum"); + assert_eq!(json, r#"{"ok":{"log":null,"count":137,"list":[18,34,12]}}"#.to_string()); + + } + } From 0f765cb4106c10ceff4b8a622e5047afbdc0d623 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sat, 26 Oct 2019 23:10:00 +0200 Subject: [PATCH 013/178] Properly serialize non-newtype (embedded) enum structs --- src/ser/mod.rs | 13 ++++++++----- src/ser/struct_.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 07d825c22..902200c50 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -129,7 +129,7 @@ impl<'a> ser::Serializer for &'a mut Serializer { type SerializeTupleVariant = Unreachable; type SerializeMap = Unreachable; type SerializeStruct = SerializeStruct<'a>; - type SerializeStructVariant = Unreachable; + type SerializeStructVariant = SerializeStruct<'a>; fn serialize_bool(self, v: bool) -> Result { if v { @@ -302,12 +302,15 @@ impl<'a> ser::Serializer for &'a mut Serializer { fn serialize_struct_variant( self, - _name: &'static str, + name: &'static str, _variant_index: u32, - _variant: &'static str, - _len: usize, + variant: &'static str, + len: usize, ) -> Result { - unreachable!() + self.buf.push(b'{'); + self.serialize_str(variant)?; + self.buf.push(b':'); + self.serialize_struct(name, len) } fn collect_str(self, _value: &T) -> Result diff --git a/src/ser/struct_.rs b/src/ser/struct_.rs index 9165d71b0..2a7197c94 100644 --- a/src/ser/struct_.rs +++ b/src/ser/struct_.rs @@ -41,3 +41,32 @@ impl<'a> ser::SerializeStruct for SerializeStruct<'a> { Ok(()) } } + +impl<'a> ser::SerializeStructVariant for SerializeStruct<'a> { + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> + where + T: ser::Serialize, + { + // XXX if `value` is `None` we not produce any output for this field + if !self.first { + self.de.buf.push(b','); + } + self.first = false; + + self.de.buf.push(b'"'); + self.de.buf.extend_from_slice(key.as_bytes()); + self.de.buf.extend_from_slice(b"\":"); + + value.serialize(&mut *self.de)?; + + Ok(()) + } + + fn end(self) -> Result { + self.de.buf.push(b'}'); + Ok(()) + } +} From b2c119ec345e834b3f960aa891d16353c1826f42 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sat, 26 Oct 2019 23:32:12 +0200 Subject: [PATCH 014/178] Properly deserialize non-newtype (embedded) enum structs --- src/de/enum_.rs | 31 ++++++++++++++++++------------- src/de/mod.rs | 11 ++--------- src/lib.rs | 2 +- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/de/enum_.rs b/src/de/enum_.rs index a369a26f6..ceda2bc73 100644 --- a/src/de/enum_.rs +++ b/src/de/enum_.rs @@ -54,17 +54,17 @@ impl<'de, 'a> de::VariantAccess<'de> for UnitVariantAccess<'a, 'de> { } } -pub(crate) struct VariantAccess<'a, 'b> { +pub(crate) struct StructVariantAccess<'a, 'b> { de: &'a mut Deserializer<'b>, } -impl<'a, 'b> VariantAccess<'a, 'b> { +impl<'a, 'b> StructVariantAccess<'a, 'b> { pub fn new(de: &'a mut Deserializer<'b>) -> Self { - VariantAccess { de: de } + StructVariantAccess { de: de } } } -impl<'a, 'de> de::EnumAccess<'de> for VariantAccess<'a, 'de> { +impl<'a, 'de> de::EnumAccess<'de> for StructVariantAccess<'a, 'de> { type Error = Error; type Variant = Self; @@ -78,34 +78,39 @@ impl<'a, 'de> de::EnumAccess<'de> for VariantAccess<'a, 'de> { } } -impl<'a, 'de> de::VariantAccess<'de> for VariantAccess<'a, 'de> { +impl<'a, 'de> de::VariantAccess<'de> for StructVariantAccess<'a, 'de> { type Error = Error; fn unit_variant(self) -> Result<()> { -// Err(Error::Custom("unit_variant".to_string())) - unreachable!(); + Err(Error::InvalidType) } fn newtype_variant_seed(self, seed: T) -> Result where T: de::DeserializeSeed<'de>, { - seed.deserialize(self.de) + let value = seed.deserialize(&mut *self.de)?; + // we remove trailing '}' to be consistent with struct_variant algorithm + match self.de.parse_whitespace().ok_or(Error::EofWhileParsingValue)? { + b'}' => { + self.de.eat_char(); + Ok(value) + }, + _ => Err(Error::ExpectedSomeValue), + } } fn tuple_variant(self, _len: usize, _visitor: V) -> Result where V: de::Visitor<'de>, { -// Err(Error::Custom("tuple_variant".to_string())) - unreachable!(); + Err(Error::InvalidType) } - fn struct_variant(self, _fields: &'static [&'static str], _visitor: V) -> Result + fn struct_variant(self, fields: &'static [&'static str], visitor: V) -> Result where V: de::Visitor<'de>, { -// Err(Error::Custom("TODO: implement struct_variant".to_string())) - unreachable!(); + de::Deserializer::deserialize_struct(self.de, "", fields, visitor) } } \ No newline at end of file diff --git a/src/de/mod.rs b/src/de/mod.rs index ba3d3093b..8897af107 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -4,7 +4,7 @@ use std::{error, fmt, str::from_utf8}; use serde::de::{self, Visitor}; -use self::enum_::{UnitVariantAccess, VariantAccess}; +use self::enum_::{StructVariantAccess, UnitVariantAccess}; use self::map::MapAccess; use self::seq::SeqAccess; @@ -610,14 +610,7 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { // if it is a struct enum b'{' => { self.eat_char(); - let value = visitor.visit_enum(VariantAccess::new(self))?; - match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? { - b'}' => { - self.eat_char(); - Ok(value) - }, - _ => Err(Error::ExpectedSomeValue), - } + visitor.visit_enum(StructVariantAccess::new(self)) }, _ => Err(Error::ExpectedSomeIdent), } diff --git a/src/lib.rs b/src/lib.rs index 58b2c45ec..1c61bc6d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,7 @@ #![deny(missing_docs)] #![deny(rust_2018_compatibility)] #![deny(rust_2018_idioms)] -//#![deny(warnings)] +#![deny(warnings)] pub mod de; pub mod ser; From c378e4e08361e944a4e087ee04c429dd44b8f8e1 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sat, 26 Oct 2019 23:49:38 +0200 Subject: [PATCH 015/178] Clean up tests --- src/de/enum_.rs | 26 ++++++---- src/de/mod.rs | 125 ++++++++++++++++++++++++++++++++++----------- src/ser/mod.rs | 44 ++++++++++------ src/ser/struct_.rs | 4 +- 4 files changed, 138 insertions(+), 61 deletions(-) diff --git a/src/de/enum_.rs b/src/de/enum_.rs index ceda2bc73..35d00e678 100644 --- a/src/de/enum_.rs +++ b/src/de/enum_.rs @@ -69,8 +69,8 @@ impl<'a, 'de> de::EnumAccess<'de> for StructVariantAccess<'a, 'de> { type Variant = Self; fn variant_seed(self, seed: V) -> Result<(V::Value, Self)> - where - V: de::DeserializeSeed<'de>, + where + V: de::DeserializeSeed<'de>, { let val = seed.deserialize(&mut *self.de)?; self.de.parse_object_colon()?; @@ -86,31 +86,35 @@ impl<'a, 'de> de::VariantAccess<'de> for StructVariantAccess<'a, 'de> { } fn newtype_variant_seed(self, seed: T) -> Result - where - T: de::DeserializeSeed<'de>, + where + T: de::DeserializeSeed<'de>, { let value = seed.deserialize(&mut *self.de)?; // we remove trailing '}' to be consistent with struct_variant algorithm - match self.de.parse_whitespace().ok_or(Error::EofWhileParsingValue)? { + match self + .de + .parse_whitespace() + .ok_or(Error::EofWhileParsingValue)? + { b'}' => { self.de.eat_char(); Ok(value) - }, + } _ => Err(Error::ExpectedSomeValue), } } fn tuple_variant(self, _len: usize, _visitor: V) -> Result - where - V: de::Visitor<'de>, + where + V: de::Visitor<'de>, { Err(Error::InvalidType) } fn struct_variant(self, fields: &'static [&'static str], visitor: V) -> Result - where - V: de::Visitor<'de>, + where + V: de::Visitor<'de>, { de::Deserializer::deserialize_struct(self.de, "", fields, visitor) } -} \ No newline at end of file +} diff --git a/src/de/mod.rs b/src/de/mod.rs index 8897af107..b252ef7cb 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -82,8 +82,8 @@ impl error::Error for Error { impl de::Error for Error { fn custom(msg: T) -> Self - where - T: fmt::Display, + where + T: fmt::Display, { Error::Custom(msg.to_string()) } @@ -611,7 +611,7 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { b'{' => { self.eat_char(); visitor.visit_enum(StructVariantAccess::new(self)) - }, + } _ => Err(Error::ExpectedSomeIdent), } } @@ -816,10 +816,13 @@ mod tests { // wrong number of args match crate::from_str::(r#"[10]"#) { - Err(super::Error::Custom(_)) => {}, + Err(super::Error::Custom(_)) => {} _ => panic!("expect custom error"), } - assert_eq!(crate::from_str::(r#"[10, 20, 30]"#), Err(crate::de::Error::TrailingCharacters)); + assert_eq!( + crate::from_str::(r#"[10, 20, 30]"#), + Err(crate::de::Error::TrailingCharacters) + ); } #[test] @@ -847,7 +850,9 @@ mod tests { ); assert_eq!( - crate::from_str(r#"{ "temperature": 20, "source": { "station": "dock", "sensors": ["front", "back"] } }"#), + crate::from_str( + r#"{ "temperature": 20, "source": { "station": "dock", "sensors": ["front", "back"] } }"# + ), Ok(Temperature { temperature: 20 }) ); @@ -880,8 +885,7 @@ mod tests { pub messages: Vec, } - #[derive(Debug, Deserialize, PartialEq)] - #[derive(serde_derive::Serialize)] + #[derive(Debug, Deserialize, PartialEq, serde_derive::Serialize)] pub struct Msg { pub name: String, } @@ -891,30 +895,59 @@ mod tests { pub name: Option, } - let m: Msg = crate::from_str(r#"{ + let m: Msg = crate::from_str( + r#"{ "name": "one" - }"#).expect("simple"); - assert_eq!(m, Msg{name: "one".to_string()}); + }"#, + ) + .expect("simple"); + assert_eq!( + m, + Msg { + name: "one".to_string() + } + ); - let o: OptIn = crate::from_str(r#"{ + let o: OptIn = crate::from_str( + r#"{ "name": "two" - }"#).expect("opt"); - assert_eq!(o, OptIn{name: Some("two".to_string())}); + }"#, + ) + .expect("opt"); + assert_eq!( + o, + OptIn { + name: Some("two".to_string()) + } + ); - let res: Response = crate::from_str(r#"{ + let res: Response = crate::from_str( + r#"{ "log": "my log", "messages": [{"name": "one"}] - }"#).expect("fud"); - assert_eq!(res, Response{ - log: Some("my log".to_string()), - messages: vec![Msg{name: "one".to_string()}], - }); + }"#, + ) + .expect("fud"); + assert_eq!( + res, + Response { + log: Some("my log".to_string()), + messages: vec![Msg { + name: "one".to_string() + }], + } + ); let res: Response = crate::from_str(r#"{"log": null,"messages": []}"#).expect("fud"); - assert_eq!(res, Response{log: None, messages: Vec::new()}); + assert_eq!( + res, + Response { + log: None, + messages: Vec::new() + } + ); } - #[test] fn deserialize_embedded_enum() { #[derive(Debug, Deserialize, PartialEq)] @@ -936,7 +969,8 @@ mod tests { pub amount: Option, } - let res: MyResult = crate::from_str(r#"{ + let res: MyResult = crate::from_str( + r#"{ "ok": { "log": "hello", "messages": [{ @@ -944,24 +978,53 @@ mod tests { "amount": "15" }] } - }"#).expect("goo"); - assert_eq!(res, MyResult::Ok(Response{log: Some("hello".to_string()), messages: vec![Msg{name: "fred".to_string(), amount: Some("15".to_string())}]})); + }"#, + ) + .expect("goo"); + assert_eq!( + res, + MyResult::Ok(Response { + log: Some("hello".to_string()), + messages: vec![Msg { + name: "fred".to_string(), + amount: Some("15".to_string()) + }] + }) + ); - let res: MyResult= crate::from_str(r#"{ + let res: MyResult = crate::from_str( + r#"{ "ok": { "log": "hello", "messages": [] } - }"#).expect("goo"); - assert_eq!(res, MyResult::Ok(Response{log: Some("hello".to_string()), messages: Vec::new()})); + }"#, + ) + .expect("goo"); + assert_eq!( + res, + MyResult::Ok(Response { + log: Some("hello".to_string()), + messages: Vec::new() + }) + ); - let res: MyResult = crate::from_str(r#"{ + let res: MyResult = crate::from_str( + r#"{ "ok": { "log": null, "messages": [] } - }"#).expect("goo"); - assert_eq!(res, MyResult::Ok(Response{log: None, messages: Vec::new()})); + }"#, + ) + .expect("goo"); + assert_eq!( + res, + MyResult::Ok(Response { + log: None, + messages: Vec::new() + }) + ); } // See https://iot.mozilla.org/wot/#thing-resource diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 902200c50..7028d44f4 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -232,15 +232,10 @@ impl<'a> ser::Serializer for &'a mut Serializer { self.serialize_str(variant) } - fn serialize_newtype_struct( - self, - _name: &'static str, - value: &T, - ) -> Result + fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result where T: ser::Serialize, { - value.serialize(&mut *self) } @@ -546,39 +541,54 @@ mod tests { ); } + use serde_derive::Deserialize; + #[test] fn serialize_embedded_enum() { - #[derive(Debug, Serialize, PartialEq)] + #[derive(Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "lowercase")] pub enum MyResult { Ok(Response), Err(String), } - #[derive(Debug, Serialize, PartialEq)] + #[derive(Debug, Deserialize, Serialize, PartialEq)] pub struct Response { pub log: Option, pub count: i64, pub list: Vec, } - let json = crate::to_string(&MyResult::Err("some error".to_string())).expect("encode err enum"); + let err_input = MyResult::Err("some error".to_string()); + let json = crate::to_string(&err_input).expect("encode err enum"); assert_eq!(json, r#"{"err":"some error"}"#.to_string()); + let loaded = crate::from_str(&json).expect("re-load err enum"); + assert_eq!(err_input, loaded); - let json = crate::to_string(&MyResult::Ok(Response { + let empty_list = MyResult::Ok(Response { log: Some("log message".to_string()), count: 137, list: Vec::new(), - })).expect("encode ok enum"); - assert_eq!(json, r#"{"ok":{"log":"log message","count":137,"list":[]}}"#.to_string()); + }); + let json = crate::to_string(&empty_list).expect("encode ok enum"); + assert_eq!( + json, + r#"{"ok":{"log":"log message","count":137,"list":[]}}"#.to_string() + ); + let loaded = crate::from_str(&json).expect("re-load ok enum"); + assert_eq!(empty_list, loaded); - let json = crate::to_string(&MyResult::Ok(Response { + let full_list = MyResult::Ok(Response { log: None, count: 137, list: vec![18u32, 34, 12], - })).expect("encode ok enum"); - assert_eq!(json, r#"{"ok":{"log":null,"count":137,"list":[18,34,12]}}"#.to_string()); - + }); + let json = crate::to_string(&full_list).expect("encode ok enum"); + assert_eq!( + json, + r#"{"ok":{"log":null,"count":137,"list":[18,34,12]}}"#.to_string() + ); + let loaded = crate::from_str(&json).expect("re-load ok enum"); + assert_eq!(full_list, loaded); } - } diff --git a/src/ser/struct_.rs b/src/ser/struct_.rs index 2a7197c94..f21d18cef 100644 --- a/src/ser/struct_.rs +++ b/src/ser/struct_.rs @@ -47,8 +47,8 @@ impl<'a> ser::SerializeStructVariant for SerializeStruct<'a> { type Error = Error; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> - where - T: ser::Serialize, + where + T: ser::Serialize, { // XXX if `value` is `None` we not produce any output for this field if !self.first { From d5e594aa40b1e4d7e8a4e6e55bce2024ad0e829b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sun, 27 Oct 2019 00:16:29 +0200 Subject: [PATCH 016/178] cleanup --- Cargo.toml | 1 + rust-toolchain | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 rust-toolchain diff --git a/Cargo.toml b/Cargo.toml index 7438c8dab..8f4bde9a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/confio/serde-json-wasm" version = "0.1.0" +exclude = ["ci/*", ".gitignore", ".cargo", ".travis.yml"] [dependencies] serde = {version = "^1.0.80", default-features = false, features = ["alloc"] } diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index bf867e0ae..000000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -nightly From 40298601fa058990df48c08443626be9e562b38e Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sun, 27 Oct 2019 19:53:52 +0100 Subject: [PATCH 017/178] Properly terminate enum structs with second } when serializing --- src/ser/struct_.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ser/struct_.rs b/src/ser/struct_.rs index f21d18cef..3898db82a 100644 --- a/src/ser/struct_.rs +++ b/src/ser/struct_.rs @@ -66,6 +66,9 @@ impl<'a> ser::SerializeStructVariant for SerializeStruct<'a> { } fn end(self) -> Result { + // close struct + self.de.buf.push(b'}'); + // close surrounding enum self.de.buf.push(b'}'); Ok(()) } From 781c9b036ce5b3be796e55a7448eb57ee78bd5fb Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sun, 27 Oct 2019 20:13:05 +0100 Subject: [PATCH 018/178] Properly eat trailing } when parsing interior enum struct --- src/de/enum_.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/de/enum_.rs b/src/de/enum_.rs index 35d00e678..8cac6f3c0 100644 --- a/src/de/enum_.rs +++ b/src/de/enum_.rs @@ -115,6 +115,17 @@ impl<'a, 'de> de::VariantAccess<'de> for StructVariantAccess<'a, 'de> { where V: de::Visitor<'de>, { - de::Deserializer::deserialize_struct(self.de, "", fields, visitor) + let value = de::Deserializer::deserialize_struct(&mut *self.de, "", fields, visitor)?; + match self + .de + .parse_whitespace() + .ok_or(Error::EofWhileParsingValue)? + { + b'}' => { + self.de.eat_char(); + Ok(value) + } + _ => Err(Error::ExpectedSomeValue), + } } } From 85e44463aee49278ac0dc9c862cae021c76fca6f Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Sun, 27 Oct 2019 20:16:56 +0100 Subject: [PATCH 019/178] Bump to 0.1.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8f4bde9a4..9fcc61c65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/confio/serde-json-wasm" -version = "0.1.0" +version = "0.1.1" exclude = ["ci/*", ".gitignore", ".cargo", ".travis.yml"] [dependencies] From 8dd6aeed44d1db33a46fb0702c0ea2ba1f1d8fa8 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 20 Dec 2019 14:51:07 +0100 Subject: [PATCH 020/178] Test showing newtype broken --- .gitignore | 1 + src/de/mod.rs | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/.gitignore b/.gitignore index 2691a8f47..642d95ddf 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .#* /target Cargo.lock +.idea diff --git a/src/de/mod.rs b/src/de/mod.rs index b252ef7cb..194cc4000 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -877,6 +877,20 @@ mod tests { ); } + #[derive(Deserialize, Clone, Debug, PartialEq)] + pub struct Address(pub String); + + #[derive(Deserialize, Clone, Debug, PartialEq)] + pub struct NewtypeDemo { + pub address: Address, + } + + #[test] + fn newtypes() { + let c: NewtypeDemo = crate::from_str(r#"{"address": "johnny"}"#).unwrap(); + assert_eq!(Address("johnny".to_string()), c.address); + } + #[test] fn deserialize_optional_vector() { #[derive(Debug, Deserialize, PartialEq)] From 66c50515d8c5f0790e893ec9c331a80f4c3f8ac5 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 20 Dec 2019 14:56:37 +0100 Subject: [PATCH 021/178] Newtype(String) support working --- src/de/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 194cc4000..ef8136666 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -518,11 +518,11 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { } /// Unsupported. We can’t parse newtypes because we don’t know the underlying type. - fn deserialize_newtype_struct(self, _name: &'static str, _visitor: V) -> Result + fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result where V: Visitor<'de>, { - unreachable!() + visitor.visit_newtype_struct(self) } fn deserialize_seq(self, visitor: V) -> Result From aa01495da6f77520c035984f96258e3146122dbc Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 20 Dec 2019 15:13:09 +0100 Subject: [PATCH 022/178] Bump to 0.1.2 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9fcc61c65..7d542da96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/confio/serde-json-wasm" -version = "0.1.1" +version = "0.1.2" exclude = ["ci/*", ".gitignore", ".cargo", ".travis.yml"] [dependencies] From 1918f6d31da04dba6acd265c0b34808ad22e27ed Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 12 Mar 2020 15:08:55 +0100 Subject: [PATCH 023/178] Expose Serializer and Unreachable --- src/ser/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 7028d44f4..f7eb47d96 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -52,7 +52,9 @@ impl fmt::Display for Error { } } -pub(crate) struct Serializer { +/// Serializer implements serde::ser::Serializer and allows us to serialize a +/// serde struct into JSON +pub struct Serializer { buf: Vec, } @@ -345,7 +347,9 @@ impl ser::Error for Error { } } -pub(crate) enum Unreachable {} +/// Unreachable is a placeholder for features that are not supported +/// (and should be unreachable, unless you use unsupported serde flags) +pub enum Unreachable {} impl ser::SerializeTupleStruct for Unreachable { type Ok = (); From 6cb0bc1c989fc8c399b1ac2d05f71d3182c60b25 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 12 Mar 2020 15:09:49 +0100 Subject: [PATCH 024/178] Expose Deserializer --- src/de/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index ef8136666..23db87823 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -132,7 +132,9 @@ impl fmt::Display for Error { } } -pub(crate) struct Deserializer<'b> { +/// Deserializer will parse serde-json-wasm flavored JSON into a +/// serde-annotated struct +pub struct Deserializer<'b> { slice: &'b [u8], index: usize, } From c9895fe69b48dc0308894f28631dcba2fc8ca46c Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 12 Mar 2020 15:10:15 +0100 Subject: [PATCH 025/178] Bump version to 0.1.3 and update repo org --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7d542da96..b866f1d94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,8 @@ keywords = ["serde", "json", "wasm"] license = "MIT OR Apache-2.0" name = "serde-json-wasm" readme = "README.md" -repository = "https://github.com/confio/serde-json-wasm" -version = "0.1.2" +repository = "https://github.com/CosmWasm/serde-json-wasm" +version = "0.1.3" exclude = ["ci/*", ".gitignore", ".cargo", ".travis.yml"] [dependencies] From 4fdef2db19bace5e338fd16d6d3ce39397191b01 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 29 Apr 2020 11:13:22 +0200 Subject: [PATCH 026/178] Reserve some memory for the serializer --- src/ser/mod.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index f7eb47d96..8896d823b 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -58,9 +58,14 @@ pub struct Serializer { buf: Vec, } +/// Number of bytes reserved by default for the output JSON +static INITIAL_CAPACITY: usize = 1024; + impl Serializer { fn new() -> Self { - Serializer { buf: Vec::new() } + Serializer { + buf: Vec::with_capacity(INITIAL_CAPACITY), + } } } From 6629f9d6566a6875c7f932baf3889b1c963f4f12 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 29 Apr 2020 12:44:12 +0200 Subject: [PATCH 027/178] Escape " and \ --- src/ser/mod.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 8896d823b..03c9b9724 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -201,7 +201,28 @@ impl<'a> ser::Serializer for &'a mut Serializer { fn serialize_str(self, v: &str) -> Result { self.buf.push(b'"'); - self.buf.extend_from_slice(v.as_bytes()); + + // Do escaping according to "6. MUST represent all strings (including object member names) in their minimal-length UTF-8 encoding": + // https://gibson042.github.io/canonicaljson-spec/ + + let mut buf = [0u8; 4]; // a char is up to 4 bytes long + for c in v.chars() { + match c { + '\\' => { + self.buf.push(b'\\'); + self.buf.push(b'\\'); + } + '"' => { + self.buf.push(b'\\'); + self.buf.push(b'"'); + } + _ => { + let encoded = c.encode_utf8(&mut buf as &mut [u8]); + self.buf.extend_from_slice(encoded.as_bytes()); + } + } + } + self.buf.push(b'"'); Ok(()) } @@ -453,6 +474,11 @@ mod tests { #[test] fn str() { assert_eq!(&*crate::to_string("hello").unwrap(), r#""hello""#); + assert_eq!(&*crate::to_string("").unwrap(), r#""""#); + + // " and \ must be escaped + assert_eq!(&*crate::to_string("foo\"bar").unwrap(), r#""foo\"bar""#); + assert_eq!(&*crate::to_string("foo\\bar").unwrap(), r#""foo\\bar""#); } #[test] From 99daed9925815ff1c6bde0016de7cecd739b926c Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 29 Apr 2020 13:09:34 +0200 Subject: [PATCH 028/178] Implement \b, \t, \n, \f, \r escaping --- src/ser/mod.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 03c9b9724..5c151aa35 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -216,6 +216,26 @@ impl<'a> ser::Serializer for &'a mut Serializer { self.buf.push(b'\\'); self.buf.push(b'"'); } + '\u{0008}' => { + self.buf.push(b'\\'); + self.buf.push(b'b'); + } + '\u{0009}' => { + self.buf.push(b'\\'); + self.buf.push(b't'); + } + '\u{000A}' => { + self.buf.push(b'\\'); + self.buf.push(b'n'); + } + '\u{000C}' => { + self.buf.push(b'\\'); + self.buf.push(b'f'); + } + '\u{000D}' => { + self.buf.push(b'\\'); + self.buf.push(b'r'); + } _ => { let encoded = c.encode_utf8(&mut buf as &mut [u8]); self.buf.extend_from_slice(encoded.as_bytes()); @@ -479,6 +499,13 @@ mod tests { // " and \ must be escaped assert_eq!(&*crate::to_string("foo\"bar").unwrap(), r#""foo\"bar""#); assert_eq!(&*crate::to_string("foo\\bar").unwrap(), r#""foo\\bar""#); + + // \b, \t, \n, \f, \r must be escaped in their two-character escaping + assert_eq!(&*crate::to_string(" \u{0008} ").unwrap(), r#"" \b ""#); + assert_eq!(&*crate::to_string(" \u{0009} ").unwrap(), r#"" \t ""#); + assert_eq!(&*crate::to_string(" \u{000A} ").unwrap(), r#"" \n ""#); + assert_eq!(&*crate::to_string(" \u{000C} ").unwrap(), r#"" \f ""#); + assert_eq!(&*crate::to_string(" \u{000D} ").unwrap(), r#"" \r ""#); } #[test] From e02353bccf432d76183bb9eabbbdf1d4d8be49f4 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 29 Apr 2020 14:21:25 +0200 Subject: [PATCH 029/178] Escape U+0000 through U+001F --- src/ser/mod.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 5c151aa35..f6790bbe5 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -127,6 +127,20 @@ macro_rules! serialize_signed { }}; } +/// Upper-case hex for value in 0..16, encoded as ASCII bytes +fn hex_4bit(c: u8) -> u8 { + if c <= 9 { + 0x30 + c + } else { + 0x41 + (c - 10) + } +} + +/// Upper-case hex for value in 0..256, encoded as ASCII bytes +fn hex(c: u8) -> (u8, u8) { + (hex_4bit(c >> 4), hex_4bit(c & 0x0F)) +} + impl<'a> ser::Serializer for &'a mut Serializer { type Ok = (); type Error = Error; @@ -236,6 +250,15 @@ impl<'a> ser::Serializer for &'a mut Serializer { self.buf.push(b'\\'); self.buf.push(b'r'); } + '\u{0000}'..='\u{001F}' => { + self.buf.push(b'\\'); + self.buf.push(b'u'); + self.buf.push(b'0'); + self.buf.push(b'0'); + let (b1, b2) = hex(c as u8); + self.buf.push(b1); + self.buf.push(b2); + } _ => { let encoded = c.encode_utf8(&mut buf as &mut [u8]); self.buf.extend_from_slice(encoded.as_bytes()); @@ -506,6 +529,14 @@ mod tests { assert_eq!(&*crate::to_string(" \u{000A} ").unwrap(), r#"" \n ""#); assert_eq!(&*crate::to_string(" \u{000C} ").unwrap(), r#"" \f ""#); assert_eq!(&*crate::to_string(" \u{000D} ").unwrap(), r#"" \r ""#); + + // U+0000 through U+001F is escaped using six-character \u00xx uppercase hexadecimal escape sequences + assert_eq!(&*crate::to_string(" \u{0000} ").unwrap(), r#"" \u0000 ""#); + assert_eq!(&*crate::to_string(" \u{0001} ").unwrap(), r#"" \u0001 ""#); + assert_eq!(&*crate::to_string(" \u{0007} ").unwrap(), r#"" \u0007 ""#); + assert_eq!(&*crate::to_string(" \u{000e} ").unwrap(), r#"" \u000E ""#); + assert_eq!(&*crate::to_string(" \u{001D} ").unwrap(), r#"" \u001D ""#); + assert_eq!(&*crate::to_string(" \u{001f} ").unwrap(), r#"" \u001F ""#); } #[test] From cbb0477b0e850614bd5513007870dc253417abb7 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 29 Apr 2020 14:31:02 +0200 Subject: [PATCH 030/178] Test Characters unescaped if possible --- src/ser/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index f6790bbe5..4afcb90b6 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -519,6 +519,13 @@ mod tests { assert_eq!(&*crate::to_string("hello").unwrap(), r#""hello""#); assert_eq!(&*crate::to_string("").unwrap(), r#""""#); + // Characters unescaped if possible + assert_eq!(&*crate::to_string("ä").unwrap(), r#""ä""#); + assert_eq!(&*crate::to_string("৬").unwrap(), r#""৬""#); + assert_eq!(&*crate::to_string("\u{A0}").unwrap(), r#"" ""#); // non-breaking space + assert_eq!(&*crate::to_string("ℝ").unwrap(), r#""ℝ""#); // 3 byte character + assert_eq!(&*crate::to_string("💣").unwrap(), r#""💣""#); // 4 byte character + // " and \ must be escaped assert_eq!(&*crate::to_string("foo\"bar").unwrap(), r#""foo\"bar""#); assert_eq!(&*crate::to_string("foo\\bar").unwrap(), r#""foo\\bar""#); From a7f7d3c37ba89a26441dde97c1bb850b5eedc57c Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 29 Apr 2020 16:39:55 +0200 Subject: [PATCH 031/178] Add comment on lone surrogates --- src/ser/mod.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 4afcb90b6..e0702788e 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -216,8 +216,13 @@ impl<'a> ser::Serializer for &'a mut Serializer { fn serialize_str(self, v: &str) -> Result { self.buf.push(b'"'); - // Do escaping according to "6. MUST represent all strings (including object member names) in their minimal-length UTF-8 encoding": - // https://gibson042.github.io/canonicaljson-spec/ + // Do escaping according to "6. MUST represent all strings (including object member names) in + // their minimal-length UTF-8 encoding": https://gibson042.github.io/canonicaljson-spec/ + // + // We don't need to escape lone surrogates because they are not valid unicode code points, even + // if they can exist in JSON or JavaScript strings. As a result, lone surrogates + // cannot exist in a Rust String. If they do, the bug is in the String constructor. + // An excellent explanation is available at https://www.youtube.com/watch?v=HhIEDWmQS3w let mut buf = [0u8; 4]; // a char is up to 4 bytes long for c in v.chars() { @@ -524,7 +529,7 @@ mod tests { assert_eq!(&*crate::to_string("৬").unwrap(), r#""৬""#); assert_eq!(&*crate::to_string("\u{A0}").unwrap(), r#"" ""#); // non-breaking space assert_eq!(&*crate::to_string("ℝ").unwrap(), r#""ℝ""#); // 3 byte character - assert_eq!(&*crate::to_string("💣").unwrap(), r#""💣""#); // 4 byte character + assert_eq!(&*crate::to_string("💣").unwrap(), r#""💣""#); // surregate pair // " and \ must be escaped assert_eq!(&*crate::to_string("foo\"bar").unwrap(), r#""foo\"bar""#); From 784b4b47cc7b164a8540dfd682d7902fecb82721 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 30 Apr 2020 09:14:29 +0200 Subject: [PATCH 032/178] Improve naming and comments --- src/ser/mod.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index e0702788e..cc640a27f 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -219,12 +219,15 @@ impl<'a> ser::Serializer for &'a mut Serializer { // Do escaping according to "6. MUST represent all strings (including object member names) in // their minimal-length UTF-8 encoding": https://gibson042.github.io/canonicaljson-spec/ // - // We don't need to escape lone surrogates because they are not valid unicode code points, even - // if they can exist in JSON or JavaScript strings. As a result, lone surrogates + // We don't need to escape lone surrogates because surrogate pairs do not exist in valid UTF-8, + // even if they can exist in JSON or JavaScript strings (UCS-2 based). As a result, lone surrogates // cannot exist in a Rust String. If they do, the bug is in the String constructor. // An excellent explanation is available at https://www.youtube.com/watch?v=HhIEDWmQS3w - let mut buf = [0u8; 4]; // a char is up to 4 bytes long + // Temporary storage for encoded a single char. + // A char is up to 4 bytes long wehn encoded to UTF-8. + let mut encoding_tmp = [0u8; 4]; + for c in v.chars() { match c { '\\' => { @@ -260,12 +263,12 @@ impl<'a> ser::Serializer for &'a mut Serializer { self.buf.push(b'u'); self.buf.push(b'0'); self.buf.push(b'0'); - let (b1, b2) = hex(c as u8); - self.buf.push(b1); - self.buf.push(b2); + let (hex1, hex2) = hex(c as u8); + self.buf.push(hex1); + self.buf.push(hex2); } _ => { - let encoded = c.encode_utf8(&mut buf as &mut [u8]); + let encoded = c.encode_utf8(&mut encoding_tmp as &mut [u8]); self.buf.extend_from_slice(encoded.as_bytes()); } } @@ -529,7 +532,7 @@ mod tests { assert_eq!(&*crate::to_string("৬").unwrap(), r#""৬""#); assert_eq!(&*crate::to_string("\u{A0}").unwrap(), r#"" ""#); // non-breaking space assert_eq!(&*crate::to_string("ℝ").unwrap(), r#""ℝ""#); // 3 byte character - assert_eq!(&*crate::to_string("💣").unwrap(), r#""💣""#); // surregate pair + assert_eq!(&*crate::to_string("💣").unwrap(), r#""💣""#); // 4 byte character // " and \ must be escaped assert_eq!(&*crate::to_string("foo\"bar").unwrap(), r#""foo\"bar""#); From e30eea4ebe15cff9459ab1a0bd59345e9425a380 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 4 May 2020 12:50:50 +0200 Subject: [PATCH 033/178] Remove TODO: escaping in string serialization This was done in https://github.com/CosmWasm/serde-json-wasm/pull/10 --- src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1c61bc6d8..781d382b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,7 +24,7 @@ //! - Supports serialization (compact format only) of: //! - `bool` //! - Integers -//! - `str` (\*\*) +//! - `str` //! - `Option` //! - Arrays //! - Tuples @@ -34,8 +34,6 @@ //! (\*) Deserialization of strings ignores escaped sequences. Escaped sequences might be supported //! in the future using a different Serializer as this operation is not zero copy. //! -//! (\*\*) Serialization of strings doesn't escape stuff. This simply has not been implemented yet. -//! //! # Planned features //! //! - (De)serialization from / into IO objects once `core::io::{Read,Write}` becomes a thing. From c554b24dffd5618569a6db37c6ae5c6b4287fa4f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 30 Apr 2020 09:39:48 +0200 Subject: [PATCH 034/178] Test parse_whitespace --- src/de/mod.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/de/mod.rs b/src/de/mod.rs index 23db87823..723bb75b6 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -686,6 +686,60 @@ mod tests { Thing, } + #[test] + fn parse_whitespace() { + assert_eq!(crate::from_str(" true"), Ok(true)); + assert_eq!(crate::from_str("\ttrue"), Ok(true)); + assert_eq!(crate::from_str("\ntrue"), Ok(true)); + assert_eq!(crate::from_str("\rtrue"), Ok(true)); + assert_eq!(crate::from_str("\n\rtrue"), Ok(true)); + assert_eq!(crate::from_str("\r\ntrue"), Ok(true)); + assert_eq!(crate::from_str("true "), Ok(true)); + assert_eq!(crate::from_str("true\t"), Ok(true)); + assert_eq!(crate::from_str("true\n"), Ok(true)); + assert_eq!(crate::from_str("true\r"), Ok(true)); + assert_eq!(crate::from_str("true\n\r"), Ok(true)); + assert_eq!(crate::from_str("true\r\n"), Ok(true)); + + assert_eq!(crate::from_str("[4,5]"), Ok([4, 5])); + assert_eq!(crate::from_str(" [4,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("\t[4,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("\n[4,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("\r[4,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("\n\r[4,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("\r\n[4,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[ 4,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[\t4,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[\n4,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[\r4,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[\n\r4,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[\r\n4,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4 ,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4\t,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4\n,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4\r,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4\n\r,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4\r\n,5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4, 5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4,\t5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4,\n5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4,\r5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4,\n\r5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4,\r\n5]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4,5 ]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4,5\t]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4,5\n]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4,5\r]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4,5\n\r]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4,5\r\n]"), Ok([4, 5])); + assert_eq!(crate::from_str("[4,5] "), Ok([4, 5])); + assert_eq!(crate::from_str("[4,5]\t"), Ok([4, 5])); + assert_eq!(crate::from_str("[4,5]\n"), Ok([4, 5])); + assert_eq!(crate::from_str("[4,5]\r"), Ok([4, 5])); + assert_eq!(crate::from_str("[4,5]\n\r"), Ok([4, 5])); + assert_eq!(crate::from_str("[4,5]\r\n"), Ok([4, 5])); + } + #[test] fn array() { assert_eq!(crate::from_str::<[i32; 0]>("[]"), Ok([])); From c97fff4e4eef8546b145d9bb5f7d4bc9ea9d2d9a Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 30 Apr 2020 09:51:01 +0200 Subject: [PATCH 035/178] Use from_str --- src/de/mod.rs | 206 +++++++++++++++++++++++++------------------------- 1 file changed, 101 insertions(+), 105 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 723bb75b6..fcb9e11b6 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -674,6 +674,7 @@ where #[cfg(test)] mod tests { + use super::from_str; use serde_derive::Deserialize; #[derive(Debug, Deserialize, PartialEq)] @@ -688,92 +689,92 @@ mod tests { #[test] fn parse_whitespace() { - assert_eq!(crate::from_str(" true"), Ok(true)); - assert_eq!(crate::from_str("\ttrue"), Ok(true)); - assert_eq!(crate::from_str("\ntrue"), Ok(true)); - assert_eq!(crate::from_str("\rtrue"), Ok(true)); - assert_eq!(crate::from_str("\n\rtrue"), Ok(true)); - assert_eq!(crate::from_str("\r\ntrue"), Ok(true)); - assert_eq!(crate::from_str("true "), Ok(true)); - assert_eq!(crate::from_str("true\t"), Ok(true)); - assert_eq!(crate::from_str("true\n"), Ok(true)); - assert_eq!(crate::from_str("true\r"), Ok(true)); - assert_eq!(crate::from_str("true\n\r"), Ok(true)); - assert_eq!(crate::from_str("true\r\n"), Ok(true)); - - assert_eq!(crate::from_str("[4,5]"), Ok([4, 5])); - assert_eq!(crate::from_str(" [4,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("\t[4,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("\n[4,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("\r[4,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("\n\r[4,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("\r\n[4,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[ 4,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[\t4,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[\n4,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[\r4,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[\n\r4,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[\r\n4,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4 ,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4\t,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4\n,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4\r,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4\n\r,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4\r\n,5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4, 5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4,\t5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4,\n5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4,\r5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4,\n\r5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4,\r\n5]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4,5 ]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4,5\t]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4,5\n]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4,5\r]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4,5\n\r]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4,5\r\n]"), Ok([4, 5])); - assert_eq!(crate::from_str("[4,5] "), Ok([4, 5])); - assert_eq!(crate::from_str("[4,5]\t"), Ok([4, 5])); - assert_eq!(crate::from_str("[4,5]\n"), Ok([4, 5])); - assert_eq!(crate::from_str("[4,5]\r"), Ok([4, 5])); - assert_eq!(crate::from_str("[4,5]\n\r"), Ok([4, 5])); - assert_eq!(crate::from_str("[4,5]\r\n"), Ok([4, 5])); + assert_eq!(from_str(" true"), Ok(true)); + assert_eq!(from_str("\ttrue"), Ok(true)); + assert_eq!(from_str("\ntrue"), Ok(true)); + assert_eq!(from_str("\rtrue"), Ok(true)); + assert_eq!(from_str("\n\rtrue"), Ok(true)); + assert_eq!(from_str("\r\ntrue"), Ok(true)); + assert_eq!(from_str("true "), Ok(true)); + assert_eq!(from_str("true\t"), Ok(true)); + assert_eq!(from_str("true\n"), Ok(true)); + assert_eq!(from_str("true\r"), Ok(true)); + assert_eq!(from_str("true\n\r"), Ok(true)); + assert_eq!(from_str("true\r\n"), Ok(true)); + + assert_eq!(from_str("[4,5]"), Ok([4, 5])); + assert_eq!(from_str(" [4,5]"), Ok([4, 5])); + assert_eq!(from_str("\t[4,5]"), Ok([4, 5])); + assert_eq!(from_str("\n[4,5]"), Ok([4, 5])); + assert_eq!(from_str("\r[4,5]"), Ok([4, 5])); + assert_eq!(from_str("\n\r[4,5]"), Ok([4, 5])); + assert_eq!(from_str("\r\n[4,5]"), Ok([4, 5])); + assert_eq!(from_str("[ 4,5]"), Ok([4, 5])); + assert_eq!(from_str("[\t4,5]"), Ok([4, 5])); + assert_eq!(from_str("[\n4,5]"), Ok([4, 5])); + assert_eq!(from_str("[\r4,5]"), Ok([4, 5])); + assert_eq!(from_str("[\n\r4,5]"), Ok([4, 5])); + assert_eq!(from_str("[\r\n4,5]"), Ok([4, 5])); + assert_eq!(from_str("[4 ,5]"), Ok([4, 5])); + assert_eq!(from_str("[4\t,5]"), Ok([4, 5])); + assert_eq!(from_str("[4\n,5]"), Ok([4, 5])); + assert_eq!(from_str("[4\r,5]"), Ok([4, 5])); + assert_eq!(from_str("[4\n\r,5]"), Ok([4, 5])); + assert_eq!(from_str("[4\r\n,5]"), Ok([4, 5])); + assert_eq!(from_str("[4, 5]"), Ok([4, 5])); + assert_eq!(from_str("[4,\t5]"), Ok([4, 5])); + assert_eq!(from_str("[4,\n5]"), Ok([4, 5])); + assert_eq!(from_str("[4,\r5]"), Ok([4, 5])); + assert_eq!(from_str("[4,\n\r5]"), Ok([4, 5])); + assert_eq!(from_str("[4,\r\n5]"), Ok([4, 5])); + assert_eq!(from_str("[4,5 ]"), Ok([4, 5])); + assert_eq!(from_str("[4,5\t]"), Ok([4, 5])); + assert_eq!(from_str("[4,5\n]"), Ok([4, 5])); + assert_eq!(from_str("[4,5\r]"), Ok([4, 5])); + assert_eq!(from_str("[4,5\n\r]"), Ok([4, 5])); + assert_eq!(from_str("[4,5\r\n]"), Ok([4, 5])); + assert_eq!(from_str("[4,5] "), Ok([4, 5])); + assert_eq!(from_str("[4,5]\t"), Ok([4, 5])); + assert_eq!(from_str("[4,5]\n"), Ok([4, 5])); + assert_eq!(from_str("[4,5]\r"), Ok([4, 5])); + assert_eq!(from_str("[4,5]\n\r"), Ok([4, 5])); + assert_eq!(from_str("[4,5]\r\n"), Ok([4, 5])); } #[test] fn array() { - assert_eq!(crate::from_str::<[i32; 0]>("[]"), Ok([])); - assert_eq!(crate::from_str("[0, 1, 2]"), Ok([0, 1, 2])); + assert_eq!(from_str::<[i32; 0]>("[]"), Ok([])); + assert_eq!(from_str("[0, 1, 2]"), Ok([0, 1, 2])); // errors - assert!(crate::from_str::<[i32; 2]>("[0, 1,]").is_err()); + assert!(from_str::<[i32; 2]>("[0, 1,]").is_err()); } #[test] fn bool() { - assert_eq!(crate::from_str("true"), Ok(true)); - assert_eq!(crate::from_str(" true"), Ok(true)); - assert_eq!(crate::from_str("true "), Ok(true)); + assert_eq!(from_str("true"), Ok(true)); + assert_eq!(from_str(" true"), Ok(true)); + assert_eq!(from_str("true "), Ok(true)); - assert_eq!(crate::from_str("false"), Ok(false)); - assert_eq!(crate::from_str(" false"), Ok(false)); - assert_eq!(crate::from_str("false "), Ok(false)); + assert_eq!(from_str("false"), Ok(false)); + assert_eq!(from_str(" false"), Ok(false)); + assert_eq!(from_str("false "), Ok(false)); // errors - assert!(crate::from_str::("true false").is_err()); - assert!(crate::from_str::("tru").is_err()); + assert!(from_str::("true false").is_err()); + assert!(from_str::("tru").is_err()); } #[test] fn enum_clike() { - assert_eq!(crate::from_str(r#" "boolean" "#), Ok(Type::Boolean)); - assert_eq!(crate::from_str(r#" "number" "#), Ok(Type::Number)); - assert_eq!(crate::from_str(r#" "thing" "#), Ok(Type::Thing)); + assert_eq!(from_str(r#" "boolean" "#), Ok(Type::Boolean)); + assert_eq!(from_str(r#" "number" "#), Ok(Type::Number)); + assert_eq!(from_str(r#" "thing" "#), Ok(Type::Thing)); } #[test] fn str() { - assert_eq!(crate::from_str(r#" "hello" "#), Ok("hello")); + assert_eq!(from_str(r#" "hello" "#), Ok("hello")); } #[test] @@ -783,11 +784,8 @@ mod tests { led: bool, } - assert_eq!(crate::from_str(r#"{ "led": true }"#), Ok(Led { led: true })); - assert_eq!( - crate::from_str(r#"{ "led": false }"#), - Ok(Led { led: false }) - ); + assert_eq!(from_str(r#"{ "led": true }"#), Ok(Led { led: true })); + assert_eq!(from_str(r#"{ "led": false }"#), Ok(Led { led: false })); } #[test] @@ -798,23 +796,23 @@ mod tests { } assert_eq!( - crate::from_str(r#"{ "temperature": -17 }"#), + from_str(r#"{ "temperature": -17 }"#), Ok(Temperature { temperature: -17 }) ); assert_eq!( - crate::from_str(r#"{ "temperature": -0 }"#), + from_str(r#"{ "temperature": -0 }"#), Ok(Temperature { temperature: -0 }) ); assert_eq!( - crate::from_str(r#"{ "temperature": 0 }"#), + from_str(r#"{ "temperature": 0 }"#), Ok(Temperature { temperature: 0 }) ); // out of range - assert!(crate::from_str::(r#"{ "temperature": 128 }"#).is_err()); - assert!(crate::from_str::(r#"{ "temperature": -129 }"#).is_err()); + assert!(from_str::(r#"{ "temperature": 128 }"#).is_err()); + assert!(from_str::(r#"{ "temperature": -129 }"#).is_err()); } #[test] @@ -826,18 +824,18 @@ mod tests { } assert_eq!( - crate::from_str(r#"{ "description": "An ambient temperature sensor" }"#), + from_str(r#"{ "description": "An ambient temperature sensor" }"#), Ok(Property { description: Some("An ambient temperature sensor"), }) ); assert_eq!( - crate::from_str(r#"{ "description": null }"#), + from_str(r#"{ "description": null }"#), Ok(Property { description: None }) ); - assert_eq!(crate::from_str(r#"{}"#), Ok(Property { description: None })); + assert_eq!(from_str(r#"{}"#), Ok(Property { description: None })); } #[test] @@ -848,18 +846,18 @@ mod tests { } assert_eq!( - crate::from_str(r#"{ "temperature": 20 }"#), + from_str(r#"{ "temperature": 20 }"#), Ok(Temperature { temperature: 20 }) ); assert_eq!( - crate::from_str(r#"{ "temperature": 0 }"#), + from_str(r#"{ "temperature": 0 }"#), Ok(Temperature { temperature: 0 }) ); // out of range - assert!(crate::from_str::(r#"{ "temperature": 256 }"#).is_err()); - assert!(crate::from_str::(r#"{ "temperature": -1 }"#).is_err()); + assert!(from_str::(r#"{ "temperature": 256 }"#).is_err()); + assert!(from_str::(r#"{ "temperature": -1 }"#).is_err()); } #[test] @@ -867,16 +865,16 @@ mod tests { #[derive(Debug, Deserialize, PartialEq)] struct Xy(i8, i8); - assert_eq!(crate::from_str(r#"[10, 20]"#), Ok(Xy(10, 20))); - assert_eq!(crate::from_str(r#"[10, -20]"#), Ok(Xy(10, -20))); + assert_eq!(from_str(r#"[10, 20]"#), Ok(Xy(10, 20))); + assert_eq!(from_str(r#"[10, -20]"#), Ok(Xy(10, -20))); // wrong number of args - match crate::from_str::(r#"[10]"#) { + match from_str::(r#"[10]"#) { Err(super::Error::Custom(_)) => {} _ => panic!("expect custom error"), } assert_eq!( - crate::from_str::(r#"[10, 20, 30]"#), + from_str::(r#"[10, 20, 30]"#), Err(crate::de::Error::TrailingCharacters) ); } @@ -889,46 +887,44 @@ mod tests { } assert_eq!( - crate::from_str(r#"{ "temperature": 20, "high": 80, "low": -10, "updated": true }"#), + from_str(r#"{ "temperature": 20, "high": 80, "low": -10, "updated": true }"#), Ok(Temperature { temperature: 20 }) ); assert_eq!( - crate::from_str( - r#"{ "temperature": 20, "conditions": "windy", "forecast": "cloudy" }"# - ), + from_str(r#"{ "temperature": 20, "conditions": "windy", "forecast": "cloudy" }"#), Ok(Temperature { temperature: 20 }) ); assert_eq!( - crate::from_str(r#"{ "temperature": 20, "hourly_conditions": ["windy", "rainy"] }"#), + from_str(r#"{ "temperature": 20, "hourly_conditions": ["windy", "rainy"] }"#), Ok(Temperature { temperature: 20 }) ); assert_eq!( - crate::from_str( + from_str( r#"{ "temperature": 20, "source": { "station": "dock", "sensors": ["front", "back"] } }"# ), Ok(Temperature { temperature: 20 }) ); assert_eq!( - crate::from_str(r#"{ "temperature": 20, "invalid": this-is-ignored }"#), + from_str(r#"{ "temperature": 20, "invalid": this-is-ignored }"#), Ok(Temperature { temperature: 20 }) ); assert_eq!( - crate::from_str::(r#"{ "temperature": 20, "broken": }"#), + from_str::(r#"{ "temperature": 20, "broken": }"#), Err(crate::de::Error::ExpectedSomeValue) ); assert_eq!( - crate::from_str::(r#"{ "temperature": 20, "broken": [ }"#), + from_str::(r#"{ "temperature": 20, "broken": [ }"#), Err(crate::de::Error::ExpectedSomeValue) ); assert_eq!( - crate::from_str::(r#"{ "temperature": 20, "broken": ] }"#), + from_str::(r#"{ "temperature": 20, "broken": ] }"#), Err(crate::de::Error::ExpectedSomeValue) ); } @@ -943,7 +939,7 @@ mod tests { #[test] fn newtypes() { - let c: NewtypeDemo = crate::from_str(r#"{"address": "johnny"}"#).unwrap(); + let c: NewtypeDemo = from_str(r#"{"address": "johnny"}"#).unwrap(); assert_eq!(Address("johnny".to_string()), c.address); } @@ -965,7 +961,7 @@ mod tests { pub name: Option, } - let m: Msg = crate::from_str( + let m: Msg = from_str( r#"{ "name": "one" }"#, @@ -978,7 +974,7 @@ mod tests { } ); - let o: OptIn = crate::from_str( + let o: OptIn = from_str( r#"{ "name": "two" }"#, @@ -991,7 +987,7 @@ mod tests { } ); - let res: Response = crate::from_str( + let res: Response = from_str( r#"{ "log": "my log", "messages": [{"name": "one"}] @@ -1008,7 +1004,7 @@ mod tests { } ); - let res: Response = crate::from_str(r#"{"log": null,"messages": []}"#).expect("fud"); + let res: Response = from_str(r#"{"log": null,"messages": []}"#).expect("fud"); assert_eq!( res, Response { @@ -1039,7 +1035,7 @@ mod tests { pub amount: Option, } - let res: MyResult = crate::from_str( + let res: MyResult = from_str( r#"{ "ok": { "log": "hello", @@ -1062,7 +1058,7 @@ mod tests { }) ); - let res: MyResult = crate::from_str( + let res: MyResult = from_str( r#"{ "ok": { "log": "hello", @@ -1079,7 +1075,7 @@ mod tests { }) ); - let res: MyResult = crate::from_str( + let res: MyResult = from_str( r#"{ "ok": { "log": null, @@ -1130,7 +1126,7 @@ mod tests { } assert_eq!( - crate::from_str::>( + from_str::>( r#" { "type": "thing", From 0024b6f846c3c78db7d2d4df9b40769da3e9d77b Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 30 Apr 2020 10:06:42 +0200 Subject: [PATCH 036/178] Improve basic string deserialization testing --- src/de/mod.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/de/mod.rs b/src/de/mod.rs index fcb9e11b6..ea01ba5f1 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -775,6 +775,17 @@ mod tests { #[test] fn str() { assert_eq!(from_str(r#" "hello" "#), Ok("hello")); + assert_eq!(from_str(r#" "" "#), Ok("")); + assert_eq!(from_str(r#" " " "#), Ok(" ")); + assert_eq!(from_str(r#" "👏" "#), Ok("👏")); + } + + #[test] + fn string() { + assert_eq!(from_str(r#" "hello" "#), Ok(String::from("hello"))); + assert_eq!(from_str(r#" "" "#), Ok(String::from(""))); + assert_eq!(from_str(r#" " " "#), Ok(String::from(" "))); + assert_eq!(from_str(r#" "👏" "#), Ok(String::from("👏"))); } #[test] From b4366199e4ad6dbd17a709dc380864e361a5d89d Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 4 May 2020 14:05:26 +0200 Subject: [PATCH 037/178] Add Github Actions workflow --- .github/workflows/Basic.yml | 63 +++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 .github/workflows/Basic.yml diff --git a/.github/workflows/Basic.yml b/.github/workflows/Basic.yml new file mode 100644 index 000000000..5f73c1541 --- /dev/null +++ b/.github/workflows/Basic.yml @@ -0,0 +1,63 @@ +# Based on https://github.com/actions-rs/example/blob/master/.github/workflows/quickstart.yml + +on: [push, pull_request] + +name: Basic + +jobs: + + tests: + name: Tests + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.40.0 + target: wasm32-unknown-unknown + override: true + + - name: Run tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --locked + env: + RUST_BACKTRACE: 1 + + - name: Compile to Wasm + uses: actions-rs/cargo@v1 + with: + command: wasm + args: --locked + + sanity: + name: Sanity + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.40.0 + override: true + components: rustfmt, clippy + + - name: Run cargo fmt + uses: actions-rs/cargo@v1 + with: + command: fmt + args: -- --check + + - name: Run cargo clippy + uses: actions-rs/cargo@v1 + with: + command: clippy + args: -- -D warnings From 0c8559b98b3bd3735b866fb27e8dc6f11d68314c Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 4 May 2020 14:15:46 +0200 Subject: [PATCH 038/178] Check-in Cargo.lock --- .gitignore | 1 - Cargo.lock | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock diff --git a/.gitignore b/.gitignore index 642d95ddf..84b8beeeb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ **/*.rs.bk .#* /target -Cargo.lock .idea diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..f9485c3cc --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,61 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "proc-macro2" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" + +[[package]] +name = "serde-json-wasm" +version = "0.1.3" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" From 9dd11c17f660b519000cdb8689526af18e178a63 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 4 May 2020 14:17:55 +0200 Subject: [PATCH 039/178] Sanitize enum_.rs --- src/de/enum_.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/de/enum_.rs b/src/de/enum_.rs index 8cac6f3c0..7547df525 100644 --- a/src/de/enum_.rs +++ b/src/de/enum_.rs @@ -60,7 +60,7 @@ pub(crate) struct StructVariantAccess<'a, 'b> { impl<'a, 'b> StructVariantAccess<'a, 'b> { pub fn new(de: &'a mut Deserializer<'b>) -> Self { - StructVariantAccess { de: de } + StructVariantAccess { de } } } @@ -120,12 +120,12 @@ impl<'a, 'de> de::VariantAccess<'de> for StructVariantAccess<'a, 'de> { .de .parse_whitespace() .ok_or(Error::EofWhileParsingValue)? - { - b'}' => { - self.de.eat_char(); - Ok(value) - } - _ => Err(Error::ExpectedSomeValue), + { + b'}' => { + self.de.eat_char(); + Ok(value) } + _ => Err(Error::ExpectedSomeValue), + } } } From 8b615bc10b61159a1f0095fbdc65d62e1a81bfdf Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 30 Apr 2020 13:47:50 +0200 Subject: [PATCH 040/178] Detect string end quotes correctly --- src/de/mod.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index ea01ba5f1..3e90f3bc4 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -224,10 +224,37 @@ impl<'a> Deserializer<'a> { loop { match self.peek() { Some(b'"') => { - let end = self.index; - self.eat_char(); - return from_utf8(&self.slice[start..end]) - .map_err(|_| Error::InvalidUnicodeCodePoint); + // Counts the number of backslashes in front of the current index. + // + // "some string with \\\" included." + // ^^^^^ + // ||||| + // loop run: 4321| + // | + // `index` + // + // Since we only get in this code branch if we found a " starting the string and `index` is greater + // than the start position, we know the loop will end no later than this point. + let leading_backslashes = |index: usize| -> usize { + let mut count = 0; + loop { + if self.slice[index - count - 1] == b'\\' { + count += 1; + } else { + return count; + } + } + }; + + let is_escaped = leading_backslashes(self.index) % 2 == 1; + if is_escaped { + self.eat_char(); // just continue + } else { + let end = self.index; + self.eat_char(); + return from_utf8(&self.slice[start..end]) + .map_err(|_| Error::InvalidUnicodeCodePoint); + } } Some(_) => self.eat_char(), None => return Err(Error::EofWhileParsingString), @@ -774,10 +801,30 @@ mod tests { #[test] fn str() { + // simple assert_eq!(from_str(r#" "hello" "#), Ok("hello")); assert_eq!(from_str(r#" "" "#), Ok("")); assert_eq!(from_str(r#" " " "#), Ok(" ")); assert_eq!(from_str(r#" "👏" "#), Ok("👏")); + + // no unescaping is done (as documented as a known issue in lib.rs) + assert_eq!(from_str(r#" "hel\tlo" "#), Ok("hel\\tlo")); + assert_eq!(from_str(r#" "hello \\" "#), Ok("hello \\\\")); + + // escaped " in the string content + assert_eq!(from_str(r#" "foo\"bar" "#), Ok(r#"foo\"bar"#)); + assert_eq!(from_str(r#" "foo\\\"bar" "#), Ok(r#"foo\\\"bar"#)); + assert_eq!(from_str(r#" "foo\"\"bar" "#), Ok(r#"foo\"\"bar"#)); + assert_eq!(from_str(r#" "\"bar" "#), Ok(r#"\"bar"#)); + assert_eq!(from_str(r#" "foo\"" "#), Ok(r#"foo\""#)); + assert_eq!(from_str(r#" "\"" "#), Ok(r#"\""#)); + + // non-excaped " preceded by backslashes + assert_eq!(from_str(r#" "foo bar\\" "#), Ok(r#"foo bar\\"#)); + assert_eq!(from_str(r#" "foo bar\\\\" "#), Ok(r#"foo bar\\\\"#)); + assert_eq!(from_str(r#" "foo bar\\\\\\" "#), Ok(r#"foo bar\\\\\\"#)); + assert_eq!(from_str(r#" "foo bar\\\\\\\\" "#), Ok(r#"foo bar\\\\\\\\"#)); + assert_eq!(from_str(r#" "\\" "#), Ok(r#"\\"#)); } #[test] @@ -786,6 +833,10 @@ mod tests { assert_eq!(from_str(r#" "" "#), Ok(String::from(""))); assert_eq!(from_str(r#" " " "#), Ok(String::from(" "))); assert_eq!(from_str(r#" "👏" "#), Ok(String::from("👏"))); + + // escaped " in the string content + // (note: no unescaping is performed, as documented as a known issue in lib.rs) + assert_eq!(from_str(r#" "foo\"bar" "#), Ok(String::from(r#"foo\"bar"#))); } #[test] From 068540f357eaaf5770fa23cc0ee31f840fb4c02d Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 5 May 2020 10:39:55 +0200 Subject: [PATCH 041/178] Prepare project for 0.2.0 --- CHANGELOG.md | 36 +++++++++++++++++++++++++++--------- Cargo.lock | 2 +- Cargo.toml | 6 +++--- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 635c46252..e217d68df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,22 +1,40 @@ -# Change Log +# Changelog All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](http://keepachangelog.com/) -and this project adheres to [Semantic Versioning](http://semver.org/). +The format is based on [Keep a Changelog](http://keepachangelog.com/) and this +project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -## [v0.1.0] - 2019-02-06 +### Fixed + +- The end of strings is now detected correctly in deserialization (#11) ### Changed -- [breaking-change] The `heapless` dependency has been bumped to v0.4.0 +- Strings are now escaped during serialization (#10) + +## [0.1.3] - 2020-03-12 + +- Expose deserializer and serializer + +## [0.1.2] - 2019-12-20 + +- Add newtype string support + +## [0.1.1] - 2019-10-27 -- This crate now compiles on stable +- Fix embeded enums -## v0.0.1 +## [0.1.0] - 2019-10-27 -Initial release +Initial release after forking from +[serde-json-core](https://github.com/japaric/serde-json-core) at +[bf5533a0](https://github.com/japaric/serde-json-core/commit/bf5533a042a0). -[Unreleased]: https://github.com/rust-embedded/cortex-m/compare/v0.5.8...HEAD +[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.1.3...HEAD +[0.1.3]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.1.2...v0.1.3 +[0.1.2]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.1.1...v0.1.2 +[0.1.1]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.1.0...v0.1.1 +[0.1.0]: https://github.com/CosmWasm/serde-json-wasm/tree/v0.1.0 diff --git a/Cargo.lock b/Cargo.lock index f9485c3cc..e86a503ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,7 +26,7 @@ checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" [[package]] name = "serde-json-wasm" -version = "0.1.3" +version = "0.2.0-dev" dependencies = [ "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index b866f1d94..a9ecbdee6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -authors = ["Jorge Aparicio ", "Ethan Frey ", "Ethan Frey Date: Tue, 5 May 2020 11:25:40 +0200 Subject: [PATCH 042/178] Let `from_str` and `from_slice` work on DeserializeOwned --- CHANGELOG.md | 7 +++ src/de/mod.rs | 121 ++++++++++++++++++++------------------------------ 2 files changed, 54 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e217d68df..990488160 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,13 @@ project adheres to [Semantic Versioning](http://semver.org/). ### Changed - Strings are now escaped during serialization (#10) +- `from_str`/`from_slice` now work for `T: DeserializeOwned` instead of + `T: Deserialize<'de>`, making it impossible to deserialize into non-owned + reference fields. This is necessary since string unescaping requires creating + a mutated copy of the source data and only JSON strings without escape + sequences can be deserialized copy-free. The same limitation applies to + serde_json, where the problem shows up at + [runtime instead of compile time](https://github.com/serde-rs/json/issues/530). ## [0.1.3] - 2020-03-12 diff --git a/src/de/mod.rs b/src/de/mod.rs index 3e90f3bc4..7f253c8f7 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -680,9 +680,9 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { } /// Deserializes an instance of type `T` from bytes of JSON text -pub fn from_slice<'a, T>(v: &'a [u8]) -> Result +pub fn from_slice(v: &[u8]) -> Result where - T: de::Deserialize<'a>, + T: de::DeserializeOwned, { let mut de = Deserializer::new(v); let value = de::Deserialize::deserialize(&mut de)?; @@ -692,9 +692,9 @@ where } /// Deserializes an instance of type T from a string of JSON text -pub fn from_str<'a, T>(s: &'a str) -> Result +pub fn from_str(s: &str) -> Result where - T: de::Deserialize<'a>, + T: de::DeserializeOwned, { from_slice(s.as_bytes()) } @@ -800,43 +800,29 @@ mod tests { } #[test] - fn str() { - // simple - assert_eq!(from_str(r#" "hello" "#), Ok("hello")); - assert_eq!(from_str(r#" "" "#), Ok("")); - assert_eq!(from_str(r#" " " "#), Ok(" ")); - assert_eq!(from_str(r#" "👏" "#), Ok("👏")); + fn string() { + assert_eq!(from_str(r#" "hello" "#), Ok("hello".to_string())); + assert_eq!(from_str(r#" "" "#), Ok("".to_string())); + assert_eq!(from_str(r#" " " "#), Ok(" ".to_string())); + assert_eq!(from_str(r#" "👏" "#), Ok("👏".to_string())); // no unescaping is done (as documented as a known issue in lib.rs) - assert_eq!(from_str(r#" "hel\tlo" "#), Ok("hel\\tlo")); - assert_eq!(from_str(r#" "hello \\" "#), Ok("hello \\\\")); - - // escaped " in the string content - assert_eq!(from_str(r#" "foo\"bar" "#), Ok(r#"foo\"bar"#)); - assert_eq!(from_str(r#" "foo\\\"bar" "#), Ok(r#"foo\\\"bar"#)); - assert_eq!(from_str(r#" "foo\"\"bar" "#), Ok(r#"foo\"\"bar"#)); - assert_eq!(from_str(r#" "\"bar" "#), Ok(r#"\"bar"#)); - assert_eq!(from_str(r#" "foo\"" "#), Ok(r#"foo\""#)); - assert_eq!(from_str(r#" "\"" "#), Ok(r#"\""#)); - - // non-excaped " preceded by backslashes - assert_eq!(from_str(r#" "foo bar\\" "#), Ok(r#"foo bar\\"#)); - assert_eq!(from_str(r#" "foo bar\\\\" "#), Ok(r#"foo bar\\\\"#)); - assert_eq!(from_str(r#" "foo bar\\\\\\" "#), Ok(r#"foo bar\\\\\\"#)); - assert_eq!(from_str(r#" "foo bar\\\\\\\\" "#), Ok(r#"foo bar\\\\\\\\"#)); - assert_eq!(from_str(r#" "\\" "#), Ok(r#"\\"#)); - } - - #[test] - fn string() { - assert_eq!(from_str(r#" "hello" "#), Ok(String::from("hello"))); - assert_eq!(from_str(r#" "" "#), Ok(String::from(""))); - assert_eq!(from_str(r#" " " "#), Ok(String::from(" "))); - assert_eq!(from_str(r#" "👏" "#), Ok(String::from("👏"))); + assert_eq!(from_str(r#" "hel\tlo" "#), Ok("hel\\tlo".to_string())); + assert_eq!(from_str(r#" "hello \\" "#), Ok("hello \\\\".to_string())); // escaped " in the string content - // (note: no unescaping is performed, as documented as a known issue in lib.rs) - assert_eq!(from_str(r#" "foo\"bar" "#), Ok(String::from(r#"foo\"bar"#))); + assert_eq!(from_str(r#" "foo\"bar" "#), Ok(r#"foo\"bar"#.to_string())); + assert_eq!(from_str(r#" "foo\\\"ba" "#), Ok(r#"foo\\\"ba"#.to_string())); + assert_eq!(from_str(r#" "foo\"\"ba" "#), Ok(r#"foo\"\"ba"#.to_string())); + assert_eq!(from_str(r#" "\"bar" "#), Ok(r#"\"bar"#.to_string())); + assert_eq!(from_str(r#" "foo\"" "#), Ok(r#"foo\""#.to_string())); + assert_eq!(from_str(r#" "\"" "#), Ok(r#"\""#.to_string())); + + // non-escaped " preceded by backslashes + assert_eq!(from_str(r#" "fooooo\\" "#), Ok(r#"fooooo\\"#.to_string())); + assert_eq!(from_str(r#" "fooo\\\\" "#), Ok(r#"fooo\\\\"#.to_string())); + assert_eq!(from_str(r#" "fo\\\\\\" "#), Ok(r#"fo\\\\\\"#.to_string())); + assert_eq!(from_str(r#" "\\\\\\\\" "#), Ok(r#"\\\\\\\\"#.to_string())); } #[test] @@ -880,15 +866,14 @@ mod tests { #[test] fn struct_option() { #[derive(Debug, Deserialize, PartialEq)] - struct Property<'a> { - #[serde(borrow)] - description: Option<&'a str>, + struct Property { + description: Option, } assert_eq!( from_str(r#"{ "description": "An ambient temperature sensor" }"#), Ok(Property { - description: Some("An ambient temperature sensor"), + description: Some("An ambient temperature sensor".to_string()), }) ); @@ -1159,36 +1144,30 @@ mod tests { #[test] fn wot() { #[derive(Debug, Deserialize, PartialEq)] - struct Thing<'a> { - #[serde(borrow)] - properties: Properties<'a>, + struct Thing { + properties: Properties, #[serde(rename = "type")] ty: Type, } #[derive(Debug, Deserialize, PartialEq)] - struct Properties<'a> { - #[serde(borrow)] - temperature: Property<'a>, - #[serde(borrow)] - humidity: Property<'a>, - #[serde(borrow)] - led: Property<'a>, + struct Properties { + temperature: Property, + humidity: Property, + led: Property, } #[derive(Debug, Deserialize, PartialEq)] - struct Property<'a> { + struct Property { #[serde(rename = "type")] ty: Type, - unit: Option<&'a str>, - #[serde(borrow)] - description: Option<&'a str>, - href: &'a str, - owned: Option, + unit: Option, + description: Option, + href: String, } assert_eq!( - from_str::>( + from_str::( r#" { "type": "thing", @@ -1197,21 +1176,18 @@ mod tests { "type": "number", "unit": "celsius", "description": "An ambient temperature sensor", - "href": "/properties/temperature", - "owned": "own temperature" + "href": "/properties/temperature" }, "humidity": { "type": "number", "unit": "percent", - "href": "/properties/humidity", - "owned": null + "href": "/properties/humidity" }, "led": { "type": "boolean", "unit": null, "description": "A red LED", - "href": "/properties/led", - "owned": "own led" + "href": "/properties/led" } } } @@ -1221,24 +1197,21 @@ mod tests { properties: Properties { temperature: Property { ty: Type::Number, - unit: Some("celsius"), - description: Some("An ambient temperature sensor"), - href: "/properties/temperature", - owned: Some("own temperature".to_string()), + unit: Some("celsius".to_string()), + description: Some("An ambient temperature sensor".to_string()), + href: "/properties/temperature".to_string(), }, humidity: Property { ty: Type::Number, - unit: Some("percent"), + unit: Some("percent".to_string()), description: None, - href: "/properties/humidity", - owned: None, + href: "/properties/humidity".to_string(), }, led: Property { ty: Type::Boolean, unit: None, - description: Some("A red LED"), - href: "/properties/led", - owned: Some("own led".to_string()), + description: Some("A red LED".to_string()), + href: "/properties/led".to_string(), }, }, ty: Type::Thing, From f6bd1e1a479a135ed761b75fdf3a5185661e9fef Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 4 May 2020 23:22:15 +0200 Subject: [PATCH 043/178] Document deserialize_any better --- src/de/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 7f253c8f7..05755fc9b 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -363,7 +363,11 @@ macro_rules! deserialize_signed { impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { type Error = Error; - /// Unsupported. Can’t parse a value without knowing its expected type. + /// Unsupported. We rely on typed deserialization methods, even if a JSON + /// has enough type information to detect types in many cases. + /// + /// See https://serde.rs/impl-deserialize.html to learn more about the differentiation + /// between `deserialize_{type}` and `deserialize_any`. fn deserialize_any(self, _visitor: V) -> Result where V: Visitor<'de>, From 4cae8f91e0c099be9b7fce465617138ddca0807e Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 5 May 2020 11:49:57 +0200 Subject: [PATCH 044/178] Prepare code for unescaping --- src/de/mod.rs | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 05755fc9b..bae4f36a8 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -1,6 +1,6 @@ //! Deserialize JSON data to a Rust data structure -use std::{error, fmt, str::from_utf8}; +use std::{error, fmt}; use serde::de::{self, Visitor}; @@ -132,6 +132,12 @@ impl fmt::Display for Error { } } +fn unescape(source: &[u8]) -> Result { + // TODO: implement unescaping + let string_data = source.to_vec(); + return String::from_utf8(string_data).map_err(|_| Error::InvalidUnicodeCodePoint); +} + /// Deserializer will parse serde-json-wasm flavored JSON into a /// serde-annotated struct pub struct Deserializer<'b> { @@ -219,7 +225,7 @@ impl<'a> Deserializer<'a> { } } - fn parse_str(&mut self) -> Result<&'a str> { + fn parse_string(&mut self) -> Result { let start = self.index; loop { match self.peek() { @@ -252,8 +258,7 @@ impl<'a> Deserializer<'a> { } else { let end = self.index; self.eat_char(); - return from_utf8(&self.slice[start..end]) - .map_err(|_| Error::InvalidUnicodeCodePoint); + return unescape(&self.slice[start..end]); } } Some(_) => self.eat_char(), @@ -477,15 +482,7 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - let peek = self.parse_whitespace().ok_or(Error::EofWhileParsingValue)?; - - match peek { - b'"' => { - self.eat_char(); - visitor.visit_borrowed_str(self.parse_str()?) - } - _ => Err(Error::InvalidType), - } + self.deserialize_string(visitor) } fn deserialize_string(self, visitor: V) -> Result @@ -497,8 +494,7 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { match peek { b'"' => { self.eat_char(); - let str = self.parse_str()?.to_string(); - visitor.visit_string(str) + visitor.visit_string(self.parse_string()?) } _ => Err(Error::InvalidType), } From 991f9274112b3d64ffb41d52c92ee81f16161460 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 5 May 2020 12:25:27 +0200 Subject: [PATCH 045/178] Pull out errors module --- src/de/errors.rs | 122 +++++++++++++++++++++++++++++++++++++++++++ src/de/mod.rs | 131 +++-------------------------------------------- 2 files changed, 128 insertions(+), 125 deletions(-) create mode 100644 src/de/errors.rs diff --git a/src/de/errors.rs b/src/de/errors.rs new file mode 100644 index 000000000..a31ca5483 --- /dev/null +++ b/src/de/errors.rs @@ -0,0 +1,122 @@ +use serde::de; +use std::{error, fmt}; + +/// Deserialization result +pub type Result = core::result::Result; + +/// This type represents all possible errors that can occur when deserializing JSON data +#[derive(Debug, PartialEq)] +pub enum Error { + /// EOF while parsing a list. + EofWhileParsingList, + + /// EOF while parsing an object. + EofWhileParsingObject, + + /// EOF while parsing a string. + EofWhileParsingString, + + /// EOF while parsing a JSON value. + EofWhileParsingValue, + + /// Expected this character to be a `':'`. + ExpectedColon, + + /// Expected this character to be either a `','` or a `']'`. + ExpectedListCommaOrEnd, + + /// Expected this character to be either a `','` or a `'}'`. + ExpectedObjectCommaOrEnd, + + /// Expected to parse either a `true`, `false`, or a `null`. + ExpectedSomeIdent, + + /// Expected this character to start a JSON value. + ExpectedSomeValue, + + /// Invalid number. + InvalidNumber, + + /// Invalid type + InvalidType, + + /// Invalid unicode code point. + InvalidUnicodeCodePoint, + + /// Object key is not a string. + KeyMustBeAString, + + /// JSON has non-whitespace trailing characters after the value. + TrailingCharacters, + + /// JSON has a comma after the last value in an array or map. + TrailingComma, + + /// Custom error message from serde + Custom(String), + + #[doc(hidden)] + __Extensible, +} + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + None + } + + fn description(&self) -> &str { + "(use display)" + } +} + +impl de::Error for Error { + fn custom(msg: T) -> Self + where + T: fmt::Display, + { + Error::Custom(msg.to_string()) + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Error::EofWhileParsingList => "EOF while parsing a list.", + Error::EofWhileParsingObject => "EOF while parsing an object.", + Error::EofWhileParsingString => "EOF while parsing a string.", + Error::EofWhileParsingValue => "EOF while parsing a JSON value.", + Error::ExpectedColon => "Expected this character to be a `':'`.", + Error::ExpectedListCommaOrEnd => { + "Expected this character to be either a `','` or\ + a \ + `']'`." + } + Error::ExpectedObjectCommaOrEnd => { + "Expected this character to be either a `','` \ + or a \ + `'}'`." + } + Error::ExpectedSomeIdent => { + "Expected to parse either a `true`, `false`, or a \ + `null`." + } + Error::ExpectedSomeValue => "Expected this character to start a JSON value.", + Error::InvalidNumber => "Invalid number.", + Error::InvalidType => "Invalid type", + Error::InvalidUnicodeCodePoint => "Invalid unicode code point.", + Error::KeyMustBeAString => "Object key is not a string.", + Error::TrailingCharacters => { + "JSON has non-whitespace trailing characters after \ + the \ + value." + } + Error::TrailingComma => "JSON has a comma after the last value in an array or map.", + Error::Custom(msg) => &msg, + _ => "Invalid JSON", + } + ) + } +} diff --git a/src/de/mod.rs b/src/de/mod.rs index bae4f36a8..e276ee672 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -1,136 +1,17 @@ //! Deserialize JSON data to a Rust data structure -use std::{error, fmt}; - -use serde::de::{self, Visitor}; - -use self::enum_::{StructVariantAccess, UnitVariantAccess}; -use self::map::MapAccess; -use self::seq::SeqAccess; - mod enum_; +mod errors; mod map; mod seq; -/// Deserialization result -pub type Result = core::result::Result; - -/// This type represents all possible errors that can occur when deserializing JSON data -#[derive(Debug, PartialEq)] -pub enum Error { - /// EOF while parsing a list. - EofWhileParsingList, - - /// EOF while parsing an object. - EofWhileParsingObject, - - /// EOF while parsing a string. - EofWhileParsingString, - - /// EOF while parsing a JSON value. - EofWhileParsingValue, - - /// Expected this character to be a `':'`. - ExpectedColon, - - /// Expected this character to be either a `','` or a `']'`. - ExpectedListCommaOrEnd, - - /// Expected this character to be either a `','` or a `'}'`. - ExpectedObjectCommaOrEnd, - - /// Expected to parse either a `true`, `false`, or a `null`. - ExpectedSomeIdent, - - /// Expected this character to start a JSON value. - ExpectedSomeValue, - - /// Invalid number. - InvalidNumber, - - /// Invalid type - InvalidType, - - /// Invalid unicode code point. - InvalidUnicodeCodePoint, - - /// Object key is not a string. - KeyMustBeAString, - - /// JSON has non-whitespace trailing characters after the value. - TrailingCharacters, - - /// JSON has a comma after the last value in an array or map. - TrailingComma, - - /// Custom error message from serde - Custom(String), +pub use errors::{Error, Result}; - #[doc(hidden)] - __Extensible, -} - -impl error::Error for Error { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - None - } - - fn description(&self) -> &str { - "(use display)" - } -} - -impl de::Error for Error { - fn custom(msg: T) -> Self - where - T: fmt::Display, - { - Error::Custom(msg.to_string()) - } -} +use serde::de::{self, Visitor}; -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match self { - Error::EofWhileParsingList => "EOF while parsing a list.", - Error::EofWhileParsingObject => "EOF while parsing an object.", - Error::EofWhileParsingString => "EOF while parsing a string.", - Error::EofWhileParsingValue => "EOF while parsing a JSON value.", - Error::ExpectedColon => "Expected this character to be a `':'`.", - Error::ExpectedListCommaOrEnd => { - "Expected this character to be either a `','` or\ - a \ - `']'`." - } - Error::ExpectedObjectCommaOrEnd => { - "Expected this character to be either a `','` \ - or a \ - `'}'`." - } - Error::ExpectedSomeIdent => { - "Expected to parse either a `true`, `false`, or a \ - `null`." - } - Error::ExpectedSomeValue => "Expected this character to start a JSON value.", - Error::InvalidNumber => "Invalid number.", - Error::InvalidType => "Invalid type", - Error::InvalidUnicodeCodePoint => "Invalid unicode code point.", - Error::KeyMustBeAString => "Object key is not a string.", - Error::TrailingCharacters => { - "JSON has non-whitespace trailing characters after \ - the \ - value." - } - Error::TrailingComma => "JSON has a comma after the last value in an array or map.", - Error::Custom(msg) => &msg, - _ => "Invalid JSON", - } - ) - } -} +use self::enum_::{StructVariantAccess, UnitVariantAccess}; +use self::map::MapAccess; +use self::seq::SeqAccess; fn unescape(source: &[u8]) -> Result { // TODO: implement unescaping From 2d207a67ee559801535aec9c355ea33ee90866c0 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 5 May 2020 12:27:41 +0200 Subject: [PATCH 046/178] Pull out unescape --- src/de/mod.rs | 9 ++------- src/de/unescape.rs | 7 +++++++ 2 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 src/de/unescape.rs diff --git a/src/de/mod.rs b/src/de/mod.rs index e276ee672..24e33d85c 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -4,6 +4,7 @@ mod enum_; mod errors; mod map; mod seq; +mod unescape; pub use errors::{Error, Result}; @@ -13,12 +14,6 @@ use self::enum_::{StructVariantAccess, UnitVariantAccess}; use self::map::MapAccess; use self::seq::SeqAccess; -fn unescape(source: &[u8]) -> Result { - // TODO: implement unescaping - let string_data = source.to_vec(); - return String::from_utf8(string_data).map_err(|_| Error::InvalidUnicodeCodePoint); -} - /// Deserializer will parse serde-json-wasm flavored JSON into a /// serde-annotated struct pub struct Deserializer<'b> { @@ -139,7 +134,7 @@ impl<'a> Deserializer<'a> { } else { let end = self.index; self.eat_char(); - return unescape(&self.slice[start..end]); + return unescape::unescape(&self.slice[start..end]); } } Some(_) => self.eat_char(), diff --git a/src/de/unescape.rs b/src/de/unescape.rs new file mode 100644 index 000000000..041a4125f --- /dev/null +++ b/src/de/unescape.rs @@ -0,0 +1,7 @@ +use super::errors::{Error, Result}; + +pub(crate) fn unescape(source: &[u8]) -> Result { + // TODO: implement unescaping + let string_data = source.to_vec(); + return String::from_utf8(string_data).map_err(|_| Error::InvalidUnicodeCodePoint); +} From 362d5f72dd74f2728e05d474513f1c45303e170e Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 5 May 2020 13:06:36 +0200 Subject: [PATCH 047/178] Implement basic unescaping --- src/de/mod.rs | 26 +++++----- src/de/unescape.rs | 119 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 129 insertions(+), 16 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 24e33d85c..3ce1e6ab0 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -682,23 +682,23 @@ mod tests { assert_eq!(from_str(r#" " " "#), Ok(" ".to_string())); assert_eq!(from_str(r#" "👏" "#), Ok("👏".to_string())); - // no unescaping is done (as documented as a known issue in lib.rs) - assert_eq!(from_str(r#" "hel\tlo" "#), Ok("hel\\tlo".to_string())); - assert_eq!(from_str(r#" "hello \\" "#), Ok("hello \\\\".to_string())); + // Unescapes things + assert_eq!(from_str(r#" "hel\tlo" "#), Ok("hel\tlo".to_string())); + assert_eq!(from_str(r#" "hel\\lo" "#), Ok("hel\\lo".to_string())); // escaped " in the string content - assert_eq!(from_str(r#" "foo\"bar" "#), Ok(r#"foo\"bar"#.to_string())); - assert_eq!(from_str(r#" "foo\\\"ba" "#), Ok(r#"foo\\\"ba"#.to_string())); - assert_eq!(from_str(r#" "foo\"\"ba" "#), Ok(r#"foo\"\"ba"#.to_string())); - assert_eq!(from_str(r#" "\"bar" "#), Ok(r#"\"bar"#.to_string())); - assert_eq!(from_str(r#" "foo\"" "#), Ok(r#"foo\""#.to_string())); - assert_eq!(from_str(r#" "\"" "#), Ok(r#"\""#.to_string())); + assert_eq!(from_str(r#" "foo\"bar" "#), Ok(r#"foo"bar"#.to_string())); + assert_eq!(from_str(r#" "foo\\\"ba" "#), Ok(r#"foo\"ba"#.to_string())); + assert_eq!(from_str(r#" "foo\"\"ba" "#), Ok(r#"foo""ba"#.to_string())); + assert_eq!(from_str(r#" "\"bar" "#), Ok(r#""bar"#.to_string())); + assert_eq!(from_str(r#" "foo\"" "#), Ok(r#"foo""#.to_string())); + assert_eq!(from_str(r#" "\"" "#), Ok(r#"""#.to_string())); // non-escaped " preceded by backslashes - assert_eq!(from_str(r#" "fooooo\\" "#), Ok(r#"fooooo\\"#.to_string())); - assert_eq!(from_str(r#" "fooo\\\\" "#), Ok(r#"fooo\\\\"#.to_string())); - assert_eq!(from_str(r#" "fo\\\\\\" "#), Ok(r#"fo\\\\\\"#.to_string())); - assert_eq!(from_str(r#" "\\\\\\\\" "#), Ok(r#"\\\\\\\\"#.to_string())); + assert_eq!(from_str(r#" "fooooo\\" "#), Ok(r#"fooooo\"#.to_string())); + assert_eq!(from_str(r#" "fooo\\\\" "#), Ok(r#"fooo\\"#.to_string())); + assert_eq!(from_str(r#" "fo\\\\\\" "#), Ok(r#"fo\\\"#.to_string())); + assert_eq!(from_str(r#" "\\\\\\\\" "#), Ok(r#"\\\\"#.to_string())); } #[test] diff --git a/src/de/unescape.rs b/src/de/unescape.rs index 041a4125f..8126ba752 100644 --- a/src/de/unescape.rs +++ b/src/de/unescape.rs @@ -1,7 +1,120 @@ use super::errors::{Error, Result}; +// https://de.wikipedia.org/wiki/American_Standard_Code_for_Information_Interchange#ASCII-Tabelle +static BACKSPACE: u8 = 0x08; // BS +static FORMFEED: u8 = 0x0C; // FF +static LINEFEED: u8 = 0x0A; // LF +static CARRIAGE_RETURN: u8 = 0x0D; // CR +static HORIZONTAL_TAB: u8 = 0x09; // HT + pub(crate) fn unescape(source: &[u8]) -> Result { - // TODO: implement unescaping - let string_data = source.to_vec(); - return String::from_utf8(string_data).map_err(|_| Error::InvalidUnicodeCodePoint); + let mut out: Vec = Vec::with_capacity(source.len()); + + let mut open = false; + for byte in source { + if open { + match byte { + b'"' | b'/' | b'\\' => { + out.push(*byte); + open = false; + } + b'b' => { + out.push(BACKSPACE); + open = false; + } + b'f' => { + out.push(FORMFEED); + open = false; + } + b'n' => { + out.push(LINEFEED); + open = false; + } + b'r' => { + out.push(CARRIAGE_RETURN); + open = false; + } + b't' => { + out.push(HORIZONTAL_TAB); + open = false; + } + _ => panic!("unsupported escape sequence"), + } + } else { + if *byte == b'\\' { + open = true; + } else { + out.push(*byte); + } + } + } + + return String::from_utf8(out).map_err(|_| Error::InvalidUnicodeCodePoint); +} + +#[cfg(test)] +mod tests { + use super::*; + + /// A testing wrapper around unescape + fn ue(source: &[u8]) -> String { + unescape(source).unwrap() + } + + #[test] + fn unescape_works() { + // Unchanged because no unescaping happens + assert_eq!(ue(b""), "".to_string()); + assert_eq!(ue(b"a"), "a".to_string()); + assert_eq!(ue(b"ab"), "ab".to_string()); + assert_eq!(ue(b"abc"), "abc".to_string()); + assert_eq!(ue(b"a/c"), "a/c".to_string()); + assert_eq!(ue(b"\xF0\x9F\x91\x8F"), "👏".to_string()); // U+1F44F + + // even number of backslashes + assert_eq!(ue(br#"\\"#), "\\".to_string()); + assert_eq!(ue(br#"\\\\"#), "\\\\".to_string()); + assert_eq!(ue(br#"\\\\\\"#), "\\\\\\".to_string()); + assert_eq!(ue(br#"\\\\\\\\"#), "\\\\\\\\".to_string()); + + // The 8 short escape sequences \", \\, \/, \b, \f, \n, \r, \t (alone, start, end, middle) + assert_eq!(ue(br#"\""#), "\"".to_string()); + assert_eq!(ue(br#"\\"#), "\\".to_string()); + assert_eq!(ue(br#"\/"#), "/".to_string()); + assert_eq!(ue(br#"\b"#), "\x08".to_string()); + assert_eq!(ue(br#"\f"#), "\x0C".to_string()); + assert_eq!(ue(br#"\n"#), "\n".to_string()); + assert_eq!(ue(br#"\r"#), "\r".to_string()); + assert_eq!(ue(br#"\t"#), "\t".to_string()); + assert_eq!(ue(br#"\"abc"#), "\"abc".to_string()); + assert_eq!(ue(br#"\\abc"#), "\\abc".to_string()); + assert_eq!(ue(br#"\/abc"#), "/abc".to_string()); + assert_eq!(ue(br#"\babc"#), "\x08abc".to_string()); + assert_eq!(ue(br#"\fabc"#), "\x0Cabc".to_string()); + assert_eq!(ue(br#"\nabc"#), "\nabc".to_string()); + assert_eq!(ue(br#"\rabc"#), "\rabc".to_string()); + assert_eq!(ue(br#"\tabc"#), "\tabc".to_string()); + assert_eq!(ue(br#"abc\""#), "abc\"".to_string()); + assert_eq!(ue(br#"abc\\"#), "abc\\".to_string()); + assert_eq!(ue(br#"abc\/"#), "abc/".to_string()); + assert_eq!(ue(br#"abc\b"#), "abc\x08".to_string()); + assert_eq!(ue(br#"abc\f"#), "abc\x0C".to_string()); + assert_eq!(ue(br#"abc\n"#), "abc\n".to_string()); + assert_eq!(ue(br#"abc\r"#), "abc\r".to_string()); + assert_eq!(ue(br#"abc\t"#), "abc\t".to_string()); + assert_eq!(ue(br#"xy\"abc"#), "xy\"abc".to_string()); + assert_eq!(ue(br#"xy\\abc"#), "xy\\abc".to_string()); + assert_eq!(ue(br#"xy\/abc"#), "xy/abc".to_string()); + assert_eq!(ue(br#"xy\babc"#), "xy\x08abc".to_string()); + assert_eq!(ue(br#"xy\fabc"#), "xy\x0Cabc".to_string()); + assert_eq!(ue(br#"xy\nabc"#), "xy\nabc".to_string()); + assert_eq!(ue(br#"xy\rabc"#), "xy\rabc".to_string()); + assert_eq!(ue(br#"xy\tabc"#), "xy\tabc".to_string()); + + // Short escape sequences mixed + assert_eq!(ue(br#" \" \" \" "#), " \" \" \" ".to_string()); + assert_eq!(ue(br#" \t \n \r "#), " \t \n \r ".to_string()); + assert_eq!(ue(br#" \"\"\" "#), " \"\"\" ".to_string()); + assert_eq!(ue(br#" \t\n\r "#), " \t\n\r ".to_string()); + } } From 09d5e508d0891276e2e92996d659cda654512496 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 5 May 2020 13:35:25 +0200 Subject: [PATCH 048/178] Create Error::InvalidEscape --- src/de/errors.rs | 4 ++++ src/de/unescape.rs | 14 +++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/de/errors.rs b/src/de/errors.rs index a31ca5483..c233c7a26 100644 --- a/src/de/errors.rs +++ b/src/de/errors.rs @@ -34,6 +34,9 @@ pub enum Error { /// Expected this character to start a JSON value. ExpectedSomeValue, + /// Invalid escape sequence + InvalidEscape, + /// Invalid number. InvalidNumber, @@ -104,6 +107,7 @@ impl fmt::Display for Error { `null`." } Error::ExpectedSomeValue => "Expected this character to start a JSON value.", + Error::InvalidEscape => "Invalid escape sequence.", Error::InvalidNumber => "Invalid number.", Error::InvalidType => "Invalid type", Error::InvalidUnicodeCodePoint => "Invalid unicode code point.", diff --git a/src/de/unescape.rs b/src/de/unescape.rs index 8126ba752..08ca221a8 100644 --- a/src/de/unescape.rs +++ b/src/de/unescape.rs @@ -38,7 +38,7 @@ pub(crate) fn unescape(source: &[u8]) -> Result { out.push(HORIZONTAL_TAB); open = false; } - _ => panic!("unsupported escape sequence"), + _ => return Err(Error::InvalidEscape), } } else { if *byte == b'\\' { @@ -117,4 +117,16 @@ mod tests { assert_eq!(ue(br#" \"\"\" "#), " \"\"\" ".to_string()); assert_eq!(ue(br#" \t\n\r "#), " \t\n\r ".to_string()); } + + #[test] + fn unescape_fails_for_invalid_escape_sequence() { + assert_eq!(unescape(br#" \ "#), Err(Error::InvalidEscape)); + assert_eq!(unescape(br#" \a "#), Err(Error::InvalidEscape)); + assert_eq!(unescape(br#" \N "#), Err(Error::InvalidEscape)); + assert_eq!(unescape(br#" \- "#), Err(Error::InvalidEscape)); + assert_eq!(unescape(br#" \' "#), Err(Error::InvalidEscape)); + assert_eq!(unescape(br#" \x "#), Err(Error::InvalidEscape)); + assert_eq!(unescape(br#" \x08 "#), Err(Error::InvalidEscape)); // valid in Rust and ES6 but not JSON + assert_eq!(unescape(br#" \u{7A} "#), Err(Error::InvalidEscape)); // valid in ES6 but not JSON + } } From bce3429ff1bbaca933743c438b39636b5f0e8ebc Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 5 May 2020 22:59:02 +0200 Subject: [PATCH 049/178] Decode unicode escape sequences --- src/de/unescape.rs | 100 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/src/de/unescape.rs b/src/de/unescape.rs index 08ca221a8..0246284b1 100644 --- a/src/de/unescape.rs +++ b/src/de/unescape.rs @@ -1,3 +1,5 @@ +use std::convert::TryFrom; + use super::errors::{Error, Result}; // https://de.wikipedia.org/wiki/American_Standard_Code_for_Information_Interchange#ASCII-Tabelle @@ -10,9 +12,35 @@ static HORIZONTAL_TAB: u8 = 0x09; // HT pub(crate) fn unescape(source: &[u8]) -> Result { let mut out: Vec = Vec::with_capacity(source.len()); + let mut encoding_tmp = [0u8; 4]; let mut open = false; + let mut in_unicode = false; + let mut unicode_tmp: Vec = Vec::with_capacity(4); for byte in source { - if open { + if in_unicode { + match byte { + b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F' => { + unicode_tmp.push(*byte); + if unicode_tmp.len() == 4 { + let codepoint = hex_decode( + unicode_tmp[0], + unicode_tmp[1], + unicode_tmp[2], + unicode_tmp[3], + ); + let encoded = match char::try_from(codepoint) { + Ok(c) => c.encode_utf8(&mut encoding_tmp as &mut [u8]), + Err(_) => return Err(Error::InvalidEscape), + }; + out.extend_from_slice(encoded.as_bytes()); + unicode_tmp.clear(); + in_unicode = false; + open = false; + } + } + _ => return Err(Error::InvalidEscape), + } + } else if open { match byte { b'"' | b'/' | b'\\' => { out.push(*byte); @@ -38,6 +66,9 @@ pub(crate) fn unescape(source: &[u8]) -> Result { out.push(HORIZONTAL_TAB); open = false; } + b'u' => { + in_unicode = true; + } _ => return Err(Error::InvalidEscape), } } else { @@ -52,6 +83,26 @@ pub(crate) fn unescape(source: &[u8]) -> Result { return String::from_utf8(out).map_err(|_| Error::InvalidUnicodeCodePoint); } +/// Returns a 16 bit value between 0x0000 and 0xFFFF, i.e. a codepoint +/// in the Basic Multilingual Plane. +fn hex_decode(a: u8, b: u8, c: u8, d: u8) -> u32 { + (hex_decode_4bit(a) as u32) << 12 + | (hex_decode_4bit(b) as u32) << 8 + | (hex_decode_4bit(c) as u32) << 4 + | (hex_decode_4bit(d) as u32) +} + +/// Decodes a single hex character into its numeric value, i.e. maps +/// ASCII '0'-'9' to 0-9, 'A'-'F' to 10-15 and 'a'-'f' to 10-15. +fn hex_decode_4bit(x: u8) -> u8 { + match x { + b'0'..=b'9' => x - 0x30, + b'A'..=b'F' => x - 0x41 + 10, + b'a'..=b'f' => x - 0x61 + 10, + _ => panic!("Non-hex ASCII character found"), + } +} + #[cfg(test)] mod tests { use super::*; @@ -116,6 +167,18 @@ mod tests { assert_eq!(ue(br#" \t \n \r "#), " \t \n \r ".to_string()); assert_eq!(ue(br#" \"\"\" "#), " \"\"\" ".to_string()); assert_eq!(ue(br#" \t\n\r "#), " \t\n\r ".to_string()); + + // Unicode + assert_eq!(ue(br#" \u0001 "#), " \u{0001} ".to_string()); + assert_eq!(ue(br#" \u0010 "#), " \u{0010} ".to_string()); + assert_eq!(ue(br#" \u0100 "#), " \u{0100} ".to_string()); + assert_eq!(ue(br#" \u1000 "#), " \u{1000} ".to_string()); + assert_eq!(ue(br#" \uABCD "#), " \u{abcd} ".to_string()); + assert_eq!(ue(br#" \uabcd "#), " \u{abcd} ".to_string()); + assert_eq!(ue(br#" \uAbCd "#), " \u{abcd} ".to_string()); + assert_eq!(ue(br#" \uABCDefg "#), " \u{abcd}efg ".to_string()); + assert_eq!(ue(br#" \uabcdefg "#), " \u{abcd}efg ".to_string()); + assert_eq!(ue(br#" \uAbCdefg "#), " \u{abcd}efg ".to_string()); } #[test] @@ -127,6 +190,41 @@ mod tests { assert_eq!(unescape(br#" \' "#), Err(Error::InvalidEscape)); assert_eq!(unescape(br#" \x "#), Err(Error::InvalidEscape)); assert_eq!(unescape(br#" \x08 "#), Err(Error::InvalidEscape)); // valid in Rust and ES6 but not JSON + + // unicode assert_eq!(unescape(br#" \u{7A} "#), Err(Error::InvalidEscape)); // valid in ES6 but not JSON + assert_eq!(unescape(br#" \uAAA "#), Err(Error::InvalidEscape)); // too short + assert_eq!(unescape(br#" \uAA "#), Err(Error::InvalidEscape)); // too short + assert_eq!(unescape(br#" \uA "#), Err(Error::InvalidEscape)); // too short + assert_eq!(unescape(br#" \u "#), Err(Error::InvalidEscape)); // too short + assert_eq!(unescape(br#" \u123. "#), Err(Error::InvalidEscape)); // non-hex char + assert_eq!(unescape(br#" \u123g "#), Err(Error::InvalidEscape)); // non-hex char + assert_eq!(unescape(br#" \u123\9 "#), Err(Error::InvalidEscape)); // non-hex char + } + + #[test] + fn unescape_fails_for_surrogates() { + // TODO: implement + assert_eq!(unescape(br#" \uDEAD "#), Err(Error::InvalidEscape)); // surrogate + } + + #[test] + fn hex_decode_works() { + assert_eq!(hex_decode(b'0', b'0', b'0', b'0'), 0x0000); + assert_eq!(hex_decode(b'0', b'0', b'0', b'1'), 0x0001); + assert_eq!(hex_decode(b'0', b'0', b'1', b'0'), 0x0010); + assert_eq!(hex_decode(b'0', b'1', b'0', b'0'), 0x0100); + assert_eq!(hex_decode(b'1', b'0', b'0', b'0'), 0x1000); + assert_eq!(hex_decode(b'1', b'1', b'1', b'1'), 0x1111); + assert_eq!(hex_decode(b'1', b'1', b'1', b'0'), 0x1110); + assert_eq!(hex_decode(b'1', b'1', b'0', b'1'), 0x1101); + assert_eq!(hex_decode(b'1', b'0', b'1', b'1'), 0x1011); + assert_eq!(hex_decode(b'0', b'1', b'1', b'1'), 0x0111); + + assert_eq!(hex_decode(b'2', b'3', b'4', b'5'), 0x2345); + assert_eq!(hex_decode(b'6', b'7', b'8', b'9'), 0x6789); + assert_eq!(hex_decode(b'a', b'b', b'c', b'd'), 0xabcd); + assert_eq!(hex_decode(b'e', b'f', b'A', b'B'), 0xefab); + assert_eq!(hex_decode(b'C', b'D', b'E', b'F'), 0xcdef); } } From 4a0f8f4807799fdf4e2e71c0e9445dffa14e70cc Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 5 May 2020 23:02:11 +0200 Subject: [PATCH 050/178] Add CHANGELOG entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 990488160..1a427dad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ project adheres to [Semantic Versioning](http://semver.org/). sequences can be deserialized copy-free. The same limitation applies to serde_json, where the problem shows up at [runtime instead of compile time](https://github.com/serde-rs/json/issues/530). +- Strings are now unescaped during deserialization (#13) ## [0.1.3] - 2020-03-12 From 0e0fea23b15798d36cee4fa3f6badf4a9e6a94df Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 5 May 2020 23:06:35 +0200 Subject: [PATCH 051/178] Make clippy happy --- src/de/unescape.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/de/unescape.rs b/src/de/unescape.rs index 0246284b1..32bc3f86b 100644 --- a/src/de/unescape.rs +++ b/src/de/unescape.rs @@ -72,6 +72,8 @@ pub(crate) fn unescape(source: &[u8]) -> Result { _ => return Err(Error::InvalidEscape), } } else { + // Default case, not in escape sequence + if *byte == b'\\' { open = true; } else { @@ -80,7 +82,7 @@ pub(crate) fn unescape(source: &[u8]) -> Result { } } - return String::from_utf8(out).map_err(|_| Error::InvalidUnicodeCodePoint); + String::from_utf8(out).map_err(|_| Error::InvalidUnicodeCodePoint) } /// Returns a 16 bit value between 0x0000 and 0xFFFF, i.e. a codepoint From a616ea276eb0b98e50fd4c0472122d37190b0e53 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 6 May 2020 09:33:32 +0200 Subject: [PATCH 052/178] Deny warnings in CI --- .github/workflows/Basic.yml | 4 ++++ src/lib.rs | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/Basic.yml b/.github/workflows/Basic.yml index 5f73c1541..50c77fb60 100644 --- a/.github/workflows/Basic.yml +++ b/.github/workflows/Basic.yml @@ -28,12 +28,16 @@ jobs: args: --locked env: RUST_BACKTRACE: 1 + RUSTFLAGS: "-D warnings" - name: Compile to Wasm uses: actions-rs/cargo@v1 with: command: wasm args: --locked + env: + RUST_BACKTRACE: 1 + RUSTFLAGS: "-D warnings" sanity: name: Sanity diff --git a/src/lib.rs b/src/lib.rs index 781d382b8..de09e1445 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,7 +54,6 @@ #![deny(missing_docs)] #![deny(rust_2018_compatibility)] #![deny(rust_2018_idioms)] -#![deny(warnings)] pub mod de; pub mod ser; From f719290420cc9c6e99f967efe723245319891513 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 6 May 2020 09:47:38 +0200 Subject: [PATCH 053/178] Test unfinished escape sequences --- src/de/unescape.rs | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/de/unescape.rs b/src/de/unescape.rs index 32bc3f86b..5dd36529b 100644 --- a/src/de/unescape.rs +++ b/src/de/unescape.rs @@ -13,7 +13,7 @@ pub(crate) fn unescape(source: &[u8]) -> Result { let mut out: Vec = Vec::with_capacity(source.len()); let mut encoding_tmp = [0u8; 4]; - let mut open = false; + let mut in_escape = false; let mut in_unicode = false; let mut unicode_tmp: Vec = Vec::with_capacity(4); for byte in source { @@ -35,36 +35,36 @@ pub(crate) fn unescape(source: &[u8]) -> Result { out.extend_from_slice(encoded.as_bytes()); unicode_tmp.clear(); in_unicode = false; - open = false; + in_escape = false; } } _ => return Err(Error::InvalidEscape), } - } else if open { + } else if in_escape { match byte { b'"' | b'/' | b'\\' => { out.push(*byte); - open = false; + in_escape = false; } b'b' => { out.push(BACKSPACE); - open = false; + in_escape = false; } b'f' => { out.push(FORMFEED); - open = false; + in_escape = false; } b'n' => { out.push(LINEFEED); - open = false; + in_escape = false; } b'r' => { out.push(CARRIAGE_RETURN); - open = false; + in_escape = false; } b't' => { out.push(HORIZONTAL_TAB); - open = false; + in_escape = false; } b'u' => { in_unicode = true; @@ -75,13 +75,17 @@ pub(crate) fn unescape(source: &[u8]) -> Result { // Default case, not in escape sequence if *byte == b'\\' { - open = true; + in_escape = true; } else { out.push(*byte); } } } + if in_escape { + return Err(Error::InvalidEscape); + } + String::from_utf8(out).map_err(|_| Error::InvalidUnicodeCodePoint) } @@ -202,6 +206,13 @@ mod tests { assert_eq!(unescape(br#" \u123. "#), Err(Error::InvalidEscape)); // non-hex char assert_eq!(unescape(br#" \u123g "#), Err(Error::InvalidEscape)); // non-hex char assert_eq!(unescape(br#" \u123\9 "#), Err(Error::InvalidEscape)); // non-hex char + + // unfinished escape sequences + assert_eq!(unescape(br#" \u123"#), Err(Error::InvalidEscape)); + assert_eq!(unescape(br#" \u12"#), Err(Error::InvalidEscape)); + assert_eq!(unescape(br#" \u1"#), Err(Error::InvalidEscape)); + assert_eq!(unescape(br#" \u"#), Err(Error::InvalidEscape)); + assert_eq!(unescape(br#" \"#), Err(Error::InvalidEscape)); } #[test] From 4bf42b3bd97240b18bcf232f0fbbea408b9ee8ab Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 6 May 2020 10:00:43 +0200 Subject: [PATCH 054/178] Make hex_decode siganture stricter --- src/de/unescape.rs | 64 +++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/de/unescape.rs b/src/de/unescape.rs index 5dd36529b..a47ecf2a7 100644 --- a/src/de/unescape.rs +++ b/src/de/unescape.rs @@ -15,25 +15,25 @@ pub(crate) fn unescape(source: &[u8]) -> Result { let mut encoding_tmp = [0u8; 4]; let mut in_escape = false; let mut in_unicode = false; - let mut unicode_tmp: Vec = Vec::with_capacity(4); + // Temporary storage of the four hex characters of \uACDC + let mut unicode_tmp = [0u8; 4]; + // Position in `unicode_tmp` where the next insertion happens + let mut unicode_tmp_pos: usize = 0; + for byte in source { if in_unicode { match byte { b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F' => { - unicode_tmp.push(*byte); - if unicode_tmp.len() == 4 { - let codepoint = hex_decode( - unicode_tmp[0], - unicode_tmp[1], - unicode_tmp[2], - unicode_tmp[3], - ); - let encoded = match char::try_from(codepoint) { + unicode_tmp[unicode_tmp_pos] = *byte; + unicode_tmp_pos += 1; + if unicode_tmp_pos == 4 { + let codepoint = hex_decode(unicode_tmp); + let encoded = match char::try_from(codepoint as u32) { Ok(c) => c.encode_utf8(&mut encoding_tmp as &mut [u8]), Err(_) => return Err(Error::InvalidEscape), }; out.extend_from_slice(encoded.as_bytes()); - unicode_tmp.clear(); + unicode_tmp_pos = 0; in_unicode = false; in_escape = false; } @@ -91,11 +91,11 @@ pub(crate) fn unescape(source: &[u8]) -> Result { /// Returns a 16 bit value between 0x0000 and 0xFFFF, i.e. a codepoint /// in the Basic Multilingual Plane. -fn hex_decode(a: u8, b: u8, c: u8, d: u8) -> u32 { - (hex_decode_4bit(a) as u32) << 12 - | (hex_decode_4bit(b) as u32) << 8 - | (hex_decode_4bit(c) as u32) << 4 - | (hex_decode_4bit(d) as u32) +fn hex_decode(a: [u8; 4]) -> u16 { + (hex_decode_4bit(a[0]) as u16) << 12 + | (hex_decode_4bit(a[1]) as u16) << 8 + | (hex_decode_4bit(a[2]) as u16) << 4 + | (hex_decode_4bit(a[3]) as u16) } /// Decodes a single hex character into its numeric value, i.e. maps @@ -223,21 +223,21 @@ mod tests { #[test] fn hex_decode_works() { - assert_eq!(hex_decode(b'0', b'0', b'0', b'0'), 0x0000); - assert_eq!(hex_decode(b'0', b'0', b'0', b'1'), 0x0001); - assert_eq!(hex_decode(b'0', b'0', b'1', b'0'), 0x0010); - assert_eq!(hex_decode(b'0', b'1', b'0', b'0'), 0x0100); - assert_eq!(hex_decode(b'1', b'0', b'0', b'0'), 0x1000); - assert_eq!(hex_decode(b'1', b'1', b'1', b'1'), 0x1111); - assert_eq!(hex_decode(b'1', b'1', b'1', b'0'), 0x1110); - assert_eq!(hex_decode(b'1', b'1', b'0', b'1'), 0x1101); - assert_eq!(hex_decode(b'1', b'0', b'1', b'1'), 0x1011); - assert_eq!(hex_decode(b'0', b'1', b'1', b'1'), 0x0111); - - assert_eq!(hex_decode(b'2', b'3', b'4', b'5'), 0x2345); - assert_eq!(hex_decode(b'6', b'7', b'8', b'9'), 0x6789); - assert_eq!(hex_decode(b'a', b'b', b'c', b'd'), 0xabcd); - assert_eq!(hex_decode(b'e', b'f', b'A', b'B'), 0xefab); - assert_eq!(hex_decode(b'C', b'D', b'E', b'F'), 0xcdef); + assert_eq!(hex_decode([b'0', b'0', b'0', b'0']), 0x0000); + assert_eq!(hex_decode([b'0', b'0', b'0', b'1']), 0x0001); + assert_eq!(hex_decode([b'0', b'0', b'1', b'0']), 0x0010); + assert_eq!(hex_decode([b'0', b'1', b'0', b'0']), 0x0100); + assert_eq!(hex_decode([b'1', b'0', b'0', b'0']), 0x1000); + assert_eq!(hex_decode([b'1', b'1', b'1', b'1']), 0x1111); + assert_eq!(hex_decode([b'1', b'1', b'1', b'0']), 0x1110); + assert_eq!(hex_decode([b'1', b'1', b'0', b'1']), 0x1101); + assert_eq!(hex_decode([b'1', b'0', b'1', b'1']), 0x1011); + assert_eq!(hex_decode([b'0', b'1', b'1', b'1']), 0x0111); + + assert_eq!(hex_decode([b'2', b'3', b'4', b'5']), 0x2345); + assert_eq!(hex_decode([b'6', b'7', b'8', b'9']), 0x6789); + assert_eq!(hex_decode([b'a', b'b', b'c', b'd']), 0xabcd); + assert_eq!(hex_decode([b'e', b'f', b'A', b'B']), 0xefab); + assert_eq!(hex_decode([b'C', b'D', b'E', b'F']), 0xcdef); } } From 5b37d5fb7a0d724fcf43833af87d56d06650d06b Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 6 May 2020 11:49:42 +0200 Subject: [PATCH 055/178] Combine surrogate pairs --- src/de/errors.rs | 12 +++++++ src/de/unescape.rs | 84 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 88 insertions(+), 8 deletions(-) diff --git a/src/de/errors.rs b/src/de/errors.rs index c233c7a26..d9fbb4e0d 100644 --- a/src/de/errors.rs +++ b/src/de/errors.rs @@ -22,9 +22,15 @@ pub enum Error { /// Expected this character to be a `':'`. ExpectedColon, + /// Expected a high surrogate (D800–DBFF) but found something else + ExpectedHighSurrogate, + /// Expected this character to be either a `','` or a `']'`. ExpectedListCommaOrEnd, + /// Expected a low surrogate (DC00–DFFF) but found something else + ExpectedLowSurrogate, + /// Expected this character to be either a `','` or a `'}'`. ExpectedObjectCommaOrEnd, @@ -49,6 +55,9 @@ pub enum Error { /// Object key is not a string. KeyMustBeAString, + /// Found a lone surrogate, which can exist in JSON but cannot be encoded to UTF-8 + LoneSurrogateFound, + /// JSON has non-whitespace trailing characters after the value. TrailingCharacters, @@ -92,11 +101,13 @@ impl fmt::Display for Error { Error::EofWhileParsingString => "EOF while parsing a string.", Error::EofWhileParsingValue => "EOF while parsing a JSON value.", Error::ExpectedColon => "Expected this character to be a `':'`.", + Error::ExpectedHighSurrogate => "Expected a high surrogate (D800–DBFF).", Error::ExpectedListCommaOrEnd => { "Expected this character to be either a `','` or\ a \ `']'`." } + Error::ExpectedLowSurrogate => "Expected a low surrogate (DC00–DFFF).", Error::ExpectedObjectCommaOrEnd => { "Expected this character to be either a `','` \ or a \ @@ -112,6 +123,7 @@ impl fmt::Display for Error { Error::InvalidType => "Invalid type", Error::InvalidUnicodeCodePoint => "Invalid unicode code point.", Error::KeyMustBeAString => "Object key is not a string.", + Error::LoneSurrogateFound => "Found a lone surrogate, which can exist in JSON but cannot be encoded to UTF-8.", Error::TrailingCharacters => { "JSON has non-whitespace trailing characters after \ the \ diff --git a/src/de/unescape.rs b/src/de/unescape.rs index a47ecf2a7..926a36f72 100644 --- a/src/de/unescape.rs +++ b/src/de/unescape.rs @@ -9,6 +9,11 @@ static LINEFEED: u8 = 0x0A; // LF static CARRIAGE_RETURN: u8 = 0x0D; // CR static HORIZONTAL_TAB: u8 = 0x09; // HT +static SURROGARES_FIRST: u16 = 0xD800; +static SURROGARES_HIGH_LAST: u16 = 0xDBFF; +static SURROGARES_LOW_FIRST: u16 = 0xDC00; +static SURROGARES_LAST: u16 = 0xDFFF; + pub(crate) fn unescape(source: &[u8]) -> Result { let mut out: Vec = Vec::with_capacity(source.len()); @@ -19,6 +24,7 @@ pub(crate) fn unescape(source: &[u8]) -> Result { let mut unicode_tmp = [0u8; 4]; // Position in `unicode_tmp` where the next insertion happens let mut unicode_tmp_pos: usize = 0; + let mut high_surrogate: Option = None; for byte in source { if in_unicode { @@ -28,11 +34,38 @@ pub(crate) fn unescape(source: &[u8]) -> Result { unicode_tmp_pos += 1; if unicode_tmp_pos == 4 { let codepoint = hex_decode(unicode_tmp); - let encoded = match char::try_from(codepoint as u32) { - Ok(c) => c.encode_utf8(&mut encoding_tmp as &mut [u8]), - Err(_) => return Err(Error::InvalidEscape), - }; - out.extend_from_slice(encoded.as_bytes()); + + if codepoint >= SURROGARES_FIRST && codepoint <= SURROGARES_LAST { + if let Some(high) = high_surrogate { + if codepoint < SURROGARES_LOW_FIRST { + return Err(Error::ExpectedLowSurrogate); + } + let low = codepoint; + + // https://en.wikipedia.org/wiki/Universal_Character_Set_characters#Surrogates + let combined = 0x1_0000 + + (((high - 0xD800) as u32) << 10 | (low - 0xDC00) as u32); + let encoded = match char::try_from(combined) { + Ok(c) => c.encode_utf8(&mut encoding_tmp as &mut [u8]), + Err(_) => return Err(Error::InvalidUnicodeCodePoint), + }; + out.extend_from_slice(encoded.as_bytes()); + + high_surrogate = None; + } else { + if codepoint > SURROGARES_HIGH_LAST { + return Err(Error::ExpectedHighSurrogate); + } + high_surrogate = Some(codepoint); + } + } else { + let encoded = match char::try_from(codepoint as u32) { + Ok(c) => c.encode_utf8(&mut encoding_tmp as &mut [u8]), + Err(_) => return Err(Error::InvalidEscape), + }; + out.extend_from_slice(encoded.as_bytes()); + } + unicode_tmp_pos = 0; in_unicode = false; in_escape = false; @@ -77,6 +110,10 @@ pub(crate) fn unescape(source: &[u8]) -> Result { if *byte == b'\\' { in_escape = true; } else { + if high_surrogate.is_some() { + return Err(Error::LoneSurrogateFound); + } + out.push(*byte); } } @@ -86,6 +123,10 @@ pub(crate) fn unescape(source: &[u8]) -> Result { return Err(Error::InvalidEscape); } + if high_surrogate.is_some() { + return Err(Error::LoneSurrogateFound); + } + String::from_utf8(out).map_err(|_| Error::InvalidUnicodeCodePoint) } @@ -118,6 +159,11 @@ mod tests { unescape(source).unwrap() } + /// A testing wrapper around unescape, expecting error + fn uee(source: &[u8]) -> Error { + unescape(source).unwrap_err() + } + #[test] fn unescape_works() { // Unchanged because no unescaping happens @@ -216,9 +262,31 @@ mod tests { } #[test] - fn unescape_fails_for_surrogates() { - // TODO: implement - assert_eq!(unescape(br#" \uDEAD "#), Err(Error::InvalidEscape)); // surrogate + fn unescape_works_for_surrogate_pairs() { + assert_eq!(ue(br#" \uD83D\uDC4F "#), " \u{1F44F} ".to_string()); + assert_eq!(ue(br#" \uD83D\uDC4F\uD83D\uDC4F "#), " 👏👏 ".to_string()); + + assert_eq!(ue(br#" \uD83E\uDD7A "#), " \u{1F97A} ".to_string()); + assert_eq!(ue(br#" \uD83E\uDD7A\uD83D\uDC4F "#), " 🥺👏 ".to_string()); + } + + #[test] + fn unescape_fails_for_broken_surrogates() { + assert_eq!(uee(br#" \uDEAD "#), Error::ExpectedHighSurrogate); + assert_eq!(uee(br#" \uDC4F\uD83D "#), Error::ExpectedHighSurrogate); // Clapping hands reversed + + assert_eq!(uee(br#" \uD800\uD800 "#), Error::ExpectedLowSurrogate); + } + + #[test] + fn unescape_fails_for_lone_surrogates() { + assert_eq!(uee(br#" \uD83Dabc "#), Error::LoneSurrogateFound); + assert_eq!(uee(br#" \uD83D"#), Error::LoneSurrogateFound); + + // high surrogate followed by non-surrogate + assert_eq!(uee(br#" \uD800\u0001 "#), Error::LoneSurrogateFound); + assert_eq!(uee(br#" \uD800\uD799 "#), Error::LoneSurrogateFound); + assert_eq!(uee(br#" \uD800\uE000 "#), Error::LoneSurrogateFound); } #[test] From e6782187115322ed6e70fc497cf5a86c187a89be Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 6 May 2020 12:11:21 +0200 Subject: [PATCH 056/178] Add roundtrip test --- src/lib.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index de09e1445..60b9072c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,3 +62,65 @@ pub mod ser; pub use self::de::{from_slice, from_str}; #[doc(inline)] pub use self::ser::{to_string, to_vec}; + +#[cfg(test)] +mod test { + use super::*; + use serde_derive::{Deserialize, Serialize}; + + #[derive(Debug, Deserialize, Serialize, PartialEq)] + enum Model { + Comment, + Post { category: String, author: String }, + } + + #[derive(Debug, Deserialize, Serialize, PartialEq)] + struct Stats { + views: u64, + score: i64, + } + + #[derive(Debug, Deserialize, Serialize, PartialEq)] + struct Item { + model: Model, + title: String, + content: Option, + list: Vec, + published: bool, + stats: Stats, + } + + #[test] + fn can_serde() { + let min = Item { + model: Model::Comment, + title: "".to_string(), + content: None, + list: vec![], + published: false, + stats: Stats { views: 0, score: 0 }, + }; + let max = Item { + model: Model::Post { + category: "fun".to_string(), + author: "sunnyboy85".to_string(), + }, + title: "Nice message".to_string(), + content: Some("Happy \"blogging\" 👏\n\n\tCheers, I'm out\0\0\0".to_string()), + list: vec![0, 1, 2, 3, 42, 154841, std::u32::MAX], + published: true, + stats: Stats { + views: std::u64::MAX, + score: std::i64::MIN, + }, + }; + + // binary + assert_eq!(from_slice::(&to_vec(&min).unwrap()).unwrap(), min); + assert_eq!(from_slice::(&to_vec(&max).unwrap()).unwrap(), max); + + // string + assert_eq!(from_str::(&to_string(&min).unwrap()).unwrap(), min); + assert_eq!(from_str::(&to_string(&max).unwrap()).unwrap(), max); + } +} From c5a1a05e170e013761fb4b5baf3f4b29a7cb1a66 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 6 May 2020 13:39:32 +0200 Subject: [PATCH 057/178] Return error when control character was found --- src/de/errors.rs | 4 ++++ src/de/unescape.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/de/errors.rs b/src/de/errors.rs index d9fbb4e0d..59102380f 100644 --- a/src/de/errors.rs +++ b/src/de/errors.rs @@ -7,6 +7,9 @@ pub type Result = core::result::Result; /// This type represents all possible errors that can occur when deserializing JSON data #[derive(Debug, PartialEq)] pub enum Error { + /// Control character (U+0000 to U+001F) found in string. Those must always be escaped. + ControlCharacterInString, + /// EOF while parsing a list. EofWhileParsingList, @@ -96,6 +99,7 @@ impl fmt::Display for Error { f, "{}", match self { + Error::ControlCharacterInString => "Control character found in string.", Error::EofWhileParsingList => "EOF while parsing a list.", Error::EofWhileParsingObject => "EOF while parsing an object.", Error::EofWhileParsingString => "EOF while parsing a string.", diff --git a/src/de/unescape.rs b/src/de/unescape.rs index 926a36f72..079d438ba 100644 --- a/src/de/unescape.rs +++ b/src/de/unescape.rs @@ -27,6 +27,10 @@ pub(crate) fn unescape(source: &[u8]) -> Result { let mut high_surrogate: Option = None; for byte in source { + if *byte <= 0x1F { + return Err(Error::ControlCharacterInString); + } + if in_unicode { match byte { b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F' => { @@ -172,6 +176,7 @@ mod tests { assert_eq!(ue(b"ab"), "ab".to_string()); assert_eq!(ue(b"abc"), "abc".to_string()); assert_eq!(ue(b"a/c"), "a/c".to_string()); + assert_eq!(ue(b"\x20"), " ".to_string()); // first non-control character assert_eq!(ue(b"\xF0\x9F\x91\x8F"), "👏".to_string()); // U+1F44F // even number of backslashes @@ -233,6 +238,42 @@ mod tests { assert_eq!(ue(br#" \uAbCdefg "#), " \u{abcd}efg ".to_string()); } + #[test] + fn unescape_fails_for_control_characters() { + assert_eq!(unescape(b" \x00 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x01 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x02 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x03 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x04 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x05 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x06 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x07 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x08 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x09 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x0a "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x0b "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x0c "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x0d "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x0e "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x0f "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x10 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x11 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x12 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x13 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x14 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x15 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x16 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x17 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x18 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x19 "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x1a "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x1b "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x1c "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x1d "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x1e "), Err(Error::ControlCharacterInString)); + assert_eq!(unescape(b" \x1f "), Err(Error::ControlCharacterInString)); + } + #[test] fn unescape_fails_for_invalid_escape_sequence() { assert_eq!(unescape(br#" \ "#), Err(Error::InvalidEscape)); From 6ae8f1bdd8feec671879d5b435e50dcc4d4fc3b0 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 6 May 2020 14:58:15 +0200 Subject: [PATCH 058/178] Use to_string in tests --- src/ser/mod.rs | 78 ++++++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index cc640a27f..4f550676b 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -495,16 +495,17 @@ impl ser::SerializeStructVariant for Unreachable { #[cfg(test)] mod tests { + use super::to_string; use serde_derive::Serialize; #[test] fn array() { - assert_eq!(&*crate::to_string(&[0, 1, 2]).unwrap(), "[0,1,2]"); + assert_eq!(to_string(&[0, 1, 2]).unwrap(), "[0,1,2]"); } #[test] fn bool() { - assert_eq!(&*crate::to_string(&true).unwrap(), "true"); + assert_eq!(to_string(&true).unwrap(), "true"); } #[test] @@ -517,41 +518,41 @@ mod tests { Number, } - assert_eq!(&*crate::to_string(&Type::Boolean).unwrap(), r#""boolean""#); + assert_eq!(to_string(&Type::Boolean).unwrap(), r#""boolean""#); - assert_eq!(&*crate::to_string(&Type::Number).unwrap(), r#""number""#); + assert_eq!(to_string(&Type::Number).unwrap(), r#""number""#); } #[test] fn str() { - assert_eq!(&*crate::to_string("hello").unwrap(), r#""hello""#); - assert_eq!(&*crate::to_string("").unwrap(), r#""""#); + assert_eq!(to_string("hello").unwrap(), r#""hello""#); + assert_eq!(to_string("").unwrap(), r#""""#); // Characters unescaped if possible - assert_eq!(&*crate::to_string("ä").unwrap(), r#""ä""#); - assert_eq!(&*crate::to_string("৬").unwrap(), r#""৬""#); - assert_eq!(&*crate::to_string("\u{A0}").unwrap(), r#"" ""#); // non-breaking space - assert_eq!(&*crate::to_string("ℝ").unwrap(), r#""ℝ""#); // 3 byte character - assert_eq!(&*crate::to_string("💣").unwrap(), r#""💣""#); // 4 byte character + assert_eq!(to_string("ä").unwrap(), r#""ä""#); + assert_eq!(to_string("৬").unwrap(), r#""৬""#); + assert_eq!(to_string("\u{A0}").unwrap(), r#"" ""#); // non-breaking space + assert_eq!(to_string("ℝ").unwrap(), r#""ℝ""#); // 3 byte character + assert_eq!(to_string("💣").unwrap(), r#""💣""#); // 4 byte character // " and \ must be escaped - assert_eq!(&*crate::to_string("foo\"bar").unwrap(), r#""foo\"bar""#); - assert_eq!(&*crate::to_string("foo\\bar").unwrap(), r#""foo\\bar""#); + assert_eq!(to_string("foo\"bar").unwrap(), r#""foo\"bar""#); + assert_eq!(to_string("foo\\bar").unwrap(), r#""foo\\bar""#); // \b, \t, \n, \f, \r must be escaped in their two-character escaping - assert_eq!(&*crate::to_string(" \u{0008} ").unwrap(), r#"" \b ""#); - assert_eq!(&*crate::to_string(" \u{0009} ").unwrap(), r#"" \t ""#); - assert_eq!(&*crate::to_string(" \u{000A} ").unwrap(), r#"" \n ""#); - assert_eq!(&*crate::to_string(" \u{000C} ").unwrap(), r#"" \f ""#); - assert_eq!(&*crate::to_string(" \u{000D} ").unwrap(), r#"" \r ""#); + assert_eq!(to_string(" \u{0008} ").unwrap(), r#"" \b ""#); + assert_eq!(to_string(" \u{0009} ").unwrap(), r#"" \t ""#); + assert_eq!(to_string(" \u{000A} ").unwrap(), r#"" \n ""#); + assert_eq!(to_string(" \u{000C} ").unwrap(), r#"" \f ""#); + assert_eq!(to_string(" \u{000D} ").unwrap(), r#"" \r ""#); // U+0000 through U+001F is escaped using six-character \u00xx uppercase hexadecimal escape sequences - assert_eq!(&*crate::to_string(" \u{0000} ").unwrap(), r#"" \u0000 ""#); - assert_eq!(&*crate::to_string(" \u{0001} ").unwrap(), r#"" \u0001 ""#); - assert_eq!(&*crate::to_string(" \u{0007} ").unwrap(), r#"" \u0007 ""#); - assert_eq!(&*crate::to_string(" \u{000e} ").unwrap(), r#"" \u000E ""#); - assert_eq!(&*crate::to_string(" \u{001D} ").unwrap(), r#"" \u001D ""#); - assert_eq!(&*crate::to_string(" \u{001f} ").unwrap(), r#"" \u001F ""#); + assert_eq!(to_string(" \u{0000} ").unwrap(), r#"" \u0000 ""#); + assert_eq!(to_string(" \u{0001} ").unwrap(), r#"" \u0001 ""#); + assert_eq!(to_string(" \u{0007} ").unwrap(), r#"" \u0007 ""#); + assert_eq!(to_string(" \u{000e} ").unwrap(), r#"" \u000E ""#); + assert_eq!(to_string(" \u{001D} ").unwrap(), r#"" \u001D ""#); + assert_eq!(to_string(" \u{001f} ").unwrap(), r#"" \u001F ""#); } #[test] @@ -561,10 +562,7 @@ mod tests { led: bool, } - assert_eq!( - &*crate::to_string(&Led { led: true }).unwrap(), - r#"{"led":true}"# - ); + assert_eq!(to_string(&Led { led: true }).unwrap(), r#"{"led":true}"#); } #[test] @@ -575,22 +573,22 @@ mod tests { } assert_eq!( - &*crate::to_string(&Temperature { temperature: 127 }).unwrap(), + to_string(&Temperature { temperature: 127 }).unwrap(), r#"{"temperature":127}"# ); assert_eq!( - &*crate::to_string(&Temperature { temperature: 20 }).unwrap(), + to_string(&Temperature { temperature: 20 }).unwrap(), r#"{"temperature":20}"# ); assert_eq!( - &*crate::to_string(&Temperature { temperature: -17 }).unwrap(), + to_string(&Temperature { temperature: -17 }).unwrap(), r#"{"temperature":-17}"# ); assert_eq!( - &*crate::to_string(&Temperature { temperature: -128 }).unwrap(), + to_string(&Temperature { temperature: -128 }).unwrap(), r#"{"temperature":-128}"# ); } @@ -603,7 +601,7 @@ mod tests { } assert_eq!( - crate::to_string(&Property { + to_string(&Property { description: Some("An ambient temperature sensor"), }) .unwrap(), @@ -612,7 +610,7 @@ mod tests { // XXX Ideally this should produce "{}" assert_eq!( - crate::to_string(&Property { description: None }).unwrap(), + to_string(&Property { description: None }).unwrap(), r#"{"description":null}"# ); } @@ -625,7 +623,7 @@ mod tests { } assert_eq!( - &*crate::to_string(&Temperature { temperature: 20 }).unwrap(), + to_string(&Temperature { temperature: 20 }).unwrap(), r#"{"temperature":20}"# ); } @@ -635,7 +633,7 @@ mod tests { #[derive(Serialize)] struct Empty {} - assert_eq!(&*crate::to_string(&Empty {}).unwrap(), r#"{}"#); + assert_eq!(to_string(&Empty {}).unwrap(), r#"{}"#); #[derive(Serialize)] struct Tuple { @@ -644,7 +642,7 @@ mod tests { } assert_eq!( - &*crate::to_string(&Tuple { a: true, b: false }).unwrap(), + to_string(&Tuple { a: true, b: false }).unwrap(), r#"{"a":true,"b":false}"# ); } @@ -668,7 +666,7 @@ mod tests { } let err_input = MyResult::Err("some error".to_string()); - let json = crate::to_string(&err_input).expect("encode err enum"); + let json = to_string(&err_input).expect("encode err enum"); assert_eq!(json, r#"{"err":"some error"}"#.to_string()); let loaded = crate::from_str(&json).expect("re-load err enum"); assert_eq!(err_input, loaded); @@ -678,7 +676,7 @@ mod tests { count: 137, list: Vec::new(), }); - let json = crate::to_string(&empty_list).expect("encode ok enum"); + let json = to_string(&empty_list).expect("encode ok enum"); assert_eq!( json, r#"{"ok":{"log":"log message","count":137,"list":[]}}"#.to_string() @@ -691,7 +689,7 @@ mod tests { count: 137, list: vec![18u32, 34, 12], }); - let json = crate::to_string(&full_list).expect("encode ok enum"); + let json = to_string(&full_list).expect("encode ok enum"); assert_eq!( json, r#"{"ok":{"log":null,"count":137,"list":[18,34,12]}}"#.to_string() From ef9b9e8a2eb82d91e4ee2e1207fc6f56f39637cf Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 6 May 2020 15:36:20 +0200 Subject: [PATCH 059/178] Improve bool any array serialization tests --- src/ser/mod.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 4f550676b..cfad2ea8d 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -499,13 +499,15 @@ mod tests { use serde_derive::Serialize; #[test] - fn array() { - assert_eq!(to_string(&[0, 1, 2]).unwrap(), "[0,1,2]"); + fn bool() { + assert_eq!(to_string(&true).unwrap(), "true"); + assert_eq!(to_string(&false).unwrap(), "false"); } #[test] - fn bool() { - assert_eq!(to_string(&true).unwrap(), "true"); + fn array() { + assert_eq!(to_string::<[u8]>(&[]).unwrap(), "[]"); + assert_eq!(to_string(&[0, 1, 2]).unwrap(), "[0,1,2]"); } #[test] From 3442e651772adf07eb4a025ae75f08d2fb96c78c Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 6 May 2020 15:37:10 +0200 Subject: [PATCH 060/178] Add support for serialization errors from serde --- src/ser/mod.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index cfad2ea8d..d350fe671 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -20,6 +20,10 @@ pub type Result = ::core::result::Result; pub enum Error { /// Buffer is full BufferFull, + + /// Custom error message from serde + Custom(String), + #[doc(hidden)] __Extensible, } @@ -48,7 +52,11 @@ impl error::Error for Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Buffer is full") + match self { + Error::BufferFull => write!(f, "Buffer is full"), + Error::Custom(msg) => write!(f, "{}", &msg), + _ => write!(f, "Unknown serialization error"), + } } } @@ -416,11 +424,11 @@ where } impl ser::Error for Error { - fn custom(_msg: T) -> Self + fn custom(msg: T) -> Self where T: fmt::Display, { - unreachable!() + Error::Custom(msg.to_string()) } } From a4c2cf648abb895bfaefe4b68100060455aef055 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 6 May 2020 15:41:36 +0200 Subject: [PATCH 061/178] Test serialization of numbers --- src/ser/mod.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 8 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index d350fe671..9a2b7ed16 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -170,42 +170,42 @@ impl<'a> ser::Serializer for &'a mut Serializer { } fn serialize_i8(self, v: i8) -> Result { - // "-128" + // -128 serialize_signed!(self, 4, v, i8, u8) } fn serialize_i16(self, v: i16) -> Result { - // "-32768" + // -32768 serialize_signed!(self, 6, v, i16, u16) } fn serialize_i32(self, v: i32) -> Result { - // "-2147483648" + // -2147483648 serialize_signed!(self, 11, v, i32, u32) } fn serialize_i64(self, v: i64) -> Result { - // "-9223372036854775808" + // -9223372036854775808 serialize_signed!(self, 20, v, i64, u64) } fn serialize_u8(self, v: u8) -> Result { - // "255" + // 255 serialize_unsigned!(self, 3, v) } fn serialize_u16(self, v: u16) -> Result { - // "65535" + // 65535 serialize_unsigned!(self, 5, v) } fn serialize_u32(self, v: u32) -> Result { - // "4294967295" + // 4294967295 serialize_unsigned!(self, 10, v) } fn serialize_u64(self, v: u64) -> Result { - // "18446744073709551615" + // 18446744073709551615 serialize_unsigned!(self, 20, v) } @@ -512,6 +512,84 @@ mod tests { assert_eq!(to_string(&false).unwrap(), "false"); } + #[test] + fn number() { + assert_eq!(to_string::(&0).unwrap(), "0"); + assert_eq!(to_string::(&1).unwrap(), "1"); + assert_eq!(to_string::(&std::u8::MAX).unwrap(), "255"); + + assert_eq!(to_string::(&0).unwrap(), "0"); + assert_eq!(to_string::(&1).unwrap(), "1"); + assert_eq!(to_string::(&127).unwrap(), "127"); + assert_eq!(to_string::(&-1).unwrap(), "-1"); + assert_eq!(to_string::(&std::i8::MIN).unwrap(), "-128"); + + assert_eq!(to_string::(&0).unwrap(), "0"); + assert_eq!(to_string::(&1).unwrap(), "1"); + assert_eq!(to_string::(&550).unwrap(), "550"); + assert_eq!(to_string::(&std::u16::MAX).unwrap(), "65535"); + + assert_eq!(to_string::(&0).unwrap(), "0"); + assert_eq!(to_string::(&1).unwrap(), "1"); + assert_eq!(to_string::(&550).unwrap(), "550"); + assert_eq!(to_string::(&std::i16::MAX).unwrap(), "32767"); + assert_eq!(to_string::(&-1).unwrap(), "-1"); + assert_eq!(to_string::(&std::i16::MIN).unwrap(), "-32768"); + + assert_eq!(to_string::(&0).unwrap(), "0"); + assert_eq!(to_string::(&1).unwrap(), "1"); + assert_eq!(to_string::(&456789).unwrap(), "456789"); + assert_eq!(to_string::(&std::u32::MAX).unwrap(), "4294967295"); + + assert_eq!(to_string::(&0).unwrap(), "0"); + assert_eq!(to_string::(&1).unwrap(), "1"); + assert_eq!(to_string::(&456789).unwrap(), "456789"); + assert_eq!(to_string::(&std::i32::MAX).unwrap(), "2147483647"); + assert_eq!(to_string::(&-1).unwrap(), "-1"); + assert_eq!(to_string::(&std::i32::MIN).unwrap(), "-2147483648"); + + assert_eq!(to_string::(&0).unwrap(), "0"); + assert_eq!(to_string::(&1).unwrap(), "1"); + assert_eq!(to_string::(&456789).unwrap(), "456789"); + assert_eq!(to_string::(&4294967295).unwrap(), "4294967295"); + assert_eq!(to_string::(&4294967296).unwrap(), "4294967296"); + assert_eq!( + to_string::(&9007199254740991).unwrap(), + "9007199254740991" + ); // Number.MAX_SAFE_INTEGER + assert_eq!( + to_string::(&9007199254740992).unwrap(), + "9007199254740992" + ); // Number.MAX_SAFE_INTEGER+1 + assert_eq!( + to_string::(&std::u64::MAX).unwrap(), + "18446744073709551615" + ); + + assert_eq!(to_string::(&0).unwrap(), "0"); + assert_eq!(to_string::(&1).unwrap(), "1"); + assert_eq!(to_string::(&456789).unwrap(), "456789"); + assert_eq!(to_string::(&4294967295).unwrap(), "4294967295"); + assert_eq!(to_string::(&4294967296).unwrap(), "4294967296"); + assert_eq!( + to_string::(&9007199254740991).unwrap(), + "9007199254740991" + ); // Number.MAX_SAFE_INTEGER + assert_eq!( + to_string::(&9007199254740992).unwrap(), + "9007199254740992" + ); // Number.MAX_SAFE_INTEGER+1 + assert_eq!( + to_string::(&std::i64::MAX).unwrap(), + "9223372036854775807" + ); + assert_eq!(to_string::(&-1).unwrap(), "-1"); + assert_eq!( + to_string::(&std::i64::MIN).unwrap(), + "-9223372036854775808" + ); + } + #[test] fn array() { assert_eq!(to_string::<[u8]>(&[]).unwrap(), "[]"); From b40c50846557befafb11a6ce54fb7c9e851bd55d Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 6 May 2020 16:26:55 +0200 Subject: [PATCH 062/178] Improve newtype testing --- src/de/mod.rs | 36 ++++++++++++++++++++++++++---------- src/lib.rs | 13 +++++++++++-- src/ser/mod.rs | 14 ++++++++++++++ 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 3ce1e6ab0..9146c4681 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -852,18 +852,34 @@ mod tests { ); } - #[derive(Deserialize, Clone, Debug, PartialEq)] - pub struct Address(pub String); - - #[derive(Deserialize, Clone, Debug, PartialEq)] - pub struct NewtypeDemo { - pub address: Address, - } - #[test] fn newtypes() { - let c: NewtypeDemo = from_str(r#"{"address": "johnny"}"#).unwrap(); - assert_eq!(Address("johnny".to_string()), c.address); + #[derive(Deserialize, Debug, PartialEq)] + struct Address(String); + + #[derive(Deserialize, Debug, PartialEq)] + struct CommentId(u32); + + #[derive(Deserialize, Debug, PartialEq)] + struct NewtypeDemo { + address: Address, + comment: CommentId, + } + + let element: Address = from_str(r#""johnny""#).unwrap(); + assert_eq!(element, Address("johnny".to_string())); + + let element: CommentId = from_str(r#"5464813"#).unwrap(); + assert_eq!(element, CommentId(5464813)); + + let element: NewtypeDemo = from_str(r#"{"address": "johnny", "comment": 9897}"#).unwrap(); + assert_eq!( + element, + NewtypeDemo { + address: Address("johnny".to_string()), + comment: CommentId(9897), + } + ); } #[test] diff --git a/src/lib.rs b/src/lib.rs index 60b9072c0..33ad0c0bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,10 +68,16 @@ mod test { use super::*; use serde_derive::{Deserialize, Serialize}; + #[derive(Debug, Deserialize, Serialize, PartialEq)] + struct Address(String); + + #[derive(Debug, Deserialize, Serialize, PartialEq)] + struct CommentId(u32); + #[derive(Debug, Deserialize, Serialize, PartialEq)] enum Model { Comment, - Post { category: String, author: String }, + Post { category: String, author: Address }, } #[derive(Debug, Deserialize, Serialize, PartialEq)] @@ -87,6 +93,7 @@ mod test { content: Option, list: Vec, published: bool, + comments: Vec, stats: Stats, } @@ -98,17 +105,19 @@ mod test { content: None, list: vec![], published: false, + comments: vec![], stats: Stats { views: 0, score: 0 }, }; let max = Item { model: Model::Post { category: "fun".to_string(), - author: "sunnyboy85".to_string(), + author: Address("sunnyboy85".to_string()), }, title: "Nice message".to_string(), content: Some("Happy \"blogging\" 👏\n\n\tCheers, I'm out\0\0\0".to_string()), list: vec![0, 1, 2, 3, 42, 154841, std::u32::MAX], published: true, + comments: vec![CommentId(2), CommentId(700)], stats: Stats { views: std::u64::MAX, score: std::i64::MIN, diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 9a2b7ed16..8b854cc9f 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -643,6 +643,20 @@ mod tests { assert_eq!(to_string(" \u{001f} ").unwrap(), r#"" \u001F ""#); } + #[test] + fn newtype() { + #[derive(Serialize)] + struct Address(String); + #[derive(Serialize)] + struct CommentId(u32); + + assert_eq!( + to_string(&Address("home".to_string())).unwrap(), + r#""home""# + ); + assert_eq!(to_string(&CommentId(42)).unwrap(), r#"42"#); + } + #[test] fn struct_bool() { #[derive(Serialize)] From ba834ebccb3d13c491db42fef6691a3c0b532806 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 6 May 2020 20:59:34 +0200 Subject: [PATCH 063/178] Set version: 0.2.0 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e86a503ca..fe05ce854 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,7 +26,7 @@ checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" [[package]] name = "serde-json-wasm" -version = "0.2.0-dev" +version = "0.2.0" dependencies = [ "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index a9ecbdee6..5cb481cb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/CosmWasm/serde-json-wasm" -version = "0.2.0-dev" +version = "0.2.0" exclude = ["ci/*", ".gitignore", ".cargo", ".travis.yml"] [dependencies] From 52f660b4edbb8a7aee340f2456a13095851c0059 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 7 May 2020 08:29:43 +0200 Subject: [PATCH 064/178] Fix email addresses --- Cargo.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5cb481cb8..7c956a407 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,9 @@ [package] -authors = ["Jorge Aparicio ", "Ethan Frey ", + "Ethan Frey ", + "Simon Warta ", +] categories = ["wasm"] description = "serde-json for wasm programs" documentation = "https://docs.rs/serde-json-wasm" From ccb15e3f8472aa134b48568594c56ff514856bde Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 7 May 2020 08:31:13 +0200 Subject: [PATCH 065/178] Remove unused Travis config --- .travis.yml | 47 --------------------------------------------- Cargo.toml | 2 +- ci/after_success.sh | 24 ----------------------- ci/install.sh | 11 ----------- ci/script.sh | 29 ---------------------------- 5 files changed, 1 insertion(+), 112 deletions(-) delete mode 100644 .travis.yml delete mode 100644 ci/after_success.sh delete mode 100644 ci/install.sh delete mode 100644 ci/script.sh diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f5a967776..000000000 --- a/.travis.yml +++ /dev/null @@ -1,47 +0,0 @@ -language: rust -rust: stable - -matrix: - include: - - env: TARGET=x86_64-unknown-linux-gnu - - # MSRV - - env: TARGET=x86_64-unknown-linux-gnu - rust: 1.31.0 - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - # no-std sanity check - - env: TARGET=thumbv7m-none-eabi - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - - env: TARGET=rustfmt - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - -before_install: set -e - -install: - - bash ci/install.sh - -script: - - bash ci/script.sh - -after_script: set +e - -after_success: - - bash ci/after_success.sh - -cache: cargo - -before_cache: - # Travis can't cache files that are not readable by "others" - - chmod -R a+r $HOME/.cargo - -branches: - only: - - master - - staging - - trying - -notifications: - email: - on_success: never diff --git a/Cargo.toml b/Cargo.toml index 7c956a407..72167cd08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/CosmWasm/serde-json-wasm" version = "0.2.0" -exclude = ["ci/*", ".gitignore", ".cargo", ".travis.yml"] +exclude = [".gitignore", ".cargo"] [dependencies] serde = { version = "^1.0.80", default-features = false, features = ["alloc"] } diff --git a/ci/after_success.sh b/ci/after_success.sh deleted file mode 100644 index db795674e..000000000 --- a/ci/after_success.sh +++ /dev/null @@ -1,24 +0,0 @@ -set -euxo pipefail - -main() { - if [ $TARGET = thumbv7m-none-eabi ]; then - return - fi - - cargo doc - - mkdir ghp-import - - curl -Ls https://github.com/davisp/ghp-import/archive/master.tar.gz | \ - tar --strip-components 1 -C ghp-import -xz - - ./ghp-import/ghp_import.py target/doc - - set +x - git push -fq https://$GH_TOKEN@github.com/$TRAVIS_REPO_SLUG.git gh-pages && \ - echo OK -} - -if [ $TRAVIS_BRANCH = master ]; then - main -fi diff --git a/ci/install.sh b/ci/install.sh deleted file mode 100644 index e88d3ac61..000000000 --- a/ci/install.sh +++ /dev/null @@ -1,11 +0,0 @@ -set -euxo pipefail - -main() { - if [ $TARGET != rustfmt ]; then - rustup target add $TARGET - else - rustup component add rustfmt - fi -} - -main diff --git a/ci/script.sh b/ci/script.sh deleted file mode 100644 index f3117384a..000000000 --- a/ci/script.sh +++ /dev/null @@ -1,29 +0,0 @@ -set -euxo pipefail - -main() { - if [ $TARGET = rustfmt ]; then - cargo fmt -- --check - return - fi - - cargo check --target $TARGET - - if [ $TARGET = x86_64-unknown-linux-gnu ]; then - cargo test --target $TARGET - - return - fi -} - -# fake Travis variables to be able to run this on a local machine -if [ -z ${TRAVIS_BRANCH-} ]; then - TRAVIS_BRANCH=auto -fi - -if [ -z ${TARGET-} ]; then - TARGET=$(rustc -Vv | grep host | cut -d ' ' -f2) -fi - -if [ $TRAVIS_BRANCH != master ] || [ $TRAVIS_PULL_REQUEST != false ]; then - main -fi From cb35b9efb7154557f348a764037c09562bebec9d Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 7 May 2020 08:31:57 +0200 Subject: [PATCH 066/178] Exclude .github/ --- Cargo.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 72167cd08..718caca73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,11 @@ name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/CosmWasm/serde-json-wasm" version = "0.2.0" -exclude = [".gitignore", ".cargo"] +exclude = [ + ".cargo/", + ".github/", + ".gitignore", +] [dependencies] serde = { version = "^1.0.80", default-features = false, features = ["alloc"] } From a84537b1e0f3de942e030a35099d9df62268f73f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 7 May 2020 08:34:16 +0200 Subject: [PATCH 067/178] Update description --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 718caca73..f29c49745 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = [ "Simon Warta ", ] categories = ["wasm"] -description = "serde-json for wasm programs" +description = "serde_json for Wasm programs (small, deterministic, no floats)" documentation = "https://docs.rs/serde-json-wasm" edition = "2018" keywords = ["serde", "json", "wasm"] From 7036452eb32c9fcf98f9c6e3ab2e8d001b3ad4be Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 7 May 2020 08:59:59 +0200 Subject: [PATCH 068/178] Prepare CHANGELOG for 0.2.1 --- CHANGELOG.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a427dad2..c2724a8d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [0.2.1] - 2020-05-07 + +### Changed + +- Remove unused Travis CI config +- Polish Cargo.toml + +## [0.2.0] - 2020-05-07 + ### Fixed - The end of strings is now detected correctly in deserialization (#11) @@ -41,7 +50,9 @@ Initial release after forking from [serde-json-core](https://github.com/japaric/serde-json-core) at [bf5533a0](https://github.com/japaric/serde-json-core/commit/bf5533a042a0). -[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.1.3...HEAD +[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.1...HEAD +[0.2.1]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.0...v0.2.1 +[0.2.0]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.1.3...v0.2.0 [0.1.3]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.1.2...v0.1.3 [0.1.2]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.1.1...v0.1.2 [0.1.1]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.1.0...v0.1.1 From 9e99ead667828df08eab4455ca73dad55f7ce63f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 7 May 2020 10:44:19 +0200 Subject: [PATCH 069/178] Set version: 0.2.1 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe05ce854..980177bfd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,7 +26,7 @@ checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" [[package]] name = "serde-json-wasm" -version = "0.2.0" +version = "0.2.1" dependencies = [ "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index f29c49745..d195fad2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ license = "MIT OR Apache-2.0" name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/CosmWasm/serde-json-wasm" -version = "0.2.0" +version = "0.2.1" exclude = [ ".cargo/", ".github/", From 9bc96511a591d5abc10e4b6682696292beda2b33 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 18 May 2020 12:04:34 +0200 Subject: [PATCH 070/178] Test different enum serializations --- src/ser/mod.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 8b854cc9f..d88fd366d 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -599,16 +599,25 @@ mod tests { #[test] fn enum_() { #[derive(Serialize)] - enum Type { - #[serde(rename = "boolean")] - Boolean, - #[serde(rename = "number")] - Number, + enum Animal { + Ant, + #[serde(rename = "kitty")] + Cat, + // serialize_tuple_variant not implemented right now + // Dog(), + Horse {}, + Zebra { + height: u32, + }, } - - assert_eq!(to_string(&Type::Boolean).unwrap(), r#""boolean""#); - - assert_eq!(to_string(&Type::Number).unwrap(), r#""number""#); + assert_eq!(to_string(&Animal::Ant).unwrap(), r#""Ant""#); + assert_eq!(to_string(&Animal::Cat).unwrap(), r#""kitty""#); + // assert_eq!(to_string(&Animal::Dog()).unwrap(), r#"{"Dog":[]}"#); + assert_eq!(to_string(&Animal::Horse {}).unwrap(), r#"{"Horse":{}}"#); + assert_eq!( + to_string(&Animal::Zebra { height: 273 }).unwrap(), + r#"{"Zebra":{"height":273}}"# + ); } #[test] From 0a4ec6a11c8c2545971737b42f505344b08d915d Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Sat, 21 Nov 2020 21:14:39 +0100 Subject: [PATCH 071/178] Serialize unit structs as the empty struct --- src/ser/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index d88fd366d..d9e65c12b 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -307,7 +307,9 @@ impl<'a> ser::Serializer for &'a mut Serializer { } fn serialize_unit_struct(self, _name: &'static str) -> Result { - unreachable!() + // Zero-sized (i.e. empty) struct + self.buf.extend_from_slice((&"{}").as_bytes()); + Ok(()) } fn serialize_unit_variant( @@ -741,6 +743,11 @@ mod tests { #[test] fn struct_() { + #[derive(Serialize)] + struct Nothing; + + assert_eq!(to_string(&Nothing).unwrap(), r#"{}"#); + #[derive(Serialize)] struct Empty {} From 8bc2471da83457c0e02110100d0bf26f46d69364 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Sun, 22 Nov 2020 12:54:41 +0100 Subject: [PATCH 072/178] Add unit structs deserialization support --- src/de/mod.rs | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 9146c4681..ff8774d87 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -414,12 +414,22 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { unreachable!() } - /// Unsupported. Use a more specific deserialize_* method - fn deserialize_unit_struct(self, _name: &'static str, _visitor: V) -> Result + /// Supported for convenience. Resolves to any requested unit (i.e. empty) struct. + fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result where V: Visitor<'de>, { - unreachable!() + let peek = self.parse_whitespace().ok_or(Error::EofWhileParsingValue)?; + + if peek == b'{' { + self.eat_char(); + let ret = visitor.visit_unit()?; + // Check syntax / consume closing curly brace + self.end_map()?; + Ok(ret) + } else { + Err(Error::InvalidType) + } } /// Unsupported. We can’t parse newtypes because we don’t know the underlying type. @@ -802,6 +812,22 @@ mod tests { ); } + #[test] + fn struct_empty() { + #[derive(Debug, Deserialize, PartialEq)] + struct Empty {}; + + assert_eq!(from_str(r#"{}"#), Ok(Empty {})); + } + + #[test] + fn struct_nothing() { + #[derive(Debug, Deserialize, PartialEq, Default)] + struct Nothing; + + assert_eq!(from_str(r#"{}"#), Ok(Nothing)); + } + #[test] fn ignoring_extra_fields() { #[derive(Debug, Deserialize, PartialEq)] From e4486da2028c9d4b09d53af91987d97fed73b49b Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 23 Nov 2020 06:29:23 +0100 Subject: [PATCH 073/178] Update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2724a8d9..47c54b03f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- Add support for unit structs serialization / deserialization + ## [0.2.1] - 2020-05-07 ### Changed From 07c2eece5b6d9add6664f1495d465ccb3df8246d Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 23 Nov 2020 06:40:03 +0100 Subject: [PATCH 074/178] Bump version to 0.2.2 --- CHANGELOG.md | 4 +++- Cargo.lock | 2 +- Cargo.toml | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47c54b03f..e5151f96b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -- Add support for unit structs serialization / deserialization +## [0.2.2] - 2020-11-21 + +- Add support for unit structs serialization / deserialization. ## [0.2.1] - 2020-05-07 diff --git a/Cargo.lock b/Cargo.lock index 980177bfd..4c6ab1da8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,7 +26,7 @@ checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" [[package]] name = "serde-json-wasm" -version = "0.2.1" +version = "0.2.2" dependencies = [ "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index d195fad2e..d55b8dcc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ authors = [ "Jorge Aparicio ", "Ethan Frey ", "Simon Warta ", + "Mauro Lacy ", ] categories = ["wasm"] description = "serde_json for Wasm programs (small, deterministic, no floats)" @@ -13,7 +14,7 @@ license = "MIT OR Apache-2.0" name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/CosmWasm/serde-json-wasm" -version = "0.2.1" +version = "0.2.2" exclude = [ ".cargo/", ".github/", From eab44fda1dc02558383d8b43b039a930699177aa Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 24 Nov 2020 17:57:03 +0100 Subject: [PATCH 075/178] Update src/ser/mod.rs Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> --- src/ser/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index d9e65c12b..b71aa5ae8 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -308,7 +308,7 @@ impl<'a> ser::Serializer for &'a mut Serializer { fn serialize_unit_struct(self, _name: &'static str) -> Result { // Zero-sized (i.e. empty) struct - self.buf.extend_from_slice((&"{}").as_bytes()); + self.buf.extend_from_slice(b"{}"); Ok(()) } From 44631815f0df2824da0a329cf71c35b68209b91a Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 24 Nov 2020 18:39:35 +0100 Subject: [PATCH 076/178] Add tests against serde_json --- Cargo.lock | 48 ++++++++++++++++++++++++++++++++++++------------ Cargo.toml | 1 + src/ser/mod.rs | 8 ++++++++ 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4c6ab1da8..67cc58196 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,28 +1,40 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + [[package]] name = "proc-macro2" -version = "1.0.12" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.4" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ "proc-macro2", ] +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + [[package]] name = "serde" -version = "1.0.106" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" +checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" [[package]] name = "serde-json-wasm" @@ -30,24 +42,36 @@ version = "0.2.2" dependencies = [ "serde", "serde_derive", + "serde_json", ] [[package]] name = "serde_derive" -version = "1.0.106" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" +checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "serde_json" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "syn" -version = "1.0.18" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" +checksum = "3b4f34193997d92804d359ed09953e25d5138df6bcc055a71bf68ee89fdf9223" dependencies = [ "proc-macro2", "quote", @@ -56,6 +80,6 @@ dependencies = [ [[package]] name = "unicode-xid" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" diff --git a/Cargo.toml b/Cargo.toml index d55b8dcc4..b6f58b08b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,3 +26,4 @@ serde = { version = "^1.0.80", default-features = false, features = ["alloc"] } [dev-dependencies] serde_derive = "^1.0.80" +serde_json = "^1.0.59" diff --git a/src/ser/mod.rs b/src/ser/mod.rs index b71aa5ae8..045910b28 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -747,11 +747,19 @@ mod tests { struct Nothing; assert_eq!(to_string(&Nothing).unwrap(), r#"{}"#); + assert_eq!( + to_string(&Nothing).unwrap(), + serde_json::to_string(&Nothing).unwrap() + ); #[derive(Serialize)] struct Empty {} assert_eq!(to_string(&Empty {}).unwrap(), r#"{}"#); + assert_eq!( + to_string(&Empty {}).unwrap(), + serde_json::to_string(&Empty {}).unwrap() + ); #[derive(Serialize)] struct Tuple { From 2d5c9c794a2e4157091256ea2fe89b53ddd89882 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 24 Nov 2020 19:12:00 +0100 Subject: [PATCH 077/178] Serialize unit struct as null (serde_json compatibility) --- src/ser/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 045910b28..2d5810b44 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -307,8 +307,8 @@ impl<'a> ser::Serializer for &'a mut Serializer { } fn serialize_unit_struct(self, _name: &'static str) -> Result { - // Zero-sized (i.e. empty) struct - self.buf.extend_from_slice(b"{}"); + // Zero-sized (i.e. empty) struct serialized to (serde_json compatible) "null" + self.buf.extend_from_slice(b"null"); Ok(()) } @@ -746,7 +746,7 @@ mod tests { #[derive(Serialize)] struct Nothing; - assert_eq!(to_string(&Nothing).unwrap(), r#"{}"#); + assert_eq!(to_string(&Nothing).unwrap(), r#"null"#); assert_eq!( to_string(&Nothing).unwrap(), serde_json::to_string(&Nothing).unwrap() From c12ef2800d2a9e51b9161ea726b8832c2a95b10a Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 24 Nov 2020 19:27:28 +0100 Subject: [PATCH 078/178] Deserialize "null" to requested unit struct --- src/de/mod.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index ff8774d87..d00902e04 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -414,18 +414,17 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { unreachable!() } - /// Supported for convenience. Resolves to any requested unit (i.e. empty) struct. + /// Resolves "null" to requested unit struct fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result where V: Visitor<'de>, { let peek = self.parse_whitespace().ok_or(Error::EofWhileParsingValue)?; - if peek == b'{' { + if peek == b'n' { self.eat_char(); + self.parse_ident(b"ull")?; let ret = visitor.visit_unit()?; - // Check syntax / consume closing curly brace - self.end_map()?; Ok(ret) } else { Err(Error::InvalidType) @@ -825,7 +824,7 @@ mod tests { #[derive(Debug, Deserialize, PartialEq, Default)] struct Nothing; - assert_eq!(from_str(r#"{}"#), Ok(Nothing)); + assert_eq!(from_str(r#"null"#), Ok(Nothing)); } #[test] From 3bd1796dc610b69c582c9604fc8cff2a05071bec Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 24 Nov 2020 20:19:25 +0100 Subject: [PATCH 079/178] Add empty / unit structs deserialization tests against serde_json --- src/de/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index d00902e04..a9bda8a05 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -813,10 +813,11 @@ mod tests { #[test] fn struct_empty() { - #[derive(Debug, Deserialize, PartialEq)] + #[derive(Debug, Deserialize, PartialEq, Clone)] struct Empty {}; assert_eq!(from_str(r#"{}"#), Ok(Empty {})); + assert_eq!(serde_json::from_str::(r#"{}"#).unwrap(), Empty {}); } #[test] @@ -825,6 +826,7 @@ mod tests { struct Nothing; assert_eq!(from_str(r#"null"#), Ok(Nothing)); + assert_eq!(serde_json::from_str::(r#"null"#).unwrap(), Nothing); } #[test] From 89de3cacf32c1d4e371cfad7e6c72a53d66d1153 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 25 Nov 2020 08:00:25 +0100 Subject: [PATCH 080/178] Update src/ser/mod.rs Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> --- src/ser/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 2d5810b44..38a628de0 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -307,7 +307,7 @@ impl<'a> ser::Serializer for &'a mut Serializer { } fn serialize_unit_struct(self, _name: &'static str) -> Result { - // Zero-sized (i.e. empty) struct serialized to (serde_json compatible) "null" + // Unit struct is serialized to (serde_json compatible) "null" self.buf.extend_from_slice(b"null"); Ok(()) } From 9c92060adfa28809c60a473506acb3e2109110af Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 25 Nov 2020 07:59:55 +0100 Subject: [PATCH 081/178] Update CHANGELOG --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5151f96b..2a4467677 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,6 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -## [0.2.2] - 2020-11-21 - - Add support for unit structs serialization / deserialization. ## [0.2.1] - 2020-05-07 From 2464935966ae7724c97e31cde908b9b2275192bb Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Sun, 22 Nov 2020 22:15:02 +0100 Subject: [PATCH 082/178] Add tuple variant serialization --- src/ser/mod.rs | 16 +++++++++------- src/ser/seq.rs | 32 ++++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 38a628de0..c9a23b554 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -155,7 +155,7 @@ impl<'a> ser::Serializer for &'a mut Serializer { type SerializeSeq = SerializeSeq<'a>; type SerializeTuple = SerializeSeq<'a>; type SerializeTupleStruct = Unreachable; - type SerializeTupleVariant = Unreachable; + type SerializeTupleVariant = SerializeSeq<'a>; type SerializeMap = Unreachable; type SerializeStruct = SerializeStruct<'a>; type SerializeStructVariant = SerializeStruct<'a>; @@ -368,10 +368,13 @@ impl<'a> ser::Serializer for &'a mut Serializer { self, _name: &'static str, _variant_index: u32, - _variant: &'static str, - _len: usize, + variant: &'static str, + len: usize, ) -> Result { - unreachable!() + self.buf.push(b'{'); + self.serialize_str(variant)?; + self.buf.push(b':'); + self.serialize_tuple(len) } fn serialize_map(self, _len: Option) -> Result { @@ -605,8 +608,7 @@ mod tests { Ant, #[serde(rename = "kitty")] Cat, - // serialize_tuple_variant not implemented right now - // Dog(), + Dog(), Horse {}, Zebra { height: u32, @@ -614,7 +616,7 @@ mod tests { } assert_eq!(to_string(&Animal::Ant).unwrap(), r#""Ant""#); assert_eq!(to_string(&Animal::Cat).unwrap(), r#""kitty""#); - // assert_eq!(to_string(&Animal::Dog()).unwrap(), r#"{"Dog":[]}"#); + assert_eq!(to_string(&Animal::Dog()).unwrap(), r#"{"Dog":[]}"#); assert_eq!(to_string(&Animal::Horse {}).unwrap(), r#"{"Horse":{}}"#); assert_eq!( to_string(&Animal::Zebra { height: 273 }).unwrap(), diff --git a/src/ser/seq.rs b/src/ser/seq.rs index dd0c69429..760d62b79 100644 --- a/src/ser/seq.rs +++ b/src/ser/seq.rs @@ -3,13 +3,13 @@ use serde::ser; use crate::ser::{Error, Result, Serializer}; pub struct SerializeSeq<'a> { - de: &'a mut Serializer, + ser: &'a mut Serializer, first: bool, } impl<'a> SerializeSeq<'a> { - pub(crate) fn new(de: &'a mut Serializer) -> Self { - SerializeSeq { de, first: true } + pub(crate) fn new(ser: &'a mut Serializer) -> Self { + SerializeSeq { ser, first: true } } } @@ -22,16 +22,16 @@ impl<'a> ser::SerializeSeq for SerializeSeq<'a> { T: ser::Serialize, { if !self.first { - self.de.buf.push(b','); + self.ser.buf.push(b','); } self.first = false; - value.serialize(&mut *self.de)?; + value.serialize(&mut *self.ser)?; Ok(()) } fn end(self) -> Result { - self.de.buf.push(b']'); + self.ser.buf.push(b']'); Ok(()) } } @@ -51,3 +51,23 @@ impl<'a> ser::SerializeTuple for SerializeSeq<'a> { ser::SerializeSeq::end(self) } } + +impl<'a> ser::SerializeTupleVariant for SerializeSeq<'a> { + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> Result<()> + where + T: ser::Serialize, + { + ser::SerializeSeq::serialize_element(self, value) + } + + fn end(self) -> Result { + // close sequence + self.ser.buf.push(b']'); + // close surrounding enum + self.ser.buf.push(b'}'); + Ok(()) + } +} From f166a20452497b6898488884cf071129cf54676e Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Sun, 22 Nov 2020 22:26:20 +0100 Subject: [PATCH 083/178] Add specific tuple variant serialization test --- src/ser/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index c9a23b554..c4fbd6423 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -601,6 +601,18 @@ mod tests { assert_eq!(to_string(&[0, 1, 2]).unwrap(), "[0,1,2]"); } + #[test] + fn tuple_variant() { + #[derive(Serialize)] + enum TupleVariant { + Add(i32, i32), + } + assert_eq!( + to_string(&TupleVariant::Add(3, 4)).unwrap(), + r#"{"Add":[3,4]}"# + ); + } + #[test] fn enum_() { #[derive(Serialize)] From 857b1f8ea478b0f172aebf69f083a447ed6b6fbe Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 23 Nov 2020 05:54:36 +0100 Subject: [PATCH 084/178] Add tuple variant deserialization --- src/de/enum_.rs | 15 +++++++++++++-- src/de/mod.rs | 11 +++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/de/enum_.rs b/src/de/enum_.rs index 7547df525..92fc354e4 100644 --- a/src/de/enum_.rs +++ b/src/de/enum_.rs @@ -104,11 +104,22 @@ impl<'a, 'de> de::VariantAccess<'de> for StructVariantAccess<'a, 'de> { } } - fn tuple_variant(self, _len: usize, _visitor: V) -> Result + fn tuple_variant(self, len: usize, visitor: V) -> Result where V: de::Visitor<'de>, { - Err(Error::InvalidType) + let value = de::Deserializer::deserialize_tuple(&mut *self.de, len, visitor)?; + match self + .de + .parse_whitespace() + .ok_or(Error::EofWhileParsingValue)? + { + b'}' => { + self.de.eat_char(); + Ok(value) + } + _ => Err(Error::ExpectedSomeValue), + } } fn struct_variant(self, fields: &'static [&'static str], visitor: V) -> Result diff --git a/src/de/mod.rs b/src/de/mod.rs index a9bda8a05..9e9186e14 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -662,6 +662,17 @@ mod tests { assert!(from_str::<[i32; 2]>("[0, 1,]").is_err()); } + #[test] + fn tuple_variant() { + #[derive(Debug, Deserialize, PartialEq)] + enum TupleVariant { + Add(i32, i32), + Sub(i32, i32), + } + assert_eq!(from_str(r#"{"Add":[3,4]}"#), Ok(TupleVariant::Add(3, 4))); + assert_eq!(from_str(r#"{"Sub":[5,6]}"#), Ok(TupleVariant::Sub(5, 6))); + } + #[test] fn bool() { assert_eq!(from_str("true"), Ok(true)); From 4d53dae7d815f8e6f038bcf584d1d249a43bc967 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 23 Nov 2020 06:31:35 +0100 Subject: [PATCH 085/178] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a4467677..9d679312a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - Add support for unit structs serialization / deserialization. +- Add support for tuple variants serialization / deserialization. ## [0.2.1] - 2020-05-07 From 8b0d122228adbc3b36395b5eeb18962e590dc5eb Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 25 Nov 2020 18:59:43 +0100 Subject: [PATCH 086/178] Add more tuple variant tests --- src/de/mod.rs | 12 +++++++----- src/ser/mod.rs | 13 +++++++------ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 9e9186e14..e76f4b51c 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -665,12 +665,14 @@ mod tests { #[test] fn tuple_variant() { #[derive(Debug, Deserialize, PartialEq)] - enum TupleVariant { - Add(i32, i32), - Sub(i32, i32), + enum Ops { + Exit(), + Square(i32), + Add(i64, i64), } - assert_eq!(from_str(r#"{"Add":[3,4]}"#), Ok(TupleVariant::Add(3, 4))); - assert_eq!(from_str(r#"{"Sub":[5,6]}"#), Ok(TupleVariant::Sub(5, 6))); + assert_eq!(from_str(r#"{"Exit":[]}"#), Ok(Ops::Exit())); + assert_eq!(from_str(r#"{"Square":2}"#), Ok(Ops::Square(2))); + assert_eq!(from_str(r#"{"Add":[3,4]}"#), Ok(Ops::Add(3, 4))); } #[test] diff --git a/src/ser/mod.rs b/src/ser/mod.rs index c4fbd6423..21f8ae9d2 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -604,13 +604,14 @@ mod tests { #[test] fn tuple_variant() { #[derive(Serialize)] - enum TupleVariant { - Add(i32, i32), + enum Ops { + Exit(), + Square(i32), + Add(i64, i64), } - assert_eq!( - to_string(&TupleVariant::Add(3, 4)).unwrap(), - r#"{"Add":[3,4]}"# - ); + assert_eq!(to_string(&Ops::Exit()).unwrap(), r#"{"Exit":[]}"#); + assert_eq!(to_string(&Ops::Square(2)).unwrap(), r#"{"Square":2}"#); + assert_eq!(to_string(&Ops::Add(3, 4)).unwrap(), r#"{"Add":[3,4]}"#); } #[test] From 2587ca1801a23573aafa4192642a252cd6eb3480 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 25 Nov 2020 19:39:04 +0100 Subject: [PATCH 087/178] Add tuple variant serde_json compatibility tests --- src/de/mod.rs | 20 ++++++++++++++++---- src/ser/mod.rs | 12 ++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index e76f4b51c..50b0e048a 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -671,8 +671,20 @@ mod tests { Add(i64, i64), } assert_eq!(from_str(r#"{"Exit":[]}"#), Ok(Ops::Exit())); - assert_eq!(from_str(r#"{"Square":2}"#), Ok(Ops::Square(2))); - assert_eq!(from_str(r#"{"Add":[3,4]}"#), Ok(Ops::Add(3, 4))); + assert_eq!( + serde_json::from_str::(r#"{"Exit":[]}"#).unwrap(), + Ops::Exit() + ); + assert_eq!(from_str(r#"{"Square":1}"#), Ok(Ops::Square(1))); + assert_eq!( + serde_json::from_str::(r#"{"Square":1}"#).unwrap(), + Ops::Square(1) + ); + assert_eq!(from_str(r#"{"Add":[2,3]}"#), Ok(Ops::Add(2, 3))); + assert_eq!( + serde_json::from_str::(r#"{"Add":[2,3]}"#).unwrap(), + Ops::Add(2, 3) + ); } #[test] @@ -826,7 +838,7 @@ mod tests { #[test] fn struct_empty() { - #[derive(Debug, Deserialize, PartialEq, Clone)] + #[derive(Debug, Deserialize, PartialEq)] struct Empty {}; assert_eq!(from_str(r#"{}"#), Ok(Empty {})); @@ -835,7 +847,7 @@ mod tests { #[test] fn struct_nothing() { - #[derive(Debug, Deserialize, PartialEq, Default)] + #[derive(Debug, Deserialize, PartialEq)] struct Nothing; assert_eq!(from_str(r#"null"#), Ok(Nothing)); diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 21f8ae9d2..40f8faef9 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -610,8 +610,20 @@ mod tests { Add(i64, i64), } assert_eq!(to_string(&Ops::Exit()).unwrap(), r#"{"Exit":[]}"#); + assert_eq!( + to_string(&Ops::Exit()).unwrap(), + serde_json::to_string(&Ops::Exit()).unwrap() + ); assert_eq!(to_string(&Ops::Square(2)).unwrap(), r#"{"Square":2}"#); + assert_eq!( + to_string(&Ops::Square(2)).unwrap(), + serde_json::to_string(&Ops::Square(2)).unwrap() + ); assert_eq!(to_string(&Ops::Add(3, 4)).unwrap(), r#"{"Add":[3,4]}"#); + assert_eq!( + to_string(&Ops::Add(3, 4)).unwrap(), + serde_json::to_string(&Ops::Add(3, 4)).unwrap() + ); } #[test] From de528f72cf2c70a6c9dd1aca09de4cb8a2eb2dbb Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 13 Jan 2021 13:48:41 +0100 Subject: [PATCH 088/178] Prefix enum tests --- src/ser/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 40f8faef9..8d96293b2 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -602,7 +602,7 @@ mod tests { } #[test] - fn tuple_variant() { + fn enum_variants_tuple_like_structs() { #[derive(Serialize)] enum Ops { Exit(), @@ -627,7 +627,7 @@ mod tests { } #[test] - fn enum_() { + fn enum_mixed() { #[derive(Serialize)] enum Animal { Ant, From f751ae436026f8441b25fb15d78c4164f153b8b2 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 13 Jan 2021 13:53:44 +0100 Subject: [PATCH 089/178] Make enum name singular --- src/ser/mod.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 8d96293b2..f6266408e 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -604,25 +604,25 @@ mod tests { #[test] fn enum_variants_tuple_like_structs() { #[derive(Serialize)] - enum Ops { + enum Op { Exit(), Square(i32), Add(i64, i64), } - assert_eq!(to_string(&Ops::Exit()).unwrap(), r#"{"Exit":[]}"#); + assert_eq!(to_string(&Op::Exit()).unwrap(), r#"{"Exit":[]}"#); assert_eq!( - to_string(&Ops::Exit()).unwrap(), - serde_json::to_string(&Ops::Exit()).unwrap() + to_string(&Op::Exit()).unwrap(), + serde_json::to_string(&Op::Exit()).unwrap() ); - assert_eq!(to_string(&Ops::Square(2)).unwrap(), r#"{"Square":2}"#); + assert_eq!(to_string(&Op::Square(2)).unwrap(), r#"{"Square":2}"#); assert_eq!( - to_string(&Ops::Square(2)).unwrap(), - serde_json::to_string(&Ops::Square(2)).unwrap() + to_string(&Op::Square(2)).unwrap(), + serde_json::to_string(&Op::Square(2)).unwrap() ); - assert_eq!(to_string(&Ops::Add(3, 4)).unwrap(), r#"{"Add":[3,4]}"#); + assert_eq!(to_string(&Op::Add(3, 4)).unwrap(), r#"{"Add":[3,4]}"#); assert_eq!( - to_string(&Ops::Add(3, 4)).unwrap(), - serde_json::to_string(&Ops::Add(3, 4)).unwrap() + to_string(&Op::Add(3, 4)).unwrap(), + serde_json::to_string(&Op::Add(3, 4)).unwrap() ); } From cb41e8dcc6c865d39b97939e89d9f729a37c0106 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 13 Jan 2021 13:57:51 +0100 Subject: [PATCH 090/178] Test enum_variants_c_like_structs explicitly --- src/ser/mod.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index f6266408e..b46f6f7f4 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -626,6 +626,37 @@ mod tests { ); } + #[test] + fn enum_variants_c_like_structs() { + #[derive(Serialize)] + enum Op { + Exit {}, + Square { input: i32 }, + Add { a: i64, b: i64 }, + } + assert_eq!(to_string(&Op::Exit {}).unwrap(), r#"{"Exit":{}}"#); + assert_eq!( + to_string(&Op::Exit {}).unwrap(), + serde_json::to_string(&Op::Exit {}).unwrap() + ); + assert_eq!( + to_string(&Op::Square { input: 2 }).unwrap(), + r#"{"Square":{"input":2}}"# + ); + assert_eq!( + to_string(&Op::Square { input: 2 }).unwrap(), + serde_json::to_string(&Op::Square { input: 2 }).unwrap() + ); + assert_eq!( + to_string(&Op::Add { a: 3, b: 4 }).unwrap(), + r#"{"Add":{"a":3,"b":4}}"# + ); + assert_eq!( + to_string(&Op::Add { a: 3, b: 4 }).unwrap(), + serde_json::to_string(&Op::Add { a: 3, b: 4 }).unwrap() + ); + } + #[test] fn enum_mixed() { #[derive(Serialize)] From 1b15fd01b20f6e25d5096e76c02b63045c01e935 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 13 Jan 2021 14:05:01 +0100 Subject: [PATCH 091/178] Test enum_variants_unit_like --- src/ser/mod.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index b46f6f7f4..143142d11 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -601,6 +601,38 @@ mod tests { assert_eq!(to_string(&[0, 1, 2]).unwrap(), "[0,1,2]"); } + #[test] + fn enum_variants_unit_like() { + #[allow(dead_code)] + #[derive(Serialize)] + enum Op { + Enter, + Exit, + } + assert_eq!(to_string(&Op::Exit).unwrap(), r#""Exit""#); + assert_eq!( + to_string(&Op::Exit).unwrap(), + serde_json::to_string(&Op::Exit).unwrap() + ); + + // Numeric values are ignored 🤷 + #[derive(Serialize)] + enum Order { + Unordered = 1, + Ordered = 42, + } + assert_eq!(to_string(&Order::Unordered).unwrap(), r#""Unordered""#); + assert_eq!( + to_string(&Order::Unordered).unwrap(), + serde_json::to_string(&Order::Unordered).unwrap() + ); + assert_eq!(to_string(&Order::Ordered).unwrap(), r#""Ordered""#); + assert_eq!( + to_string(&Order::Ordered).unwrap(), + serde_json::to_string(&Order::Ordered).unwrap() + ); + } + #[test] fn enum_variants_tuple_like_structs() { #[derive(Serialize)] From 370d3a0e2f410767dabe6074478d6da58152afce Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 13 Jan 2021 15:45:38 +0100 Subject: [PATCH 092/178] Add support for unit serialization / deserialization --- CHANGELOG.md | 1 + src/ser/mod.rs | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d679312a..60a90baa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ project adheres to [Semantic Versioning](http://semver.org/). - Add support for unit structs serialization / deserialization. - Add support for tuple variants serialization / deserialization. +- Add support for unit serialization / deserialization. ## [0.2.1] - 2020-05-07 diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 143142d11..1246dcd35 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -303,7 +303,10 @@ impl<'a> ser::Serializer for &'a mut Serializer { } fn serialize_unit(self) -> Result { - unreachable!() + // The unit type is a zero element tuple, so the consistent way to serialize this would be "[]". + // However, for compatibility with serde_json we serialize to "null". + self.buf.extend_from_slice(b"null"); + Ok(()) } fn serialize_unit_struct(self, _name: &'static str) -> Result { @@ -601,6 +604,34 @@ mod tests { assert_eq!(to_string(&[0, 1, 2]).unwrap(), "[0,1,2]"); } + #[test] + fn tuple() { + type Pair = (i64, i64); + type Wrapped = (i64,); // Comma differentiates one element tuple from a primary type surrounded by parentheses + type Unit = (); + + let pair: Pair = (1, 2); + assert_eq!(to_string(&pair).unwrap(), "[1,2]"); + assert_eq!( + to_string(&pair).unwrap(), + serde_json::to_string(&pair).unwrap() + ); + + let wrapped: Wrapped = (5,); + assert_eq!(to_string(&wrapped).unwrap(), "[5]"); + assert_eq!( + to_string(&wrapped).unwrap(), + serde_json::to_string(&wrapped).unwrap() + ); + + let unit: Unit = (); + assert_eq!(to_string(&unit).unwrap(), "null"); + assert_eq!( + to_string(&unit).unwrap(), + serde_json::to_string(&unit).unwrap() + ); + } + #[test] fn enum_variants_unit_like() { #[allow(dead_code)] From 63d18db44bada337a86432a7d913cfd2b1184851 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 13 Jan 2021 15:52:22 +0100 Subject: [PATCH 093/178] Finalize version: 0.2.2 --- .editorconfig | 12 ++++++++++++ CHANGELOG.md | 7 ++++++- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..c1e2c6435 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/CHANGELOG.md b/CHANGELOG.md index 60a90baa4..37d198e2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [0.2.2] - 2021-01-13 + +### Added + - Add support for unit structs serialization / deserialization. - Add support for tuple variants serialization / deserialization. - Add support for unit serialization / deserialization. @@ -54,7 +58,8 @@ Initial release after forking from [serde-json-core](https://github.com/japaric/serde-json-core) at [bf5533a0](https://github.com/japaric/serde-json-core/commit/bf5533a042a0). -[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.1...HEAD +[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.2...HEAD +[0.2.2]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.1...v0.2.2 [0.2.1]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.0...v0.2.1 [0.2.0]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.1.3...v0.2.0 [0.1.3]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.1.2...v0.1.3 From ccfb00d73739c598fb838676359aa8b7ffba7f49 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 13 Jan 2021 15:57:25 +0100 Subject: [PATCH 094/178] Update README --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 80ed33c2e..dbcaa3fd1 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ -# `serde-json-core` +# serde-json-wasm -> [`serde-json`] for `no_std` programs +[![serde-json-wasm on crates.io](https://img.shields.io/crates/v/serde-json-wasm.svg)](https://crates.io/crates/serde-json-wasm) +[![Docs](https://docs.rs/serde-json-wasm/badge.svg)](https://docs.rs/serde-json-wasm) -[`serde-json`]: https://crates.io/crates/serde_json +A [serde-json] alternative for [CosmWasm] smart contracts. -## [Documentation](https://japaric.github.io/serde-json-core/serde_json_core) +[serde-json]: https://crates.io/crates/serde_json +[CosmWasm]: https://cosmwasm.com/ ## License From 4a019395e735a4b550411048c16e2aebae205e45 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 13 Jan 2021 16:35:51 +0100 Subject: [PATCH 095/178] Bump version to 0.2.3 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 67cc58196..7f187836b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,7 +38,7 @@ checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" [[package]] name = "serde-json-wasm" -version = "0.2.2" +version = "0.2.3" dependencies = [ "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index b6f58b08b..8b4af765c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ license = "MIT OR Apache-2.0" name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/CosmWasm/serde-json-wasm" -version = "0.2.2" +version = "0.2.3" exclude = [ ".cargo/", ".github/", From 3314ae4f63113027f92d66acecfbd31c5141fbf3 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 13 Jan 2021 16:36:08 +0100 Subject: [PATCH 096/178] Optimize string deserialization Special chars handling --- src/de/mod.rs | 45 +++++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 50b0e048a..cf6e48ec9 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -103,41 +103,34 @@ impl<'a> Deserializer<'a> { fn parse_string(&mut self) -> Result { let start = self.index; + let mut backslash = false; + let mut escaped = false; loop { match self.peek() { Some(b'"') => { - // Counts the number of backslashes in front of the current index. - // - // "some string with \\\" included." - // ^^^^^ - // ||||| - // loop run: 4321| - // | - // `index` - // - // Since we only get in this code branch if we found a " starting the string and `index` is greater - // than the start position, we know the loop will end no later than this point. - let leading_backslashes = |index: usize| -> usize { - let mut count = 0; - loop { - if self.slice[index - count - 1] == b'\\' { - count += 1; - } else { - return count; - } - } - }; - - let is_escaped = leading_backslashes(self.index) % 2 == 1; - if is_escaped { + if escaped { + escaped = false; self.eat_char(); // just continue } else { let end = self.index; self.eat_char(); - return unescape::unescape(&self.slice[start..end]); + return if backslash { + unescape::unescape(&self.slice[start..end]) + } else { + String::from_utf8(Vec::from(&self.slice[start..end])) + .map_err(|_| Error::InvalidUnicodeCodePoint) + }; } } - Some(_) => self.eat_char(), + Some(b'\\') => { + backslash = true; + escaped = !escaped; + self.eat_char() + } + Some(_) => { + escaped = false; + self.eat_char() + } None => return Err(Error::EofWhileParsingString), } } From 37c1b26596bd23973018936afb06b841914094d5 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 13 Jan 2021 16:57:33 +0100 Subject: [PATCH 097/178] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37d198e2f..040ca1cd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Changed + +- Optimize string deserialization. + ## [0.2.2] - 2021-01-13 ### Added From 634fdaf419d1477cb6b5eec769753b22a70b57d8 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 13 Jan 2021 19:48:48 +0100 Subject: [PATCH 098/178] Optimize string serialization Improve one code point serialization --- src/ser/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 1246dcd35..47024bfe9 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -233,7 +233,7 @@ impl<'a> ser::Serializer for &'a mut Serializer { // An excellent explanation is available at https://www.youtube.com/watch?v=HhIEDWmQS3w // Temporary storage for encoded a single char. - // A char is up to 4 bytes long wehn encoded to UTF-8. + // A char is up to 4 bytes long when encoded to UTF-8. let mut encoding_tmp = [0u8; 4]; for c in v.chars() { @@ -276,8 +276,12 @@ impl<'a> ser::Serializer for &'a mut Serializer { self.buf.push(hex2); } _ => { - let encoded = c.encode_utf8(&mut encoding_tmp as &mut [u8]); - self.buf.extend_from_slice(encoded.as_bytes()); + if c.len_utf8() == 1 { + self.buf.push(c as u8); + } else { + let encoded = c.encode_utf8(&mut encoding_tmp as &mut [u8]); + self.buf.extend_from_slice(encoded.as_bytes()); + } } } } From 24ba551fd2446c29c3250346e80bf522cce12d83 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 13 Jan 2021 20:01:48 +0100 Subject: [PATCH 099/178] Update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 040ca1cd3..78246afe2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ project adheres to [Semantic Versioning](http://semver.org/). ### Changed -- Optimize string deserialization. +- Optimize string serialization / deserialization. ## [0.2.2] - 2021-01-13 From 1d82dcdac4704359caa848467cd9392ba0969a8d Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 14 Jan 2021 07:22:14 +0100 Subject: [PATCH 100/178] s/backslash/contains_backslash/ for clarity --- src/de/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index cf6e48ec9..9f5d349c5 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -103,7 +103,7 @@ impl<'a> Deserializer<'a> { fn parse_string(&mut self) -> Result { let start = self.index; - let mut backslash = false; + let mut contains_backslash = false; let mut escaped = false; loop { match self.peek() { @@ -114,7 +114,7 @@ impl<'a> Deserializer<'a> { } else { let end = self.index; self.eat_char(); - return if backslash { + return if contains_backslash { unescape::unescape(&self.slice[start..end]) } else { String::from_utf8(Vec::from(&self.slice[start..end])) @@ -123,7 +123,7 @@ impl<'a> Deserializer<'a> { } } Some(b'\\') => { - backslash = true; + contains_backslash = true; escaped = !escaped; self.eat_char() } From 2f4dcc37e37a1feb8a9e52dd7f3023751c4087a7 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 14 Jan 2021 09:36:31 +0100 Subject: [PATCH 101/178] Optimize string deserialization Use borrowed strings when possible --- src/de/mod.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 9f5d349c5..09d62da0a 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -13,6 +13,7 @@ use serde::de::{self, Visitor}; use self::enum_::{StructVariantAccess, UnitVariantAccess}; use self::map::MapAccess; use self::seq::SeqAccess; +use std::str::from_utf8; /// Deserializer will parse serde-json-wasm flavored JSON into a /// serde-annotated struct @@ -21,6 +22,11 @@ pub struct Deserializer<'b> { index: usize, } +enum StringLike<'a> { + Borrowed(&'a str), + Owned(String), +} + impl<'a> Deserializer<'a> { fn new(slice: &'a [u8]) -> Deserializer<'_> { Deserializer { slice, index: 0 } @@ -101,7 +107,7 @@ impl<'a> Deserializer<'a> { } } - fn parse_string(&mut self) -> Result { + fn parse_string(&mut self) -> Result> { let start = self.index; let mut contains_backslash = false; let mut escaped = false; @@ -115,10 +121,14 @@ impl<'a> Deserializer<'a> { let end = self.index; self.eat_char(); return if contains_backslash { - unescape::unescape(&self.slice[start..end]) + Ok(StringLike::Owned(unescape::unescape( + &self.slice[start..end], + )?)) } else { - String::from_utf8(Vec::from(&self.slice[start..end])) - .map_err(|_| Error::InvalidUnicodeCodePoint) + Ok(StringLike::Borrowed( + from_utf8(&self.slice[start..end]) + .map_err(|_| Error::InvalidUnicodeCodePoint)?, + )) }; } } @@ -363,7 +373,11 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { match peek { b'"' => { self.eat_char(); - visitor.visit_string(self.parse_string()?) + let str_like = self.parse_string()?; + match str_like { + StringLike::Borrowed(str) => visitor.visit_borrowed_str(str), + StringLike::Owned(string) => visitor.visit_string(string), + } } _ => Err(Error::InvalidType), } From 20bb1c986dcaea1667d9a49b3f16b9dd0506b7a8 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 14 Jan 2021 11:59:54 +0100 Subject: [PATCH 102/178] Update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78246afe2..6dc0f9374 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [0.2.3] - 2021-01-14 + ### Changed - Optimize string serialization / deserialization. From 50b40a789b71050e4d7e27eeb259f00016654be6 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 14 Jan 2021 12:05:05 +0100 Subject: [PATCH 103/178] Finalize version: 0.2.3 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dc0f9374..dc0185cd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,7 +64,8 @@ Initial release after forking from [serde-json-core](https://github.com/japaric/serde-json-core) at [bf5533a0](https://github.com/japaric/serde-json-core/commit/bf5533a042a0). -[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.2...HEAD +[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.3...HEAD +[0.2.3]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.2...v0.2.3 [0.2.2]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.1...v0.2.2 [0.2.1]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.0...v0.2.1 [0.2.0]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.1.3...v0.2.0 From cbc3a27b3f80535724f2ff165f8a3a43300bacd1 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 14 Jan 2021 15:20:43 +0100 Subject: [PATCH 104/178] Update CI toolchain (clippy) v1.49.0 --- .github/workflows/Basic.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Basic.yml b/.github/workflows/Basic.yml index 50c77fb60..9035d61f4 100644 --- a/.github/workflows/Basic.yml +++ b/.github/workflows/Basic.yml @@ -50,7 +50,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.40.0 + toolchain: 1.49.0 override: true components: rustfmt, clippy From ba703c443db55529dbbaa1123f425504f7ce70cd Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 14 Jan 2021 15:36:32 +0100 Subject: [PATCH 105/178] Fix clippy::manual-non-exhaustive warnings --- src/de/errors.rs | 5 +---- src/ser/mod.rs | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/de/errors.rs b/src/de/errors.rs index 59102380f..09ed48e6f 100644 --- a/src/de/errors.rs +++ b/src/de/errors.rs @@ -6,6 +6,7 @@ pub type Result = core::result::Result; /// This type represents all possible errors that can occur when deserializing JSON data #[derive(Debug, PartialEq)] +#[non_exhaustive] pub enum Error { /// Control character (U+0000 to U+001F) found in string. Those must always be escaped. ControlCharacterInString, @@ -69,9 +70,6 @@ pub enum Error { /// Custom error message from serde Custom(String), - - #[doc(hidden)] - __Extensible, } impl error::Error for Error { @@ -135,7 +133,6 @@ impl fmt::Display for Error { } Error::TrailingComma => "JSON has a comma after the last value in an array or map.", Error::Custom(msg) => &msg, - _ => "Invalid JSON", } ) } diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 47024bfe9..288f5ceab 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -17,15 +17,13 @@ pub type Result = ::core::result::Result; /// This type represents all possible errors that can occur when serializing JSON data #[derive(Debug)] +#[non_exhaustive] pub enum Error { /// Buffer is full BufferFull, /// Custom error message from serde Custom(String), - - #[doc(hidden)] - __Extensible, } impl From<()> for Error { @@ -55,7 +53,6 @@ impl fmt::Display for Error { match self { Error::BufferFull => write!(f, "Buffer is full"), Error::Custom(msg) => write!(f, "{}", &msg), - _ => write!(f, "Unknown serialization error"), } } } From c45e7f8fef68d9448506273f3ae8e9f41d3d1c86 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 14 Jan 2021 15:36:53 +0100 Subject: [PATCH 106/178] Update CHANGELOG --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc0185cd4..589f3f9e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Changed + +- Maintenance release: + Update clippy version in CI to 1.49.0 + Fix `clippy::manual-non-exhaustive` warnings + ## [0.2.3] - 2021-01-14 ### Changed From d2f0c7f880f422d630c591bd642a151d57810a7e Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 14 Jan 2021 15:41:37 +0100 Subject: [PATCH 107/178] Bump package version to 0.3.0 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7f187836b..dfacee257 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,7 +38,7 @@ checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" [[package]] name = "serde-json-wasm" -version = "0.2.3" +version = "0.3.0" dependencies = [ "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index 8b4af765c..bff057c69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ license = "MIT OR Apache-2.0" name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/CosmWasm/serde-json-wasm" -version = "0.2.3" +version = "0.3.0" exclude = [ ".cargo/", ".github/", From 1e38b504a161c5a8433af84bc5a16076c81bf8d1 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 14 Jan 2021 15:49:24 +0100 Subject: [PATCH 108/178] Finalize version: 0.3.0 --- CHANGELOG.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 589f3f9e2..130d885be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,13 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [0.3.0] - 2021-01-14 + ### Changed -- Maintenance release: - Update clippy version in CI to 1.49.0 - Fix `clippy::manual-non-exhaustive` warnings +Maintenance release: +- Update clippy version in CI to 1.49.0. +- Fix `clippy::manual-non-exhaustive` warnings. ## [0.2.3] - 2021-01-14 @@ -70,7 +72,8 @@ Initial release after forking from [serde-json-core](https://github.com/japaric/serde-json-core) at [bf5533a0](https://github.com/japaric/serde-json-core/commit/bf5533a042a0). -[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.3...HEAD +[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.3.0...HEAD +[0.3.0]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.3...v0.3.0 [0.2.3]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.2...v0.2.3 [0.2.2]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.1...v0.2.2 [0.2.1]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.0...v0.2.1 From b7538048837c9661ea83152910d02b5a02de78eb Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Fri, 15 Jan 2021 08:06:06 +0100 Subject: [PATCH 109/178] Add Unit deserialization --- src/de/mod.rs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 09d62da0a..92bd74cc0 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -413,16 +413,8 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { } } - /// Unsupported. Use a more specific deserialize_* method - fn deserialize_unit(self, _visitor: V) -> Result - where - V: Visitor<'de>, - { - unreachable!() - } - - /// Resolves "null" to requested unit struct - fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result + /// Resolves "null" to () + fn deserialize_unit(self, visitor: V) -> Result where V: Visitor<'de>, { @@ -438,6 +430,14 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { } } + /// Resolves "null" to requested unit struct + fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_unit(visitor) + } + /// Unsupported. We can’t parse newtypes because we don’t know the underlying type. fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result where @@ -852,6 +852,12 @@ mod tests { assert_eq!(serde_json::from_str::(r#"{}"#).unwrap(), Empty {}); } + #[test] + fn unit() { + assert_eq!(from_str(r#"null"#), Ok(())); + assert_eq!(serde_json::from_str::<()>(r#"null"#).unwrap(), ()); + } + #[test] fn struct_nothing() { #[derive(Debug, Deserialize, PartialEq)] From 290a2069495bf4ba15db9833c6d99fcc10b5bdbe Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Fri, 15 Jan 2021 08:07:27 +0100 Subject: [PATCH 110/178] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 130d885be..a43338195 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added + +- Add support for Unit () deserialization. + ## [0.3.0] - 2021-01-14 ### Changed From f8106f363c20c52f8e314f0b3dd3684684dc5d76 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Fri, 15 Jan 2021 10:40:27 +0100 Subject: [PATCH 111/178] Add extra Unit ser/de tests --- src/ser/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 288f5ceab..499de24db 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -902,6 +902,7 @@ mod tests { #[derive(Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "lowercase")] pub enum MyResult { + Unit(()), Ok(Response), Err(String), } @@ -919,6 +920,12 @@ mod tests { let loaded = crate::from_str(&json).expect("re-load err enum"); assert_eq!(err_input, loaded); + let unit = MyResult::Unit(()); + let json = to_string(&unit).expect("encode unit enum"); + assert_eq!(json, r#"{"unit":null}"#.to_string()); + let loaded = crate::from_str(&json).expect("re-load unit enum"); + assert_eq!(unit, loaded); + let empty_list = MyResult::Ok(Response { log: Some("log message".to_string()), count: 137, From a189247e46d8531d528caa7a7ac5856511128daa Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Fri, 15 Jan 2021 13:19:48 +0100 Subject: [PATCH 112/178] Add tuple deserialization tests --- src/de/mod.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 92bd74cc0..f4cab650a 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -669,6 +669,25 @@ mod tests { assert!(from_str::<[i32; 2]>("[0, 1,]").is_err()); } + #[test] + fn tuple() { + type Pair = (i64, i64); + type Wrapped = (i64,); // Comma differentiates one element tuple from a primary type surrounded by parentheses + type Unit = (); + + let pair: Pair = (1, 2); + assert_eq!(from_str("[1,2]"), Ok(pair)); + assert_eq!(serde_json::from_str::("[1,2]").unwrap(), pair); + + let wrapped: Wrapped = (5,); + assert_eq!(from_str("[5]"), Ok(wrapped)); + assert_eq!(serde_json::from_str::("[5]").unwrap(), wrapped); + + let unit: Unit = (); + assert_eq!(from_str("null"), Ok(unit)); + assert_eq!(serde_json::from_str::<()>("null").unwrap(), unit); + } + #[test] fn tuple_variant() { #[derive(Debug, Deserialize, PartialEq)] @@ -852,12 +871,6 @@ mod tests { assert_eq!(serde_json::from_str::(r#"{}"#).unwrap(), Empty {}); } - #[test] - fn unit() { - assert_eq!(from_str(r#"null"#), Ok(())); - assert_eq!(serde_json::from_str::<()>(r#"null"#).unwrap(), ()); - } - #[test] fn struct_nothing() { #[derive(Debug, Deserialize, PartialEq)] From 5713829226f7b9a9e7a41a34a275b7a398519595 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 19 Jan 2021 16:00:53 +0100 Subject: [PATCH 113/178] Version 0.3.1 --- CHANGELOG.md | 5 ++++- Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a43338195..a50f4d450 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [0.3.1] - 2021-01-19 + ### Added - Add support for Unit () deserialization. @@ -76,7 +78,8 @@ Initial release after forking from [serde-json-core](https://github.com/japaric/serde-json-core) at [bf5533a0](https://github.com/japaric/serde-json-core/commit/bf5533a042a0). -[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.3.0...HEAD +[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.3.1...HEAD +[0.3.1]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.3.0...v0.3.1 [0.3.0]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.3...v0.3.0 [0.2.3]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.2...v0.2.3 [0.2.2]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.1...v0.2.2 diff --git a/Cargo.lock b/Cargo.lock index dfacee257..0619b3076 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,7 +38,7 @@ checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" [[package]] name = "serde-json-wasm" -version = "0.3.0" +version = "0.3.1" dependencies = [ "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index bff057c69..e001cda3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ license = "MIT OR Apache-2.0" name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/CosmWasm/serde-json-wasm" -version = "0.3.0" +version = "0.3.1" exclude = [ ".cargo/", ".github/", From 30d01e8405423e22638b6531a8ab2dda0378c83d Mon Sep 17 00:00:00 2001 From: loloicci Date: Tue, 16 Nov 2021 14:51:22 +0900 Subject: [PATCH 114/178] add serializer for i128 and u128 --- src/ser/mod.rs | 84 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 499de24db..c4581acb6 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -2,7 +2,7 @@ use std::{error, fmt}; -use serde::ser; +use serde::{ser, serde_if_integer128}; use std::vec::Vec; @@ -186,6 +186,17 @@ impl<'a> ser::Serializer for &'a mut Serializer { serialize_signed!(self, 20, v, i64, u64) } + serde_if_integer128! { + fn serialize_i128(self, v: i128) -> Result { + // -170141183460469231731687303715884105728 + let res: Result = serialize_signed!(self, 40, v, i128, u128); + res?; + self.buf.insert(0, b'"'); + self.buf.push(b'"'); + Ok(()) + } + } + fn serialize_u8(self, v: u8) -> Result { // 255 serialize_unsigned!(self, 3, v) @@ -206,6 +217,17 @@ impl<'a> ser::Serializer for &'a mut Serializer { serialize_unsigned!(self, 20, v) } + serde_if_integer128! { + fn serialize_u128(self, v: u128) -> Result { + // 340282366920938463463374607431768211455 + let res: Result = serialize_unsigned!(self, 39, v); + res?; + self.buf.insert(0, b'"'); + self.buf.push(b'"'); + Ok(()) + } + } + fn serialize_f32(self, _v: f32) -> Result { unreachable!() } @@ -513,6 +535,7 @@ impl ser::SerializeStructVariant for Unreachable { #[cfg(test)] mod tests { use super::to_string; + use serde::serde_if_integer128; use serde_derive::Serialize; #[test] @@ -597,6 +620,65 @@ mod tests { to_string::(&std::i64::MIN).unwrap(), "-9223372036854775808" ); + + serde_if_integer128! { + assert_eq!(to_string::(&0).unwrap(), "\"0\""); + assert_eq!(to_string::(&1).unwrap(), "\"1\""); + assert_eq!(to_string::(&456789).unwrap(), "\"456789\""); + assert_eq!(to_string::(&4294967295).unwrap(), "\"4294967295\""); + assert_eq!(to_string::(&4294967296).unwrap(), "\"4294967296\""); + assert_eq!( + to_string::(&9007199254740991).unwrap(), + "\"9007199254740991\"" + ); // Number.MAX_SAFE_INTEGER + assert_eq!( + to_string::(&9007199254740992).unwrap(), + "\"9007199254740992\"" + ); // Number.MAX_SAFE_INTEGER+1 + assert_eq!( + to_string::(&9223372036854775807).unwrap(), + "\"9223372036854775807\"" + ); + assert_eq!( + to_string::(&9223372036854775808).unwrap(), + "\"9223372036854775808\"" + ); + assert_eq!( + to_string::(&std::u128::MAX).unwrap(), + "\"340282366920938463463374607431768211455\"" + ); + + assert_eq!(to_string::(&0).unwrap(), "\"0\""); + assert_eq!(to_string::(&1).unwrap(), "\"1\""); + assert_eq!(to_string::(&456789).unwrap(), "\"456789\""); + assert_eq!(to_string::(&4294967295).unwrap(), "\"4294967295\""); + assert_eq!(to_string::(&4294967296).unwrap(), "\"4294967296\""); + assert_eq!( + to_string::(&9007199254740991).unwrap(), + "\"9007199254740991\"" + ); // Number.MAX_SAFE_INTEGER + assert_eq!( + to_string::(&9007199254740992).unwrap(), + "\"9007199254740992\"" + ); // Number.MAX_SAFE_INTEGER+1 + assert_eq!( + to_string::(&9223372036854775807).unwrap(), + "\"9223372036854775807\"" + ); + assert_eq!( + to_string::(&9223372036854775808).unwrap(), + "\"9223372036854775808\"" + ); + assert_eq!( + to_string::(&std::i128::MAX).unwrap(), + "\"170141183460469231731687303715884105727\"" + ); + assert_eq!(to_string::(&-1).unwrap(), "\"-1\""); + assert_eq!( + to_string::(&std::i128::MIN).unwrap(), + "\"-170141183460469231731687303715884105728\"" + ); + } } #[test] From 152b3a6a2953bfe48483dc8e204ef6c3bf032f30 Mon Sep 17 00:00:00 2001 From: loloicci Date: Tue, 16 Nov 2021 15:41:23 +0900 Subject: [PATCH 115/178] add deserializer for i128 and u128 --- src/de/mod.rs | 107 ++++++++++++++++++++++++++++++++++++++++++++++++- src/ser/mod.rs | 44 ++++++++++---------- 2 files changed, 127 insertions(+), 24 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index f4cab650a..75096289d 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -9,6 +9,7 @@ mod unescape; pub use errors::{Error, Result}; use serde::de::{self, Visitor}; +use serde::serde_if_integer128; use self::enum_::{StructVariantAccess, UnitVariantAccess}; use self::map::MapAccess; @@ -194,9 +195,10 @@ macro_rules! deserialize_unsigned { .checked_add((c - b'0') as $uxx) .ok_or(Error::InvalidNumber)?; } - _ => return $visitor.$visit_uxx(number), + _ => break, } } + $visitor.$visit_uxx(number) } _ => Err(Error::InvalidType), } @@ -235,9 +237,10 @@ macro_rules! deserialize_signed { .checked_add((c - b'0') as $ixx * if signed { -1 } else { 1 }) .ok_or(Error::InvalidNumber)?; } - _ => return $visitor.$visit_ixx(number), + _ => break, } } + $visitor.$visit_ixx(number) } _ => return Err(Error::InvalidType), } @@ -308,6 +311,34 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { deserialize_signed!(self, visitor, i64, visit_i64) } + serde_if_integer128! { + fn deserialize_i128(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self + .parse_whitespace() + .ok_or(Error::EofWhileParsingValue)? { + b'"' => { + self.eat_char() + } + _ => return Err(Error::InvalidType) + }; + + let result = match self.peek() { + Some(b'0'..=b'9' | b'-') => deserialize_signed!(self, visitor, i128, visit_i128), + _ => return Err(Error::InvalidType) + }; + match self.peek() { + Some(b'"') => { + self.eat_char(); + result + } + _ => Err(Error::InvalidType) + } + } + } + fn deserialize_u8(self, visitor: V) -> Result where V: Visitor<'de>, @@ -336,6 +367,35 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { deserialize_unsigned!(self, visitor, u64, visit_u64) } + serde_if_integer128! { + fn deserialize_u128(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self + .parse_whitespace() + .ok_or(Error::EofWhileParsingValue)? { + b'"' => { + self.eat_char(); + } + _ => return Err(Error::InvalidType) + }; + + let result = match self.peek() { + Some(b'-') => return Err(Error::InvalidNumber), + Some(b'0'..=b'9') => deserialize_unsigned!(self, visitor, u128, visit_u128), + _ => return Err(Error::InvalidType) + }; + match self.peek() { + Some(b'"') => { + self.eat_char(); + result + } + _ => Err(Error::InvalidType) + } + } + } + fn deserialize_f32(self, _visitor: V) -> Result where V: Visitor<'de>, @@ -660,6 +720,49 @@ mod tests { assert_eq!(from_str("[4,5]\r\n"), Ok([4, 5])); } + #[test] + fn integer128() { + assert_eq!(from_str::(r#"0"#), Err(crate::de::Error::InvalidType)); + assert_eq!(from_str::(r#""0""#), Ok(0)); + assert_eq!(from_str::(r#""1""#), Ok(1)); + assert_eq!(from_str::(r#""-1""#), Ok(-1)); + // max i128 + assert_eq!( + from_str::(r#""170141183460469231731687303715884105727""#), + Ok(170141183460469231731687303715884105727) + ); + assert_eq!( + from_str::(r#""170141183460469231731687303715884105728""#), + Err(crate::de::Error::InvalidNumber) + ); + // min i128 + assert_eq!( + from_str::(r#""-170141183460469231731687303715884105728""#), + Ok(-170141183460469231731687303715884105728) + ); + assert_eq!( + from_str::(r#""-170141183460469231731687303715884105729""#), + Err(crate::de::Error::InvalidNumber) + ); + + assert_eq!(from_str::(r#"0"#), Err(crate::de::Error::InvalidType)); + assert_eq!(from_str::(r#""0""#), Ok(0)); + assert_eq!(from_str::(r#""1""#), Ok(1)); + assert_eq!( + from_str::(r#""-1""#), + Err(crate::de::Error::InvalidNumber) + ); + // max u128 + assert_eq!( + from_str::(r#""340282366920938463463374607431768211455""#), + Ok(340282366920938463463374607431768211455) + ); + assert_eq!( + from_str::(r#""340282366920938463463374607431768211456""#), + Err(crate::de::Error::InvalidNumber) + ) + } + #[test] fn array() { assert_eq!(from_str::<[i32; 0]>("[]"), Ok([])); diff --git a/src/ser/mod.rs b/src/ser/mod.rs index c4581acb6..eb0b56638 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -622,61 +622,61 @@ mod tests { ); serde_if_integer128! { - assert_eq!(to_string::(&0).unwrap(), "\"0\""); - assert_eq!(to_string::(&1).unwrap(), "\"1\""); - assert_eq!(to_string::(&456789).unwrap(), "\"456789\""); - assert_eq!(to_string::(&4294967295).unwrap(), "\"4294967295\""); - assert_eq!(to_string::(&4294967296).unwrap(), "\"4294967296\""); + assert_eq!(to_string::(&0).unwrap(), r#""0""#); + assert_eq!(to_string::(&1).unwrap(), r#""1""#); + assert_eq!(to_string::(&456789).unwrap(), r#""456789""#); + assert_eq!(to_string::(&4294967295).unwrap(), r#""4294967295""#); + assert_eq!(to_string::(&4294967296).unwrap(), r#""4294967296""#); assert_eq!( to_string::(&9007199254740991).unwrap(), - "\"9007199254740991\"" + r#""9007199254740991""# ); // Number.MAX_SAFE_INTEGER assert_eq!( to_string::(&9007199254740992).unwrap(), - "\"9007199254740992\"" + r#""9007199254740992""# ); // Number.MAX_SAFE_INTEGER+1 assert_eq!( to_string::(&9223372036854775807).unwrap(), - "\"9223372036854775807\"" + r#""9223372036854775807""# ); assert_eq!( to_string::(&9223372036854775808).unwrap(), - "\"9223372036854775808\"" + r#""9223372036854775808""# ); assert_eq!( to_string::(&std::u128::MAX).unwrap(), - "\"340282366920938463463374607431768211455\"" + r#""340282366920938463463374607431768211455""# ); - assert_eq!(to_string::(&0).unwrap(), "\"0\""); - assert_eq!(to_string::(&1).unwrap(), "\"1\""); - assert_eq!(to_string::(&456789).unwrap(), "\"456789\""); - assert_eq!(to_string::(&4294967295).unwrap(), "\"4294967295\""); - assert_eq!(to_string::(&4294967296).unwrap(), "\"4294967296\""); + assert_eq!(to_string::(&0).unwrap(), r#""0""#); + assert_eq!(to_string::(&1).unwrap(), r#""1""#); + assert_eq!(to_string::(&456789).unwrap(), r#""456789""#); + assert_eq!(to_string::(&4294967295).unwrap(), r#""4294967295""#); + assert_eq!(to_string::(&4294967296).unwrap(), r#""4294967296""#); assert_eq!( to_string::(&9007199254740991).unwrap(), - "\"9007199254740991\"" + r#""9007199254740991""# ); // Number.MAX_SAFE_INTEGER assert_eq!( to_string::(&9007199254740992).unwrap(), - "\"9007199254740992\"" + r#""9007199254740992""# ); // Number.MAX_SAFE_INTEGER+1 assert_eq!( to_string::(&9223372036854775807).unwrap(), - "\"9223372036854775807\"" + r#""9223372036854775807""# ); assert_eq!( to_string::(&9223372036854775808).unwrap(), - "\"9223372036854775808\"" + r#""9223372036854775808""# ); assert_eq!( to_string::(&std::i128::MAX).unwrap(), - "\"170141183460469231731687303715884105727\"" + r#""170141183460469231731687303715884105727""# ); - assert_eq!(to_string::(&-1).unwrap(), "\"-1\""); + assert_eq!(to_string::(&-1).unwrap(), r#""-1""#); assert_eq!( to_string::(&std::i128::MIN).unwrap(), - "\"-170141183460469231731687303715884105728\"" + r#""-170141183460469231731687303715884105728""# ); } } From b6e1f47fbaf5340db5d1531d94b545f3beeb9a6e Mon Sep 17 00:00:00 2001 From: loloicci Date: Tue, 16 Nov 2021 21:17:38 +0900 Subject: [PATCH 116/178] remove serde_if_integer128 --- src/de/mod.rs | 87 +++++++++++++---------------- src/ser/mod.rs | 149 +++++++++++++++++++++++-------------------------- 2 files changed, 109 insertions(+), 127 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 75096289d..fbfa3b4f6 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -9,7 +9,6 @@ mod unescape; pub use errors::{Error, Result}; use serde::de::{self, Visitor}; -use serde::serde_if_integer128; use self::enum_::{StructVariantAccess, UnitVariantAccess}; use self::map::MapAccess; @@ -311,31 +310,25 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { deserialize_signed!(self, visitor, i64, visit_i64) } - serde_if_integer128! { - fn deserialize_i128(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self - .parse_whitespace() - .ok_or(Error::EofWhileParsingValue)? { - b'"' => { - self.eat_char() - } - _ => return Err(Error::InvalidType) - }; + fn deserialize_i128(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? { + b'"' => self.eat_char(), + _ => return Err(Error::InvalidType), + }; - let result = match self.peek() { - Some(b'0'..=b'9' | b'-') => deserialize_signed!(self, visitor, i128, visit_i128), - _ => return Err(Error::InvalidType) - }; - match self.peek() { - Some(b'"') => { - self.eat_char(); - result - } - _ => Err(Error::InvalidType) + let result = match self.peek() { + Some(b'0'..=b'9' | b'-') => deserialize_signed!(self, visitor, i128, visit_i128), + _ => return Err(Error::InvalidType) + }; + match self.peek() { + Some(b'"') => { + self.eat_char(); + result } + _ => Err(Error::InvalidType), } } @@ -367,32 +360,28 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { deserialize_unsigned!(self, visitor, u64, visit_u64) } - serde_if_integer128! { - fn deserialize_u128(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self - .parse_whitespace() - .ok_or(Error::EofWhileParsingValue)? { - b'"' => { - self.eat_char(); - } - _ => return Err(Error::InvalidType) - }; - - let result = match self.peek() { - Some(b'-') => return Err(Error::InvalidNumber), - Some(b'0'..=b'9') => deserialize_unsigned!(self, visitor, u128, visit_u128), - _ => return Err(Error::InvalidType) - }; - match self.peek() { - Some(b'"') => { - self.eat_char(); - result - } - _ => Err(Error::InvalidType) + fn deserialize_u128(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? { + b'"' => { + self.eat_char(); } + _ => return Err(Error::InvalidType), + }; + + let result = match self.peek() { + Some(b'-') => return Err(Error::InvalidNumber), + Some(b'0'..=b'9') => deserialize_unsigned!(self, visitor, u128, visit_u128), + _ => return Err(Error::InvalidType), + }; + match self.peek() { + Some(b'"') => { + self.eat_char(); + result + } + _ => Err(Error::InvalidType), } } diff --git a/src/ser/mod.rs b/src/ser/mod.rs index eb0b56638..3f30d59e7 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -2,7 +2,7 @@ use std::{error, fmt}; -use serde::{ser, serde_if_integer128}; +use serde::ser; use std::vec::Vec; @@ -186,15 +186,13 @@ impl<'a> ser::Serializer for &'a mut Serializer { serialize_signed!(self, 20, v, i64, u64) } - serde_if_integer128! { - fn serialize_i128(self, v: i128) -> Result { - // -170141183460469231731687303715884105728 - let res: Result = serialize_signed!(self, 40, v, i128, u128); - res?; - self.buf.insert(0, b'"'); - self.buf.push(b'"'); - Ok(()) - } + fn serialize_i128(self, v: i128) -> Result { + // -170141183460469231731687303715884105728 + let res: Result = serialize_signed!(self, 40, v, i128, u128); + res?; + self.buf.insert(0, b'"'); + self.buf.push(b'"'); + Ok(()) } fn serialize_u8(self, v: u8) -> Result { @@ -217,15 +215,13 @@ impl<'a> ser::Serializer for &'a mut Serializer { serialize_unsigned!(self, 20, v) } - serde_if_integer128! { - fn serialize_u128(self, v: u128) -> Result { - // 340282366920938463463374607431768211455 - let res: Result = serialize_unsigned!(self, 39, v); - res?; - self.buf.insert(0, b'"'); - self.buf.push(b'"'); - Ok(()) - } + fn serialize_u128(self, v: u128) -> Result { + // 340282366920938463463374607431768211455 + let res: Result = serialize_unsigned!(self, 39, v); + res?; + self.buf.insert(0, b'"'); + self.buf.push(b'"'); + Ok(()) } fn serialize_f32(self, _v: f32) -> Result { @@ -535,7 +531,6 @@ impl ser::SerializeStructVariant for Unreachable { #[cfg(test)] mod tests { use super::to_string; - use serde::serde_if_integer128; use serde_derive::Serialize; #[test] @@ -621,64 +616,62 @@ mod tests { "-9223372036854775808" ); - serde_if_integer128! { - assert_eq!(to_string::(&0).unwrap(), r#""0""#); - assert_eq!(to_string::(&1).unwrap(), r#""1""#); - assert_eq!(to_string::(&456789).unwrap(), r#""456789""#); - assert_eq!(to_string::(&4294967295).unwrap(), r#""4294967295""#); - assert_eq!(to_string::(&4294967296).unwrap(), r#""4294967296""#); - assert_eq!( - to_string::(&9007199254740991).unwrap(), - r#""9007199254740991""# - ); // Number.MAX_SAFE_INTEGER - assert_eq!( - to_string::(&9007199254740992).unwrap(), - r#""9007199254740992""# - ); // Number.MAX_SAFE_INTEGER+1 - assert_eq!( - to_string::(&9223372036854775807).unwrap(), - r#""9223372036854775807""# - ); - assert_eq!( - to_string::(&9223372036854775808).unwrap(), - r#""9223372036854775808""# - ); - assert_eq!( - to_string::(&std::u128::MAX).unwrap(), - r#""340282366920938463463374607431768211455""# - ); - - assert_eq!(to_string::(&0).unwrap(), r#""0""#); - assert_eq!(to_string::(&1).unwrap(), r#""1""#); - assert_eq!(to_string::(&456789).unwrap(), r#""456789""#); - assert_eq!(to_string::(&4294967295).unwrap(), r#""4294967295""#); - assert_eq!(to_string::(&4294967296).unwrap(), r#""4294967296""#); - assert_eq!( - to_string::(&9007199254740991).unwrap(), - r#""9007199254740991""# - ); // Number.MAX_SAFE_INTEGER - assert_eq!( - to_string::(&9007199254740992).unwrap(), - r#""9007199254740992""# - ); // Number.MAX_SAFE_INTEGER+1 - assert_eq!( - to_string::(&9223372036854775807).unwrap(), - r#""9223372036854775807""# - ); - assert_eq!( - to_string::(&9223372036854775808).unwrap(), - r#""9223372036854775808""# - ); - assert_eq!( - to_string::(&std::i128::MAX).unwrap(), - r#""170141183460469231731687303715884105727""# - ); - assert_eq!(to_string::(&-1).unwrap(), r#""-1""#); - assert_eq!( - to_string::(&std::i128::MIN).unwrap(), - r#""-170141183460469231731687303715884105728""# - ); - } + assert_eq!(to_string::(&0).unwrap(), r#""0""#); + assert_eq!(to_string::(&1).unwrap(), r#""1""#); + assert_eq!(to_string::(&456789).unwrap(), r#""456789""#); + assert_eq!(to_string::(&4294967295).unwrap(), r#""4294967295""#); + assert_eq!(to_string::(&4294967296).unwrap(), r#""4294967296""#); + assert_eq!( + to_string::(&9007199254740991).unwrap(), + r#""9007199254740991""# + ); // Number.MAX_SAFE_INTEGER + assert_eq!( + to_string::(&9007199254740992).unwrap(), + r#""9007199254740992""# + ); // Number.MAX_SAFE_INTEGER+1 + assert_eq!( + to_string::(&9223372036854775807).unwrap(), + r#""9223372036854775807""# + ); + assert_eq!( + to_string::(&9223372036854775808).unwrap(), + r#""9223372036854775808""# + ); + assert_eq!( + to_string::(&std::u128::MAX).unwrap(), + r#""340282366920938463463374607431768211455""# + ); + + assert_eq!(to_string::(&0).unwrap(), r#""0""#); + assert_eq!(to_string::(&1).unwrap(), r#""1""#); + assert_eq!(to_string::(&456789).unwrap(), r#""456789""#); + assert_eq!(to_string::(&4294967295).unwrap(), r#""4294967295""#); + assert_eq!(to_string::(&4294967296).unwrap(), r#""4294967296""#); + assert_eq!( + to_string::(&9007199254740991).unwrap(), + r#""9007199254740991""# + ); // Number.MAX_SAFE_INTEGER + assert_eq!( + to_string::(&9007199254740992).unwrap(), + r#""9007199254740992""# + ); // Number.MAX_SAFE_INTEGER+1 + assert_eq!( + to_string::(&9223372036854775807).unwrap(), + r#""9223372036854775807""# + ); + assert_eq!( + to_string::(&9223372036854775808).unwrap(), + r#""9223372036854775808""# + ); + assert_eq!( + to_string::(&std::i128::MAX).unwrap(), + r#""170141183460469231731687303715884105727""# + ); + assert_eq!(to_string::(&-1).unwrap(), r#""-1""#); + assert_eq!( + to_string::(&std::i128::MIN).unwrap(), + r#""-170141183460469231731687303715884105728""# + ); } #[test] From cf5fd57cd072fe66acea62c57c8bb7b390541d0c Mon Sep 17 00:00:00 2001 From: loloicci Date: Tue, 16 Nov 2021 21:22:49 +0900 Subject: [PATCH 117/178] remove or-patterns in pattern match --- src/de/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index fbfa3b4f6..2ebaf6955 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -320,8 +320,11 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { }; let result = match self.peek() { - Some(b'0'..=b'9' | b'-') => deserialize_signed!(self, visitor, i128, visit_i128), - _ => return Err(Error::InvalidType) + // after rust merged or-patterns feature, these two clause can be merged. + // error[E0658]: or-patterns syntax is experimental + Some(b'0'..=b'9') => deserialize_signed!(self, visitor, i128, visit_i128), + Some(b'-') => deserialize_signed!(self, visitor, i128, visit_i128), + _ => return Err(Error::InvalidType), }; match self.peek() { Some(b'"') => { From be05ee7e578d80db691b6fba899825608185efb5 Mon Sep 17 00:00:00 2001 From: loloicci Date: Tue, 16 Nov 2021 21:26:03 +0900 Subject: [PATCH 118/178] solve a warning about unnecessary trailing semicolon --- src/de/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index f4cab650a..d54ba976b 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -865,7 +865,7 @@ mod tests { #[test] fn struct_empty() { #[derive(Debug, Deserialize, PartialEq)] - struct Empty {}; + struct Empty {} assert_eq!(from_str(r#"{}"#), Ok(Empty {})); assert_eq!(serde_json::from_str::(r#"{}"#).unwrap(), Empty {}); From 5c129c9353c02b4bd0a375c85895f49c412c5a09 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 14 Dec 2021 18:59:37 +0100 Subject: [PATCH 119/178] Bump to 0.3.2 --- Cargo.lock | 4 +++- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0619b3076..5c59703f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "itoa" version = "0.4.6" @@ -38,7 +40,7 @@ checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" [[package]] name = "serde-json-wasm" -version = "0.3.1" +version = "0.3.2" dependencies = [ "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index e001cda3f..0071ac154 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ license = "MIT OR Apache-2.0" name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/CosmWasm/serde-json-wasm" -version = "0.3.1" +version = "0.3.2" exclude = [ ".cargo/", ".github/", From bee4414ffdbeed5246fcc6f44e5cd19be8414b85 Mon Sep 17 00:00:00 2001 From: Alwin Peng Date: Fri, 11 Mar 2022 23:42:24 -0500 Subject: [PATCH 120/178] fix u128/i128 serialization --- src/ser/mod.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 3f30d59e7..47a542fd6 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -188,9 +188,9 @@ impl<'a> ser::Serializer for &'a mut Serializer { fn serialize_i128(self, v: i128) -> Result { // -170141183460469231731687303715884105728 + self.buf.push(b'"'); let res: Result = serialize_signed!(self, 40, v, i128, u128); res?; - self.buf.insert(0, b'"'); self.buf.push(b'"'); Ok(()) } @@ -217,9 +217,9 @@ impl<'a> ser::Serializer for &'a mut Serializer { fn serialize_u128(self, v: u128) -> Result { // 340282366920938463463374607431768211455 + self.buf.push(b'"'); let res: Result = serialize_unsigned!(self, 39, v); res?; - self.buf.insert(0, b'"'); self.buf.push(b'"'); Ok(()) } @@ -706,6 +706,15 @@ mod tests { to_string(&unit).unwrap(), serde_json::to_string(&unit).unwrap() ); + + type BigPair = (u128, u128); + + let pair: BigPair = (u128::MAX, u128::MAX); + + assert_eq!( + to_string(&pair).unwrap(), + r#"["340282366920938463463374607431768211455","340282366920938463463374607431768211455"]"# + ); } #[test] From 036a94674d90be94f2b04225f32ca97b8b0f0ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Kuras?= Date: Mon, 28 Mar 2022 15:31:05 +0200 Subject: [PATCH 121/178] Added implementation of `deserialize_any` to support `untagged` enum representation --- src/de/map.rs | 5 ++-- src/de/mod.rs | 56 +++++++++++++++++++++++++++++++++++----- src/lib.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 9 deletions(-) diff --git a/src/de/map.rs b/src/de/map.rs index 7da47d3f3..41f9fe4d7 100644 --- a/src/de/map.rs +++ b/src/de/map.rs @@ -64,11 +64,12 @@ struct MapKey<'a, 'b> { impl<'de, 'a> de::Deserializer<'de> for MapKey<'a, 'de> { type Error = Error; - fn deserialize_any(self, _visitor: V) -> Result + fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { - unreachable!() + // Only identifiers are proper json object keys + self.deserialize_identifier(visitor) } fn deserialize_bool(self, _visitor: V) -> Result diff --git a/src/de/mod.rs b/src/de/mod.rs index 1ac5c8b3e..b66dc58c5 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -249,16 +249,58 @@ macro_rules! deserialize_signed { impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { type Error = Error; - /// Unsupported. We rely on typed deserialization methods, even if a JSON - /// has enough type information to detect types in many cases. - /// - /// See https://serde.rs/impl-deserialize.html to learn more about the differentiation - /// between `deserialize_{type}` and `deserialize_any`. - fn deserialize_any(self, _visitor: V) -> Result + fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { - unreachable!() + match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? { + b'n' => { + self.eat_char(); + self.parse_ident(b"ull")?; + visitor.visit_unit() + } + b't' => { + self.eat_char(); + self.parse_ident(b"rue")?; + visitor.visit_bool(true) + } + b'f' => { + self.eat_char(); + self.parse_ident(b"alse")?; + visitor.visit_bool(false) + } + b'-' => { + deserialize_signed!(self, visitor, i64, visit_i64) + } + b'0'..=b'9' => { + deserialize_unsigned!(self, visitor, u64, visit_u64) + } + b'"' => { + self.eat_char(); + let str_like = self.parse_string()?; + match str_like { + StringLike::Borrowed(str) => visitor.visit_borrowed_str(str), + StringLike::Owned(string) => visitor.visit_string(string), + } + } + b'[' => { + self.eat_char(); + let ret = visitor.visit_seq(SeqAccess::new(self))?; + + self.end_seq()?; + + Ok(ret) + } + b'{' => { + self.eat_char(); + let ret = visitor.visit_map(MapAccess::new(self))?; + + self.end_map()?; + + Ok(ret) + } + _ => Err(Error::ExpectedSomeValue), + } } fn deserialize_bool(self, visitor: V) -> Result diff --git a/src/lib.rs b/src/lib.rs index 33ad0c0bd..6a82e060d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -132,4 +132,75 @@ mod test { assert_eq!(from_str::(&to_string(&min).unwrap()).unwrap(), min); assert_eq!(from_str::(&to_string(&max).unwrap()).unwrap(), max); } + + #[test] + fn untagged() { + #[derive(Debug, Deserialize, Serialize, PartialEq)] + #[serde(untagged)] + enum UntaggedEnum { + S(String), + I(i64), + } + + let s = UntaggedEnum::S("Some string".to_owned()); + let i = UntaggedEnum::I(32); + + assert_eq!(from_slice::(&to_vec(&s).unwrap()).unwrap(), s); + assert_eq!(from_slice::(&to_vec(&i).unwrap()).unwrap(), i); + + assert_eq!( + from_str::(&to_string(&s).unwrap()).unwrap(), + s + ); + assert_eq!( + from_str::(&to_string(&i).unwrap()).unwrap(), + i + ); + } + + #[test] + fn untagged_structures() { + #[derive(Debug, Deserialize, Serialize, PartialEq)] + #[serde(untagged)] + enum ModelOrItem { + Model(Model), + Item(Item), + } + + let model = ModelOrItem::Model(Model::Post { + category: "Rust".to_owned(), + author: Address("no-reply@domain.com".to_owned()), + }); + + let item = ModelOrItem::Item(Item { + model: Model::Comment, + title: "Title".to_owned(), + content: None, + list: vec![13, 14], + published: true, + comments: vec![], + stats: Stats { + views: 110, + score: 12, + }, + }); + + assert_eq!( + from_slice::(&to_vec(&model).unwrap()).unwrap(), + model + ); + assert_eq!( + from_slice::(&to_vec(&item).unwrap()).unwrap(), + item + ); + + assert_eq!( + from_str::(&to_string(&model).unwrap()).unwrap(), + model + ); + assert_eq!( + from_str::(&to_string(&item).unwrap()).unwrap(), + item + ); + } } From 565923494a9c27ad5a5375abf112949628e37cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Kuras?= Date: Tue, 29 Mar 2022 11:39:26 +0200 Subject: [PATCH 122/178] Updated version for release --- CHANGELOG.md | 4 +++- Cargo.lock | 38 +++++++++++++++++++------------------- Cargo.toml | 3 ++- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a50f4d450..5b84de603 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## [Unreleased] +## [0.4.0] - 2022-03-29 +### Added +- Add support for `#[serde(untagged)]` enums representation ## [0.3.1] - 2021-01-19 diff --git a/Cargo.lock b/Cargo.lock index 5c59703f2..f163f4b1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,43 +4,43 @@ version = 3 [[package]] name = "itoa" -version = "0.4.6" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.7" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" dependencies = [ "proc-macro2", ] [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] name = "serde" -version = "1.0.117" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" [[package]] name = "serde-json-wasm" -version = "0.3.2" +version = "0.4.0" dependencies = [ "serde", "serde_derive", @@ -49,9 +49,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.117" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" dependencies = [ "proc-macro2", "quote", @@ -60,9 +60,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.59" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" dependencies = [ "itoa", "ryu", @@ -71,9 +71,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.51" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b4f34193997d92804d359ed09953e25d5138df6bcc055a71bf68ee89fdf9223" +checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f" dependencies = [ "proc-macro2", "quote", @@ -82,6 +82,6 @@ dependencies = [ [[package]] name = "unicode-xid" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" diff --git a/Cargo.toml b/Cargo.toml index 0071ac154..676e437c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ authors = [ "Ethan Frey ", "Simon Warta ", "Mauro Lacy ", + "Bartłomiej Kuras ", ] categories = ["wasm"] description = "serde_json for Wasm programs (small, deterministic, no floats)" @@ -14,7 +15,7 @@ license = "MIT OR Apache-2.0" name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/CosmWasm/serde-json-wasm" -version = "0.3.2" +version = "0.4.0" exclude = [ ".cargo/", ".github/", From be6715cb363629978ab4c6ae397955b23ca9739a Mon Sep 17 00:00:00 2001 From: Alwin Peng Date: Tue, 29 Mar 2022 09:22:46 -0400 Subject: [PATCH 123/178] fix tests --- src/ser/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 47a542fd6..b3d8a24d2 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -709,7 +709,7 @@ mod tests { type BigPair = (u128, u128); - let pair: BigPair = (u128::MAX, u128::MAX); + let pair: BigPair = (std::u128::MAX, std::u128::MAX); assert_eq!( to_string(&pair).unwrap(), From e3c9e2dd17382e5178d18b4e71475c8d53a51fc5 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 5 May 2022 20:51:37 +0200 Subject: [PATCH 124/178] Prepare 0.4.1 release --- CHANGELOG.md | 6 ++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- src/de/errors.rs | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b84de603..fda356773 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [0.4.1] - 2022-05-05 + +### Changed +- Properly serialize `u128`/`i128` types when embedded in structs + ## [0.4.0] - 2022-03-29 + ### Added - Add support for `#[serde(untagged)]` enums representation diff --git a/Cargo.lock b/Cargo.lock index f163f4b1a..ec430f855 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,7 +40,7 @@ checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" [[package]] name = "serde-json-wasm" -version = "0.4.0" +version = "0.4.1" dependencies = [ "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index 676e437c7..5ffafaf8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "MIT OR Apache-2.0" name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/CosmWasm/serde-json-wasm" -version = "0.4.0" +version = "0.4.1" exclude = [ ".cargo/", ".github/", diff --git a/src/de/errors.rs b/src/de/errors.rs index 09ed48e6f..fd3e6e511 100644 --- a/src/de/errors.rs +++ b/src/de/errors.rs @@ -132,7 +132,7 @@ impl fmt::Display for Error { value." } Error::TrailingComma => "JSON has a comma after the last value in an array or map.", - Error::Custom(msg) => &msg, + Error::Custom(msg) => msg, } ) } From a0ae9c8ae6803e5760980b24894c6f48b8e11cdd Mon Sep 17 00:00:00 2001 From: CyberHoward Date: Tue, 8 Nov 2022 16:09:42 +0000 Subject: [PATCH 125/178] implement embedded-rust changes --- Cargo.toml | 1 + src/de/mod.rs | 27 +++++++++++++------------- src/lib.rs | 11 +++++++++++ src/ser/map.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++ src/ser/mod.rs | 8 ++++++-- src/ser/struct_.rs | 32 +++++++++++++++---------------- 6 files changed, 96 insertions(+), 31 deletions(-) create mode 100644 src/ser/map.rs diff --git a/Cargo.toml b/Cargo.toml index 5ffafaf8e..1969d3785 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ exclude = [ [dependencies] serde = { version = "^1.0.80", default-features = false, features = ["alloc"] } +serde-cw-value = "0.7.0" [dev-dependencies] serde_derive = "^1.0.80" diff --git a/src/de/mod.rs b/src/de/mod.rs index b66dc58c5..51d65e165 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -579,19 +579,7 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { /// Unsupported. Can’t make an arbitrary-sized map in no-std. Use a struct with a /// known format, or implement a custom map deserializer / visitor: /// https://serde.rs/deserialize-map.html - fn deserialize_map(self, _visitor: V) -> Result - where - V: Visitor<'de>, - { - unreachable!() - } - - fn deserialize_struct( - self, - _name: &'static str, - _fields: &'static [&'static str], - visitor: V, - ) -> Result + fn deserialize_map(self, visitor: V) -> Result where V: Visitor<'de>, { @@ -608,6 +596,19 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { } else { Err(Error::InvalidType) } + + } + + fn deserialize_struct( + self, + _name: &'static str, + _fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.deserialize_map(visitor) } fn deserialize_enum( diff --git a/src/lib.rs b/src/lib.rs index 6a82e060d..2f8bd65f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,6 +65,8 @@ pub use self::ser::{to_string, to_vec}; #[cfg(test)] mod test { + use std::collections::BTreeMap; + use super::*; use serde_derive::{Deserialize, Serialize}; @@ -95,6 +97,7 @@ mod test { published: bool, comments: Vec, stats: Stats, + balances: BTreeMap, } #[test] @@ -107,7 +110,10 @@ mod test { published: false, comments: vec![], stats: Stats { views: 0, score: 0 }, + balances: BTreeMap::new(), }; + let mut balances: BTreeMap = BTreeMap::new(); + balances.insert("chareen".into(), 347); let max = Item { model: Model::Post { category: "fun".to_string(), @@ -122,6 +128,7 @@ mod test { views: std::u64::MAX, score: std::i64::MIN, }, + balances, }; // binary @@ -172,6 +179,9 @@ mod test { author: Address("no-reply@domain.com".to_owned()), }); + let mut balances: BTreeMap = BTreeMap::new(); + balances.insert("chareen".into(), 347); + let item = ModelOrItem::Item(Item { model: Model::Comment, title: "Title".to_owned(), @@ -183,6 +193,7 @@ mod test { views: 110, score: 12, }, + balances }); assert_eq!( diff --git a/src/ser/map.rs b/src/ser/map.rs new file mode 100644 index 000000000..6b35c7017 --- /dev/null +++ b/src/ser/map.rs @@ -0,0 +1,48 @@ +use serde::ser; + +use crate::ser::{Error, Result, Serializer}; + +pub struct SerializeMap<'a> +{ + ser: &'a mut Serializer, + first: bool, +} + +impl<'a> SerializeMap<'a> +{ + pub(crate) fn new(ser: &'a mut Serializer) -> Self { + SerializeMap { ser, first: true } + } +} + +impl<'a> ser::SerializeMap for SerializeMap<'a> +{ + type Ok = (); + type Error = Error; + + fn end(self) -> Result { + self.ser.buf.push(b'}'); + Ok(()) + } + + fn serialize_key(&mut self, key: &T) -> Result<()> + where + T: ser::Serialize, + { + if !self.first { + self.ser.buf.push(b','); + } + self.first = false; + key.serialize(&mut *self.ser)?; + self.ser.buf.extend_from_slice(b":"); + Ok(()) + } + + fn serialize_value(&mut self, value: &T) -> Result<()> + where + T: ser::Serialize, + { + value.serialize(&mut *self.ser)?; + Ok(()) + } +} \ No newline at end of file diff --git a/src/ser/mod.rs b/src/ser/mod.rs index b3d8a24d2..bc1beef3d 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -8,9 +8,12 @@ use std::vec::Vec; use self::seq::SerializeSeq; use self::struct_::SerializeStruct; +use self::map::SerializeMap; mod seq; mod struct_; +mod map; + /// Serialization result pub type Result = ::core::result::Result; @@ -153,7 +156,7 @@ impl<'a> ser::Serializer for &'a mut Serializer { type SerializeTuple = SerializeSeq<'a>; type SerializeTupleStruct = Unreachable; type SerializeTupleVariant = SerializeSeq<'a>; - type SerializeMap = Unreachable; + type SerializeMap = SerializeMap<'a>; type SerializeStruct = SerializeStruct<'a>; type SerializeStructVariant = SerializeStruct<'a>; @@ -400,7 +403,8 @@ impl<'a> ser::Serializer for &'a mut Serializer { } fn serialize_map(self, _len: Option) -> Result { - unreachable!() + self.buf.push(b'{'); + Ok(SerializeMap::new(self)) } fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { diff --git a/src/ser/struct_.rs b/src/ser/struct_.rs index 3898db82a..502d03460 100644 --- a/src/ser/struct_.rs +++ b/src/ser/struct_.rs @@ -3,13 +3,13 @@ use serde::ser; use crate::ser::{Error, Result, Serializer}; pub struct SerializeStruct<'a> { - de: &'a mut Serializer, + ser: &'a mut Serializer, first: bool, } impl<'a> SerializeStruct<'a> { - pub(crate) fn new(de: &'a mut Serializer) -> Self { - SerializeStruct { de, first: true } + pub(crate) fn new(ser: &'a mut Serializer) -> Self { + SerializeStruct { ser, first: true } } } @@ -23,21 +23,21 @@ impl<'a> ser::SerializeStruct for SerializeStruct<'a> { { // XXX if `value` is `None` we not produce any output for this field if !self.first { - self.de.buf.push(b','); + self.ser.buf.push(b','); } self.first = false; - self.de.buf.push(b'"'); - self.de.buf.extend_from_slice(key.as_bytes()); - self.de.buf.extend_from_slice(b"\":"); + self.ser.buf.push(b'"'); + self.ser.buf.extend_from_slice(key.as_bytes()); + self.ser.buf.extend_from_slice(b"\":"); - value.serialize(&mut *self.de)?; + value.serialize(&mut *self.ser)?; Ok(()) } fn end(self) -> Result { - self.de.buf.push(b'}'); + self.ser.buf.push(b'}'); Ok(()) } } @@ -52,24 +52,24 @@ impl<'a> ser::SerializeStructVariant for SerializeStruct<'a> { { // XXX if `value` is `None` we not produce any output for this field if !self.first { - self.de.buf.push(b','); + self.ser.buf.push(b','); } self.first = false; - self.de.buf.push(b'"'); - self.de.buf.extend_from_slice(key.as_bytes()); - self.de.buf.extend_from_slice(b"\":"); + self.ser.buf.push(b'"'); + self.ser.buf.extend_from_slice(key.as_bytes()); + self.ser.buf.extend_from_slice(b"\":"); - value.serialize(&mut *self.de)?; + value.serialize(&mut *self.ser)?; Ok(()) } fn end(self) -> Result { // close struct - self.de.buf.push(b'}'); + self.ser.buf.push(b'}'); // close surrounding enum - self.de.buf.push(b'}'); + self.ser.buf.push(b'}'); Ok(()) } } From a0a2658799039906ca7cd5bee3b4347ca1409f7a Mon Sep 17 00:00:00 2001 From: CyberHoward Date: Tue, 8 Nov 2022 16:57:48 +0000 Subject: [PATCH 126/178] remove comment --- src/de/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 51d65e165..c4e265144 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -576,9 +576,6 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { self.deserialize_seq(visitor) } - /// Unsupported. Can’t make an arbitrary-sized map in no-std. Use a struct with a - /// known format, or implement a custom map deserializer / visitor: - /// https://serde.rs/deserialize-map.html fn deserialize_map(self, visitor: V) -> Result where V: Visitor<'de>, From cb45eea25e4e1689038281c9c931331d00b59635 Mon Sep 17 00:00:00 2001 From: CyberHoward Date: Wed, 9 Nov 2022 08:49:55 +0000 Subject: [PATCH 127/178] deserialize with str method for String --- src/de/map.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/de/map.rs b/src/de/map.rs index 41f9fe4d7..c15d6595e 100644 --- a/src/de/map.rs +++ b/src/de/map.rs @@ -163,11 +163,11 @@ impl<'de, 'a> de::Deserializer<'de> for MapKey<'a, 'de> { self.de.deserialize_str(visitor) } - fn deserialize_string(self, _visitor: V) -> Result + fn deserialize_string(self, visitor: V) -> Result where V: Visitor<'de>, { - unreachable!() + self.deserialize_str(visitor) } fn deserialize_bytes(self, _visitor: V) -> Result From ebb363bd8851b1f2ad2d33bc126264a7a8e9ee73 Mon Sep 17 00:00:00 2001 From: CyberHoward Date: Wed, 9 Nov 2022 08:51:07 +0000 Subject: [PATCH 128/178] rm serde-cw-value dependency --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1969d3785..5ffafaf8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,6 @@ exclude = [ [dependencies] serde = { version = "^1.0.80", default-features = false, features = ["alloc"] } -serde-cw-value = "0.7.0" [dev-dependencies] serde_derive = "^1.0.80" From 2112dfcf3bcb24e94e5e97ec117504482fc8fb7d Mon Sep 17 00:00:00 2001 From: CyberHoward Date: Wed, 16 Nov 2022 09:52:05 +0000 Subject: [PATCH 129/178] let map deserializer handle deserialization --- src/de/map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/map.rs b/src/de/map.rs index c15d6595e..f9dde70e9 100644 --- a/src/de/map.rs +++ b/src/de/map.rs @@ -167,7 +167,7 @@ impl<'de, 'a> de::Deserializer<'de> for MapKey<'a, 'de> { where V: Visitor<'de>, { - self.deserialize_str(visitor) + self.de.deserialize_string(visitor) } fn deserialize_bytes(self, _visitor: V) -> Result From 62b6f2636ac9b8eaee7a1c4a8e8c79762e4e6be8 Mon Sep 17 00:00:00 2001 From: CyberHoward Date: Wed, 16 Nov 2022 10:15:36 +0000 Subject: [PATCH 130/178] add serialization test for map structure --- src/ser/mod.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index bc1beef3d..9cde7aaa5 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -534,6 +534,7 @@ impl ser::SerializeStructVariant for Unreachable { #[cfg(test)] mod tests { + use super::to_string; use serde_derive::Serialize; @@ -983,6 +984,30 @@ mod tests { ); } + #[test] + fn btree_map() { + use std::collections::BTreeMap; + // empty map + assert_eq!(to_string(&BTreeMap::<(),()>::new()).unwrap(), r#"{}"#); + + let mut two_values = BTreeMap::new(); + two_values.insert("my_name", "joseph"); + two_values.insert("her_name", "aline"); + assert_eq!( + to_string(&two_values).unwrap(), + r#"{"her_name":"aline","my_name":"joseph"}"# + ); + + let mut nested_map = BTreeMap::new(); + nested_map.insert("two_entries", two_values.clone()); + + two_values.remove("my_name"); + nested_map.insert("one_entry", two_values); + assert_eq!( + to_string(&nested_map).unwrap(), + r#"{"one_entry":{"her_name":"aline"},"two_entries":{"her_name":"aline","my_name":"joseph"}}"# + ); + } use serde_derive::Deserialize; #[test] From ca4e8ec2adec22e895703262b52b668eb72dd074 Mon Sep 17 00:00:00 2001 From: CyberHoward Date: Wed, 16 Nov 2022 10:16:15 +0000 Subject: [PATCH 131/178] format --- src/de/mod.rs | 1 - src/lib.rs | 10 +++++----- src/ser/map.rs | 11 ++++------- src/ser/mod.rs | 7 +++---- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index c4e265144..5315fc587 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -593,7 +593,6 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { } else { Err(Error::InvalidType) } - } fn deserialize_struct( diff --git a/src/lib.rs b/src/lib.rs index 2f8bd65f4..4ce4e51e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,7 +97,7 @@ mod test { published: bool, comments: Vec, stats: Stats, - balances: BTreeMap, + balances: BTreeMap, } #[test] @@ -110,9 +110,9 @@ mod test { published: false, comments: vec![], stats: Stats { views: 0, score: 0 }, - balances: BTreeMap::new(), + balances: BTreeMap::new(), }; - let mut balances: BTreeMap = BTreeMap::new(); + let mut balances: BTreeMap = BTreeMap::new(); balances.insert("chareen".into(), 347); let max = Item { model: Model::Post { @@ -179,7 +179,7 @@ mod test { author: Address("no-reply@domain.com".to_owned()), }); - let mut balances: BTreeMap = BTreeMap::new(); + let mut balances: BTreeMap = BTreeMap::new(); balances.insert("chareen".into(), 347); let item = ModelOrItem::Item(Item { @@ -193,7 +193,7 @@ mod test { views: 110, score: 12, }, - balances + balances, }); assert_eq!( diff --git a/src/ser/map.rs b/src/ser/map.rs index 6b35c7017..51415f8e6 100644 --- a/src/ser/map.rs +++ b/src/ser/map.rs @@ -2,21 +2,18 @@ use serde::ser; use crate::ser::{Error, Result, Serializer}; -pub struct SerializeMap<'a> -{ +pub struct SerializeMap<'a> { ser: &'a mut Serializer, first: bool, } -impl<'a> SerializeMap<'a> -{ +impl<'a> SerializeMap<'a> { pub(crate) fn new(ser: &'a mut Serializer) -> Self { SerializeMap { ser, first: true } } } -impl<'a> ser::SerializeMap for SerializeMap<'a> -{ +impl<'a> ser::SerializeMap for SerializeMap<'a> { type Ok = (); type Error = Error; @@ -45,4 +42,4 @@ impl<'a> ser::SerializeMap for SerializeMap<'a> value.serialize(&mut *self.ser)?; Ok(()) } -} \ No newline at end of file +} diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 9cde7aaa5..f3cb8c433 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -6,14 +6,13 @@ use serde::ser; use std::vec::Vec; +use self::map::SerializeMap; use self::seq::SerializeSeq; use self::struct_::SerializeStruct; -use self::map::SerializeMap; +mod map; mod seq; mod struct_; -mod map; - /// Serialization result pub type Result = ::core::result::Result; @@ -988,7 +987,7 @@ mod tests { fn btree_map() { use std::collections::BTreeMap; // empty map - assert_eq!(to_string(&BTreeMap::<(),()>::new()).unwrap(), r#"{}"#); + assert_eq!(to_string(&BTreeMap::<(), ()>::new()).unwrap(), r#"{}"#); let mut two_values = BTreeMap::new(); two_values.insert("my_name", "joseph"); From 2e6554a0f498d5ecf19e543d3382d01bccf568a2 Mon Sep 17 00:00:00 2001 From: CyberHoward Date: Wed, 16 Nov 2022 11:11:19 +0000 Subject: [PATCH 132/178] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fda356773..f43e7dd71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +### Unreleased +- Add support for map (de)serialization. + ## [0.4.1] - 2022-05-05 ### Changed From 39a4cb8cc68e4975fd7049f7fdec441f424f3555 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 16 Nov 2022 21:09:45 +0100 Subject: [PATCH 133/178] Move Deserialize import into test --- src/ser/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index f3cb8c433..2183d49f4 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -1007,10 +1007,11 @@ mod tests { r#"{"one_entry":{"her_name":"aline"},"two_entries":{"her_name":"aline","my_name":"joseph"}}"# ); } - use serde_derive::Deserialize; #[test] fn serialize_embedded_enum() { + use serde_derive::Deserialize; + #[derive(Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "lowercase")] pub enum MyResult { From b677f792f1956b0cc13dff9ab14344f72fdb5120 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 16 Nov 2022 21:10:08 +0100 Subject: [PATCH 134/178] Add HashMap test --- src/ser/mod.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 2183d49f4..bc46c215d 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -1008,6 +1008,30 @@ mod tests { ); } + #[test] + fn hash_map() { + use std::collections::HashMap; + + // empty map + assert_eq!(to_string(&HashMap::<(), ()>::new()).unwrap(), r#"{}"#); + + // One element + let mut map = HashMap::new(); + map.insert("my_age", 28); + assert_eq!(to_string(&map).unwrap(), r#"{"my_age":28}"#); + + // HashMap does not have deterministic iteration order (except in the Wasm target). + // So the two element map is serialized as one of two options. + let mut two_values = HashMap::new(); + two_values.insert("my_name", "joseph"); + two_values.insert("her_name", "aline"); + let serialized = to_string(&two_values).unwrap(); + assert!( + serialized == r#"{"her_name":"aline","my_name":"joseph"}"# + || serialized == r#"{"my_name":"joseph","her_name":"aline"}"# + ); + } + #[test] fn serialize_embedded_enum() { use serde_derive::Deserialize; From cdc78130b25d65981b74a5e4a10a9f8667292d36 Mon Sep 17 00:00:00 2001 From: CyberHoward Date: Mon, 21 Nov 2022 14:50:44 +0000 Subject: [PATCH 135/178] Implement MapKeySerializer --- src/ser/map.rs | 5 +- src/ser/mod.rs | 328 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 331 insertions(+), 2 deletions(-) diff --git a/src/ser/map.rs b/src/ser/map.rs index 51415f8e6..c8b980541 100644 --- a/src/ser/map.rs +++ b/src/ser/map.rs @@ -2,6 +2,8 @@ use serde::ser; use crate::ser::{Error, Result, Serializer}; +use super::MapKeySerializer; + pub struct SerializeMap<'a> { ser: &'a mut Serializer, first: bool, @@ -30,7 +32,8 @@ impl<'a> ser::SerializeMap for SerializeMap<'a> { self.ser.buf.push(b','); } self.first = false; - key.serialize(&mut *self.ser)?; + // Use key serializer to unsure key type validity. + key.serialize(MapKeySerializer { ser: self.ser })?; self.ser.buf.extend_from_slice(b":"); Ok(()) } diff --git a/src/ser/mod.rs b/src/ser/mod.rs index bc46c215d..52f120420 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -2,7 +2,7 @@ use std::{error, fmt}; -use serde::ser; +use serde::{ser, Serialize}; use std::vec::Vec; @@ -531,6 +531,192 @@ impl ser::SerializeStructVariant for Unreachable { } } +/// Wrapper around Serializer that only allows serialization of valid JSON key types (strings). +struct MapKeySerializer<'a> { + ser: &'a mut Serializer, +} + +fn key_must_be_a_string() -> Error { + Error::Custom("JSON object key is required to be a string type.".to_string()) +} + +impl<'a> ser::Serializer for MapKeySerializer<'a> { + type Ok = (); + type Error = Error; + type SerializeSeq = SerializeSeq<'a>; + type SerializeTuple = SerializeSeq<'a>; + type SerializeTupleStruct = Unreachable; + type SerializeTupleVariant = SerializeSeq<'a>; + type SerializeMap = SerializeMap<'a>; + type SerializeStruct = SerializeStruct<'a>; + type SerializeStructVariant = SerializeStruct<'a>; + + fn serialize_bool(self, _value: bool) -> Result<()> { + Err(key_must_be_a_string()) + } + #[inline] + fn serialize_str(self, value: &str) -> Result<()> { + self.ser.serialize_str(value) + } + + #[inline] + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result<()> { + self.ser.serialize_str(variant) + } + + #[inline] + fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + value.serialize(self) + } + + fn serialize_i8(self, _value: i8) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_i16(self, _value: i16) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_i32(self, _value: i32) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_i64(self, _value: i64) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_i128(self, _value: i128) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_u8(self, _value: u8) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_u16(self, _value: u16) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_u32(self, _value: u32) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_u64(self, _value: u64) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_u128(self, _value: u128) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_f32(self, _value: f32) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_f64(self, _value: f64) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_char(self, value: char) -> Result<()> { + self.ser.serialize_str(&value.to_string()) + } + + fn serialize_bytes(self, _value: &[u8]) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_unit(self) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _value: &T, + ) -> Result<()> + where + T: ?Sized + Serialize, + { + Err(key_must_be_a_string()) + } + + fn serialize_none(self) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_some(self, _value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + Err(key_must_be_a_string()) + } + + fn serialize_seq(self, _len: Option) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_tuple(self, _len: usize) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_map(self, _len: Option) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(key_must_be_a_string()) + } + + fn collect_str(self, _value: &T) -> Result<()> + where + T: ?Sized + fmt::Display, + { + unreachable!() + } +} + #[cfg(test)] mod tests { @@ -1020,6 +1206,25 @@ mod tests { map.insert("my_age", 28); assert_eq!(to_string(&map).unwrap(), r#"{"my_age":28}"#); + #[derive(Debug, Serialize, PartialEq, Eq, Hash)] + pub struct NewType(String); + + // New type wrappers around String types work as keys + let mut map = HashMap::new(); + map.insert(NewType(String::from("my_age")), 44); + assert_eq!(to_string(&map).unwrap(), r#"{"my_age":44}"#); + + #[derive(Debug, Serialize, PartialEq, Eq, Hash)] + #[serde(rename_all = "lowercase")] + pub enum MyResult { + Err, + } + + // Unit variants are also valid keys + let mut map = HashMap::new(); + map.insert(MyResult::Err, 404); + assert_eq!(to_string(&map).unwrap(), r#"{"err":404}"#); + // HashMap does not have deterministic iteration order (except in the Wasm target). // So the two element map is serialized as one of two options. let mut two_values = HashMap::new(); @@ -1032,6 +1237,127 @@ mod tests { ); } + #[test] + fn invalid_json_key() { + use crate::ser::key_must_be_a_string; + use std::collections::HashMap; + + // i8 key + let mut map = HashMap::new(); + map.insert(1i8, "my_age"); + assert_eq!( + to_string(&map).unwrap_err().to_string(), + key_must_be_a_string().to_string() + ); + + // i16 key + let mut map = HashMap::new(); + map.insert(40i16, "my_age"); + assert_eq!( + to_string(&map).unwrap_err().to_string(), + key_must_be_a_string().to_string() + ); + + // i32 key + let mut map = HashMap::new(); + map.insert(40i32, "my_age"); + assert_eq!( + to_string(&map).unwrap_err().to_string(), + key_must_be_a_string().to_string() + ); + + // i64 key + let mut map = HashMap::new(); + map.insert(40i64, "my_age"); + assert_eq!( + to_string(&map).unwrap_err().to_string(), + key_must_be_a_string().to_string() + ); + + // u8 key + let mut map = HashMap::new(); + map.insert(1u8, "my_age"); + assert_eq!( + to_string(&map).unwrap_err().to_string(), + key_must_be_a_string().to_string() + ); + + // u16 key + let mut map = HashMap::new(); + map.insert(40u16, "my_age"); + assert_eq!( + to_string(&map).unwrap_err().to_string(), + key_must_be_a_string().to_string() + ); + + // u32 key + let mut map = HashMap::new(); + map.insert(40u32, "my_age"); + assert_eq!( + to_string(&map).unwrap_err().to_string(), + key_must_be_a_string().to_string() + ); + + // u64 key + let mut map = HashMap::new(); + map.insert(40u64, "my_age"); + assert_eq!( + to_string(&map).unwrap_err().to_string(), + key_must_be_a_string().to_string() + ); + + #[derive(Debug, Serialize, PartialEq, Eq, Hash)] + #[serde(rename_all = "lowercase")] + pub enum MyResult { + Unit(()), + Ok(Response), + } + #[derive(Debug, Serialize, PartialEq, Eq, Hash)] + pub struct Response { + pub log: Option, + pub count: i64, + pub list: Vec, + } + + // unit enum + let mut map = HashMap::new(); + map.insert(MyResult::Unit(()), "my_age"); + assert_eq!( + to_string(&map).unwrap_err().to_string(), + key_must_be_a_string().to_string() + ); + + // struct enum + let mut map = HashMap::new(); + map.insert( + MyResult::Ok(Response { + log: None, + count: 1, + list: vec![6], + }), + "my_age", + ); + assert_eq!( + to_string(&map).unwrap_err().to_string(), + key_must_be_a_string().to_string() + ); + + // Struct + let mut map = HashMap::new(); + map.insert( + Response { + log: None, + count: 1, + list: vec![6], + }, + "my_age", + ); + assert_eq!( + to_string(&map).unwrap_err().to_string(), + key_must_be_a_string().to_string() + ); + } + #[test] fn serialize_embedded_enum() { use serde_derive::Deserialize; From d1f21abfbdc0f1f4c373395d95a9f24fc4e97b60 Mon Sep 17 00:00:00 2001 From: CyberHoward Date: Tue, 22 Nov 2022 11:10:52 +0000 Subject: [PATCH 136/178] implement number key serialization as string --- src/ser/mod.rs | 137 +++++++++++++++++++++++++++---------------------- 1 file changed, 75 insertions(+), 62 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 52f120420..dbd351479 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -540,6 +540,28 @@ fn key_must_be_a_string() -> Error { Error::Custom("JSON object key is required to be a string type.".to_string()) } +macro_rules! serialize_unsigned_key { + ($self:ident, $N:expr, $v:expr) => {{ + let ser = $self.ser; + ser.buf.push(b'"'); + let res: Result = serialize_unsigned!(ser, $N, $v); + res?; + ser.buf.push(b'"'); + Ok(()) + }}; +} + +macro_rules! serialize_signed_key { + ($self:ident, $N:expr, $v:expr, $ixx:ident, $uxx:ident) => {{ + let ser = $self.ser; + ser.buf.push(b'"'); + let res: Result = serialize_signed!(ser, $N, $v, $ixx, $uxx); + res?; + ser.buf.push(b'"'); + Ok(()) + }}; +} + impl<'a> ser::Serializer for MapKeySerializer<'a> { type Ok = (); type Error = Error; @@ -577,44 +599,44 @@ impl<'a> ser::Serializer for MapKeySerializer<'a> { value.serialize(self) } - fn serialize_i8(self, _value: i8) -> Result<()> { - Err(key_must_be_a_string()) + fn serialize_i8(self, value: i8) -> Result<()> { + serialize_signed_key!(self, 4, value, i8, u8) } - fn serialize_i16(self, _value: i16) -> Result<()> { - Err(key_must_be_a_string()) + fn serialize_i16(self, value: i16) -> Result<()> { + serialize_signed_key!(self, 6, value, i16, u16) } - fn serialize_i32(self, _value: i32) -> Result<()> { - Err(key_must_be_a_string()) + fn serialize_i32(self, value: i32) -> Result<()> { + serialize_signed_key!(self, 11, value, i32, u32) } - fn serialize_i64(self, _value: i64) -> Result<()> { - Err(key_must_be_a_string()) + fn serialize_i64(self, value: i64) -> Result<()> { + serialize_signed_key!(self, 20, value, i64, u64) } - fn serialize_i128(self, _value: i128) -> Result<()> { - Err(key_must_be_a_string()) + fn serialize_i128(self, value: i128) -> Result<()> { + serialize_signed_key!(self, 40, value, i128, u128) } - fn serialize_u8(self, _value: u8) -> Result<()> { - Err(key_must_be_a_string()) + fn serialize_u8(self, value: u8) -> Result<()> { + serialize_unsigned_key!(self, 3, value) } - fn serialize_u16(self, _value: u16) -> Result<()> { - Err(key_must_be_a_string()) + fn serialize_u16(self, value: u16) -> Result<()> { + serialize_unsigned_key!(self, 5, value) } - fn serialize_u32(self, _value: u32) -> Result<()> { - Err(key_must_be_a_string()) + fn serialize_u32(self, value: u32) -> Result<()> { + serialize_unsigned_key!(self, 10, value) } - fn serialize_u64(self, _value: u64) -> Result<()> { - Err(key_must_be_a_string()) + fn serialize_u64(self, value: u64) -> Result<()> { + serialize_unsigned_key!(self, 20, value) } - fn serialize_u128(self, _value: u128) -> Result<()> { - Err(key_must_be_a_string()) + fn serialize_u128(self, value: u128) -> Result<()> { + serialize_unsigned_key!(self, 39, value) } fn serialize_f32(self, _value: f32) -> Result<()> { @@ -1238,73 +1260,64 @@ mod tests { } #[test] - fn invalid_json_key() { - use crate::ser::key_must_be_a_string; + fn number_key() { use std::collections::HashMap; // i8 key let mut map = HashMap::new(); - map.insert(1i8, "my_age"); - assert_eq!( - to_string(&map).unwrap_err().to_string(), - key_must_be_a_string().to_string() - ); + map.insert(10i8, "my_age"); + assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); // i16 key let mut map = HashMap::new(); - map.insert(40i16, "my_age"); - assert_eq!( - to_string(&map).unwrap_err().to_string(), - key_must_be_a_string().to_string() - ); + map.insert(10i16, "my_age"); + assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); // i32 key let mut map = HashMap::new(); - map.insert(40i32, "my_age"); - assert_eq!( - to_string(&map).unwrap_err().to_string(), - key_must_be_a_string().to_string() - ); + map.insert(10i32, "my_age"); + assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); // i64 key let mut map = HashMap::new(); - map.insert(40i64, "my_age"); - assert_eq!( - to_string(&map).unwrap_err().to_string(), - key_must_be_a_string().to_string() - ); + map.insert(10i64, "my_age"); + assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); + + // i128 key + let mut map = HashMap::new(); + map.insert(10i128, "my_age"); + assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); // u8 key let mut map = HashMap::new(); - map.insert(1u8, "my_age"); - assert_eq!( - to_string(&map).unwrap_err().to_string(), - key_must_be_a_string().to_string() - ); + map.insert(10u8, "my_age"); + assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); // u16 key let mut map = HashMap::new(); - map.insert(40u16, "my_age"); - assert_eq!( - to_string(&map).unwrap_err().to_string(), - key_must_be_a_string().to_string() - ); + map.insert(10u16, "my_age"); + assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); // u32 key let mut map = HashMap::new(); - map.insert(40u32, "my_age"); - assert_eq!( - to_string(&map).unwrap_err().to_string(), - key_must_be_a_string().to_string() - ); + map.insert(10u32, "my_age"); + assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); // u64 key let mut map = HashMap::new(); - map.insert(40u64, "my_age"); - assert_eq!( - to_string(&map).unwrap_err().to_string(), - key_must_be_a_string().to_string() - ); + map.insert(10u64, "my_age"); + assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); + + // u128 key + let mut map = HashMap::new(); + map.insert(10u128, "my_age"); + assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); + } + + #[test] + fn invalid_json_key() { + use crate::ser::key_must_be_a_string; + use std::collections::HashMap; #[derive(Debug, Serialize, PartialEq, Eq, Hash)] #[serde(rename_all = "lowercase")] From 784121c2f48f033d8452cb6600c5c416421e72df Mon Sep 17 00:00:00 2001 From: CyberHoward Date: Tue, 22 Nov 2022 11:31:21 +0000 Subject: [PATCH 137/178] add failing number key deserialization test --- src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 4ce4e51e3..f2f5c244e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -214,4 +214,16 @@ mod test { item ); } + + // #[test] + // fn numbered_keys() { + // let mut ranking: BTreeMap = BTreeMap::new(); + // ranking.insert(1, "Elon".to_string()); + // ranking.insert(2, "Bazos".to_string()); + + // assert_eq!( + // from_str::>(&to_string(&ranking).unwrap()).unwrap(), + // ranking + // ); + // } } From c1b254c97495f47a9791df2546a9551cdfa85b22 Mon Sep 17 00:00:00 2001 From: CyberHoward Date: Tue, 22 Nov 2022 11:39:42 +0000 Subject: [PATCH 138/178] move MapKeySerializer implementation --- src/ser/map.rs | 214 +++++++++++++++++++++++++++++++++++++++++++++++- src/ser/mod.rs | 216 ++----------------------------------------------- 2 files changed, 218 insertions(+), 212 deletions(-) diff --git a/src/ser/map.rs b/src/ser/map.rs index c8b980541..c82c2ee34 100644 --- a/src/ser/map.rs +++ b/src/ser/map.rs @@ -1,8 +1,10 @@ -use serde::ser; +use std::fmt; + +use serde::{ser, Serialize}; use crate::ser::{Error, Result, Serializer}; -use super::MapKeySerializer; +use super::{seq::SerializeSeq, struct_::SerializeStruct, Unreachable}; pub struct SerializeMap<'a> { ser: &'a mut Serializer, @@ -46,3 +48,211 @@ impl<'a> ser::SerializeMap for SerializeMap<'a> { Ok(()) } } + +/// Wrapper around Serializer that only allows serialization of valid JSON key types (strings). +struct MapKeySerializer<'a> { + ser: &'a mut Serializer, +} + +pub(crate) fn key_must_be_a_string() -> Error { + Error::Custom("JSON object key is required to be a string type.".to_string()) +} + +macro_rules! serialize_unsigned_key { + ($self:ident, $N:expr, $v:expr) => {{ + let ser = $self.ser; + ser.buf.push(b'"'); + let res: Result = super::serialize_unsigned!(ser, $N, $v); + res?; + ser.buf.push(b'"'); + Ok(()) + }}; +} + +macro_rules! serialize_signed_key { + ($self:ident, $N:expr, $v:expr, $ixx:ident, $uxx:ident) => {{ + let ser = $self.ser; + ser.buf.push(b'"'); + let res: Result = super::serialize_signed!(ser, $N, $v, $ixx, $uxx); + res?; + ser.buf.push(b'"'); + Ok(()) + }}; +} + +impl<'a> ser::Serializer for MapKeySerializer<'a> { + type Ok = (); + type Error = Error; + type SerializeSeq = SerializeSeq<'a>; + type SerializeTuple = SerializeSeq<'a>; + type SerializeTupleStruct = Unreachable; + type SerializeTupleVariant = SerializeSeq<'a>; + type SerializeMap = SerializeMap<'a>; + type SerializeStruct = SerializeStruct<'a>; + type SerializeStructVariant = SerializeStruct<'a>; + + fn serialize_bool(self, _value: bool) -> Result<()> { + Err(key_must_be_a_string()) + } + #[inline] + fn serialize_str(self, value: &str) -> Result<()> { + self.ser.serialize_str(value) + } + + #[inline] + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result<()> { + self.ser.serialize_str(variant) + } + + #[inline] + fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + value.serialize(self) + } + + fn serialize_i8(self, value: i8) -> Result<()> { + serialize_signed_key!(self, 4, value, i8, u8) + } + + fn serialize_i16(self, value: i16) -> Result<()> { + serialize_signed_key!(self, 6, value, i16, u16) + } + + fn serialize_i32(self, value: i32) -> Result<()> { + serialize_signed_key!(self, 11, value, i32, u32) + } + + fn serialize_i64(self, value: i64) -> Result<()> { + serialize_signed_key!(self, 20, value, i64, u64) + } + + fn serialize_i128(self, value: i128) -> Result<()> { + serialize_signed_key!(self, 40, value, i128, u128) + } + + fn serialize_u8(self, value: u8) -> Result<()> { + serialize_unsigned_key!(self, 3, value) + } + + fn serialize_u16(self, value: u16) -> Result<()> { + serialize_unsigned_key!(self, 5, value) + } + + fn serialize_u32(self, value: u32) -> Result<()> { + serialize_unsigned_key!(self, 10, value) + } + + fn serialize_u64(self, value: u64) -> Result<()> { + serialize_unsigned_key!(self, 20, value) + } + + fn serialize_u128(self, value: u128) -> Result<()> { + serialize_unsigned_key!(self, 39, value) + } + + fn serialize_f32(self, _value: f32) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_f64(self, _value: f64) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_char(self, value: char) -> Result<()> { + self.ser.serialize_str(&value.to_string()) + } + + fn serialize_bytes(self, _value: &[u8]) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_unit(self) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _value: &T, + ) -> Result<()> + where + T: ?Sized + Serialize, + { + Err(key_must_be_a_string()) + } + + fn serialize_none(self) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_some(self, _value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + Err(key_must_be_a_string()) + } + + fn serialize_seq(self, _len: Option) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_tuple(self, _len: usize) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_map(self, _len: Option) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(key_must_be_a_string()) + } + + fn collect_str(self, _value: &T) -> Result<()> + where + T: ?Sized + fmt::Display, + { + unreachable!() + } +} diff --git a/src/ser/mod.rs b/src/ser/mod.rs index dbd351479..8490157b8 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -2,7 +2,7 @@ use std::{error, fmt}; -use serde::{ser, Serialize}; +use serde::ser; use std::vec::Vec; @@ -99,6 +99,8 @@ macro_rules! serialize_unsigned { Ok(()) }}; } +// Export for use in map +pub(crate) use serialize_unsigned; macro_rules! serialize_signed { ($self:ident, $N:expr, $v:expr, $ixx:ident, $uxx:ident) => {{ @@ -133,6 +135,8 @@ macro_rules! serialize_signed { Ok(()) }}; } +// Export for use in map +pub(crate) use serialize_signed; /// Upper-case hex for value in 0..16, encoded as ASCII bytes fn hex_4bit(c: u8) -> u8 { @@ -531,214 +535,6 @@ impl ser::SerializeStructVariant for Unreachable { } } -/// Wrapper around Serializer that only allows serialization of valid JSON key types (strings). -struct MapKeySerializer<'a> { - ser: &'a mut Serializer, -} - -fn key_must_be_a_string() -> Error { - Error::Custom("JSON object key is required to be a string type.".to_string()) -} - -macro_rules! serialize_unsigned_key { - ($self:ident, $N:expr, $v:expr) => {{ - let ser = $self.ser; - ser.buf.push(b'"'); - let res: Result = serialize_unsigned!(ser, $N, $v); - res?; - ser.buf.push(b'"'); - Ok(()) - }}; -} - -macro_rules! serialize_signed_key { - ($self:ident, $N:expr, $v:expr, $ixx:ident, $uxx:ident) => {{ - let ser = $self.ser; - ser.buf.push(b'"'); - let res: Result = serialize_signed!(ser, $N, $v, $ixx, $uxx); - res?; - ser.buf.push(b'"'); - Ok(()) - }}; -} - -impl<'a> ser::Serializer for MapKeySerializer<'a> { - type Ok = (); - type Error = Error; - type SerializeSeq = SerializeSeq<'a>; - type SerializeTuple = SerializeSeq<'a>; - type SerializeTupleStruct = Unreachable; - type SerializeTupleVariant = SerializeSeq<'a>; - type SerializeMap = SerializeMap<'a>; - type SerializeStruct = SerializeStruct<'a>; - type SerializeStructVariant = SerializeStruct<'a>; - - fn serialize_bool(self, _value: bool) -> Result<()> { - Err(key_must_be_a_string()) - } - #[inline] - fn serialize_str(self, value: &str) -> Result<()> { - self.ser.serialize_str(value) - } - - #[inline] - fn serialize_unit_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - ) -> Result<()> { - self.ser.serialize_str(variant) - } - - #[inline] - fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> - where - T: ?Sized + Serialize, - { - value.serialize(self) - } - - fn serialize_i8(self, value: i8) -> Result<()> { - serialize_signed_key!(self, 4, value, i8, u8) - } - - fn serialize_i16(self, value: i16) -> Result<()> { - serialize_signed_key!(self, 6, value, i16, u16) - } - - fn serialize_i32(self, value: i32) -> Result<()> { - serialize_signed_key!(self, 11, value, i32, u32) - } - - fn serialize_i64(self, value: i64) -> Result<()> { - serialize_signed_key!(self, 20, value, i64, u64) - } - - fn serialize_i128(self, value: i128) -> Result<()> { - serialize_signed_key!(self, 40, value, i128, u128) - } - - fn serialize_u8(self, value: u8) -> Result<()> { - serialize_unsigned_key!(self, 3, value) - } - - fn serialize_u16(self, value: u16) -> Result<()> { - serialize_unsigned_key!(self, 5, value) - } - - fn serialize_u32(self, value: u32) -> Result<()> { - serialize_unsigned_key!(self, 10, value) - } - - fn serialize_u64(self, value: u64) -> Result<()> { - serialize_unsigned_key!(self, 20, value) - } - - fn serialize_u128(self, value: u128) -> Result<()> { - serialize_unsigned_key!(self, 39, value) - } - - fn serialize_f32(self, _value: f32) -> Result<()> { - Err(key_must_be_a_string()) - } - - fn serialize_f64(self, _value: f64) -> Result<()> { - Err(key_must_be_a_string()) - } - - fn serialize_char(self, value: char) -> Result<()> { - self.ser.serialize_str(&value.to_string()) - } - - fn serialize_bytes(self, _value: &[u8]) -> Result<()> { - Err(key_must_be_a_string()) - } - - fn serialize_unit(self) -> Result<()> { - Err(key_must_be_a_string()) - } - - fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { - Err(key_must_be_a_string()) - } - - fn serialize_newtype_variant( - self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _value: &T, - ) -> Result<()> - where - T: ?Sized + Serialize, - { - Err(key_must_be_a_string()) - } - - fn serialize_none(self) -> Result<()> { - Err(key_must_be_a_string()) - } - - fn serialize_some(self, _value: &T) -> Result<()> - where - T: ?Sized + Serialize, - { - Err(key_must_be_a_string()) - } - - fn serialize_seq(self, _len: Option) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_tuple(self, _len: usize) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_tuple_struct( - self, - _name: &'static str, - _len: usize, - ) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_tuple_variant( - self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _len: usize, - ) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_map(self, _len: Option) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_struct_variant( - self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _len: usize, - ) -> Result { - Err(key_must_be_a_string()) - } - - fn collect_str(self, _value: &T) -> Result<()> - where - T: ?Sized + fmt::Display, - { - unreachable!() - } -} - #[cfg(test)] mod tests { @@ -1316,7 +1112,7 @@ mod tests { #[test] fn invalid_json_key() { - use crate::ser::key_must_be_a_string; + use crate::ser::map::key_must_be_a_string; use std::collections::HashMap; #[derive(Debug, Serialize, PartialEq, Eq, Hash)] From 227daee0037643614ef8290dbbd73e0603aed497 Mon Sep 17 00:00:00 2001 From: CyberHoward Date: Tue, 22 Nov 2022 12:45:20 +0000 Subject: [PATCH 139/178] Implement deserialize for number map keys --- src/de/map.rs | 100 +++++++++++++++++++++++++++++++++++++++++--------- src/de/mod.rs | 15 ++++++++ src/lib.rs | 12 ------ 3 files changed, 97 insertions(+), 30 deletions(-) diff --git a/src/de/map.rs b/src/de/map.rs index f9dde70e9..17b88800f 100644 --- a/src/de/map.rs +++ b/src/de/map.rs @@ -1,6 +1,5 @@ -use serde::de::{self, Visitor}; - use crate::de::{Deserializer, Error}; +use serde::de::{self, Visitor}; pub struct MapAccess<'a, 'b> { de: &'a mut Deserializer<'b>, @@ -13,6 +12,56 @@ impl<'a, 'b> MapAccess<'a, 'b> { } } +macro_rules! deserialize_signed_key { + ($self:ident, $visitor:ident, $ixx:ident, $visit_ixx:ident) => {{ + let de = $self.de; + match de.parse_whitespace().ok_or(Error::EofWhileParsingValue)? { + b'"' => de.eat_char(), + _ => return Err(Error::InvalidType), + }; + + let result = match de.peek() { + // after rust merged or-patterns feature, these two clause can be merged. + // error[E0658]: or-patterns syntax is experimental + Some(b'0'..=b'9') => super::deserialize_signed!(de, $visitor, $ixx, $visit_ixx), + Some(b'-') => super::deserialize_signed!(de, $visitor, $ixx, $visit_ixx), + _ => return Err(Error::InvalidType), + }; + match de.peek() { + Some(b'"') => { + de.eat_char(); + result + } + _ => Err(Error::InvalidType), + } + }}; +} + +macro_rules! deserialize_unsigned_key { + ($self:ident, $visitor:ident, $ixx:ident, $visit_ixx:ident) => {{ + let de = $self.de; + match de.parse_whitespace().ok_or(Error::EofWhileParsingValue)? { + b'"' => de.eat_char(), + _ => return Err(Error::InvalidType), + }; + + let result = match de.peek() { + // after rust merged or-patterns feature, these two clause can be merged. + // error[E0658]: or-patterns syntax is experimental + Some(b'0'..=b'9') => super::deserialize_unsigned!(de, $visitor, $ixx, $visit_ixx), + Some(b'-') => super::deserialize_unsigned!(de, $visitor, $ixx, $visit_ixx), + _ => return Err(Error::InvalidType), + }; + match de.peek() { + Some(b'"') => { + de.eat_char(); + result + } + _ => Err(Error::InvalidType), + } + }}; +} + impl<'a, 'de> de::MapAccess<'de> for MapAccess<'a, 'de> { type Error = Error; @@ -79,60 +128,75 @@ impl<'de, 'a> de::Deserializer<'de> for MapKey<'a, 'de> { unreachable!() } - fn deserialize_i8(self, _visitor: V) -> Result + fn deserialize_i8(self, visitor: V) -> Result where V: Visitor<'de>, { - unreachable!() + deserialize_signed_key!(self, visitor, i8, visit_i8) } - fn deserialize_i16(self, _visitor: V) -> Result + fn deserialize_i16(self, visitor: V) -> Result where V: Visitor<'de>, { - unreachable!() + deserialize_signed_key!(self, visitor, i16, visit_i16) } - fn deserialize_i32(self, _visitor: V) -> Result + fn deserialize_i32(self, visitor: V) -> Result where V: Visitor<'de>, { - unreachable!() + deserialize_signed_key!(self, visitor, i32, visit_i32) } - fn deserialize_i64(self, _visitor: V) -> Result + fn deserialize_i64(self, visitor: V) -> Result where V: Visitor<'de>, { - unreachable!() + deserialize_signed_key!(self, visitor, i64, visit_i64) } - fn deserialize_u8(self, _visitor: V) -> Result + fn deserialize_i128(self, visitor: V) -> Result where V: Visitor<'de>, { - unreachable!() + // default implementation includes string unparsing + self.de.deserialize_i128(visitor) } - fn deserialize_u16(self, _visitor: V) -> Result + fn deserialize_u8(self, visitor: V) -> Result where V: Visitor<'de>, { - unreachable!() + deserialize_unsigned_key!(self, visitor, u8, visit_u8) } - fn deserialize_u32(self, _visitor: V) -> Result + fn deserialize_u16(self, visitor: V) -> Result where V: Visitor<'de>, { - unreachable!() + deserialize_unsigned_key!(self, visitor, u16, visit_u16) } - fn deserialize_u64(self, _visitor: V) -> Result + fn deserialize_u32(self, visitor: V) -> Result where V: Visitor<'de>, { - unreachable!() + deserialize_unsigned_key!(self, visitor, u32, visit_u32) + } + + fn deserialize_u64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + deserialize_unsigned_key!(self, visitor, u64, visit_u64) + } + + fn deserialize_u128(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.de.deserialize_u128(visitor) } fn deserialize_f32(self, _visitor: V) -> Result diff --git a/src/de/mod.rs b/src/de/mod.rs index 5315fc587..c17c7c4ba 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -203,6 +203,7 @@ macro_rules! deserialize_unsigned { } }}; } +pub(crate) use deserialize_unsigned; macro_rules! deserialize_signed { ($self:ident, $visitor:ident, $ixx:ident, $visit_ixx:ident) => {{ @@ -245,6 +246,7 @@ macro_rules! deserialize_signed { } }}; } +pub(crate) use deserialize_signed; impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { type Error = Error; @@ -1094,6 +1096,19 @@ mod tests { ); } + #[test] + fn numbered_key_maps() { + use std::collections::BTreeMap; + let mut ranking: BTreeMap = BTreeMap::new(); + ranking.insert(1, "Elon".to_string()); + ranking.insert(2, "Bazos".to_string()); + + assert_eq!( + from_str::>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(), + ranking + ); + } + #[test] fn deserialize_optional_vector() { #[derive(Debug, Deserialize, PartialEq)] diff --git a/src/lib.rs b/src/lib.rs index f2f5c244e..4ce4e51e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -214,16 +214,4 @@ mod test { item ); } - - // #[test] - // fn numbered_keys() { - // let mut ranking: BTreeMap = BTreeMap::new(); - // ranking.insert(1, "Elon".to_string()); - // ranking.insert(2, "Bazos".to_string()); - - // assert_eq!( - // from_str::>(&to_string(&ranking).unwrap()).unwrap(), - // ranking - // ); - // } } From 96d4319519e5576cf967657ff7affcaa7496b810 Mon Sep 17 00:00:00 2001 From: CyberHoward Date: Tue, 22 Nov 2022 12:46:46 +0000 Subject: [PATCH 140/178] format --- src/de/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index c17c7c4ba..61e740b78 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -1102,7 +1102,7 @@ mod tests { let mut ranking: BTreeMap = BTreeMap::new(); ranking.insert(1, "Elon".to_string()); ranking.insert(2, "Bazos".to_string()); - + assert_eq!( from_str::>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(), ranking From 008fcfd1ad4cbaa4b38fc04c5885c28ad94ba11e Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 25 Nov 2022 11:01:59 +0100 Subject: [PATCH 141/178] Upgrade Rust to 1.59.0 --- .github/workflows/Basic.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Basic.yml b/.github/workflows/Basic.yml index 9035d61f4..ca38b67d0 100644 --- a/.github/workflows/Basic.yml +++ b/.github/workflows/Basic.yml @@ -17,7 +17,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.40.0 + toolchain: 1.59.0 target: wasm32-unknown-unknown override: true @@ -50,7 +50,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.49.0 + toolchain: 1.59.0 override: true components: rustfmt, clippy From 1248a066e0c1d360ef9c7babac562d054be35ab0 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 25 Nov 2022 11:04:59 +0100 Subject: [PATCH 142/178] Format CHANGELOG with prettier --- .editorconfig | 6 ++++-- CHANGELOG.md | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index c1e2c6435..4b0d3a35a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,8 +5,10 @@ root = true [*] indent_style = space -indent_size = 4 -end_of_line = lf +indent_size = 2 charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true + +[*.rs] +indent_size = 4 diff --git a/CHANGELOG.md b/CHANGELOG.md index fda356773..6efdba9a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,13 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [0.4.1] - 2022-05-05 ### Changed + - Properly serialize `u128`/`i128` types when embedded in structs ## [0.4.0] - 2022-03-29 ### Added + - Add support for `#[serde(untagged)]` enums representation ## [0.3.1] - 2021-01-19 @@ -26,6 +28,7 @@ project adheres to [Semantic Versioning](http://semver.org/). ### Changed Maintenance release: + - Update clippy version in CI to 1.49.0. - Fix `clippy::manual-non-exhaustive` warnings. From 908b1d032ed4288a2b0a53d1cf49ca785d138961 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 25 Nov 2022 11:05:23 +0100 Subject: [PATCH 143/178] Bump edition to 2021 --- CHANGELOG.md | 7 +++++++ Cargo.toml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6efdba9a0..85001393d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## Unreleased + +### Changed + +- Bump min supported Rust version to 1.59.0 (same as cosmwasm-std) +- Upgrade codebase to Rust edition 2021 + ## [0.4.1] - 2022-05-05 ### Changed diff --git a/Cargo.toml b/Cargo.toml index 5ffafaf8e..48b66ec72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ authors = [ categories = ["wasm"] description = "serde_json for Wasm programs (small, deterministic, no floats)" documentation = "https://docs.rs/serde-json-wasm" -edition = "2018" +edition = "2021" keywords = ["serde", "json", "wasm"] license = "MIT OR Apache-2.0" name = "serde-json-wasm" From ff801647f46ae7d4feba73024302c318626bd92f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 28 Nov 2022 11:29:42 +0100 Subject: [PATCH 144/178] Test deserializing all integer types --- src/de/mod.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 61e740b78..78bf4039e 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -1099,14 +1099,96 @@ mod tests { #[test] fn numbered_key_maps() { use std::collections::BTreeMap; - let mut ranking: BTreeMap = BTreeMap::new(); + + // u8 + let mut ranking: BTreeMap = BTreeMap::new(); + ranking.insert(1, "Elon".to_string()); + ranking.insert(2, "Bazos".to_string()); + assert_eq!( + from_str::>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(), + ranking + ); + + // u16 + let mut ranking: BTreeMap = BTreeMap::new(); + ranking.insert(1, "Elon".to_string()); + ranking.insert(2, "Bazos".to_string()); + assert_eq!( + from_str::>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(), + ranking + ); + + // u32 + let mut ranking: BTreeMap = BTreeMap::new(); ranking.insert(1, "Elon".to_string()); ranking.insert(2, "Bazos".to_string()); + assert_eq!( + from_str::>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(), + ranking + ); + // u64 + let mut ranking: BTreeMap = BTreeMap::new(); + ranking.insert(1, "Elon".to_string()); + ranking.insert(2, "Bazos".to_string()); assert_eq!( from_str::>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(), ranking ); + + // u128 + let mut ranking: BTreeMap = BTreeMap::new(); + ranking.insert(1, "Elon".to_string()); + ranking.insert(2, "Bazos".to_string()); + assert_eq!( + from_str::>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(), + ranking + ); + + // i8 + let mut ranking: BTreeMap = BTreeMap::new(); + ranking.insert(1, "Elon".to_string()); + ranking.insert(2, "Bazos".to_string()); + assert_eq!( + from_str::>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(), + ranking + ); + + // i16 + let mut ranking: BTreeMap = BTreeMap::new(); + ranking.insert(1, "Elon".to_string()); + ranking.insert(2, "Bazos".to_string()); + assert_eq!( + from_str::>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(), + ranking + ); + + // i32 + let mut ranking: BTreeMap = BTreeMap::new(); + ranking.insert(1, "Elon".to_string()); + ranking.insert(2, "Bazos".to_string()); + assert_eq!( + from_str::>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(), + ranking + ); + + // i64 + let mut ranking: BTreeMap = BTreeMap::new(); + ranking.insert(1, "Elon".to_string()); + ranking.insert(2, "Bazos".to_string()); + assert_eq!( + from_str::>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(), + ranking + ); + + // i128 + let mut ranking: BTreeMap = BTreeMap::new(); + ranking.insert(1, "Elon".to_string()); + ranking.insert(2, "Bazos".to_string()); + assert_eq!( + from_str::>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(), + ranking + ); } #[test] From 761cf3d46e66cd4f0d96108cb9082af1f40a0fda Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 28 Nov 2022 11:41:12 +0100 Subject: [PATCH 145/178] Test unit element type --- src/ser/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 8490157b8..cce6a28b6 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -990,9 +990,15 @@ mod tests { #[test] fn btree_map() { use std::collections::BTreeMap; + // empty map assert_eq!(to_string(&BTreeMap::<(), ()>::new()).unwrap(), r#"{}"#); + // One element with unit type + let mut map = BTreeMap::<&str, ()>::new(); + map.insert("set_element", ()); + assert_eq!(to_string(&map).unwrap(), r#"{"set_element":null}"#); + let mut two_values = BTreeMap::new(); two_values.insert("my_name", "joseph"); two_values.insert("her_name", "aline"); From 19ea90ceea697e5b4a2dcfc1b739d65b8f85cb19 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 28 Nov 2022 11:41:21 +0100 Subject: [PATCH 146/178] Test map_serialization_matches_json_serde --- src/ser/mod.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index cce6a28b6..98a280b51 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -1061,6 +1061,48 @@ mod tests { ); } + #[test] + fn map_serialization_matches_json_serde() { + use std::collections::BTreeMap; + + fn ser_actual(value: &T) -> String { + to_string(value).unwrap() + } + + fn ser_expected(value: &T) -> String { + serde_json::to_string(value).unwrap() + } + + let map = BTreeMap::<(), ()>::new(); + assert_eq!(ser_actual(&map), ser_expected(&map)); + + let mut two_values = BTreeMap::new(); + two_values.insert("my_name", "joseph"); + two_values.insert("her_name", "aline"); + assert_eq!(ser_actual(&two_values), ser_expected(&two_values)); + + let mut nested_map = BTreeMap::new(); + nested_map.insert("two_entries", two_values.clone()); + two_values.remove("my_name"); + nested_map.insert("one_entry", two_values); + assert_eq!(ser_actual(&nested_map), ser_expected(&nested_map)); + + // One element with unit type + let mut map = BTreeMap::<&str, ()>::new(); + map.insert("set_element", ()); + assert_eq!(ser_actual(&map), ser_expected(&map)); + + // numeric keys + let mut map = BTreeMap::new(); + map.insert(10i8, "my_age"); + assert_eq!(ser_actual(&map), ser_expected(&map)); + + // numeric values + let mut scores = BTreeMap::new(); + scores.insert("player A", 1234212); + assert_eq!(ser_actual(&scores), ser_expected(&scores)); + } + #[test] fn number_key() { use std::collections::HashMap; From 27351c0fe3ca19290181887c426b4460fd9eccf0 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 28 Nov 2022 11:58:50 +0100 Subject: [PATCH 147/178] Add --all-targets to clippy args --- .github/workflows/Basic.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Basic.yml b/.github/workflows/Basic.yml index ca38b67d0..33907c160 100644 --- a/.github/workflows/Basic.yml +++ b/.github/workflows/Basic.yml @@ -64,4 +64,4 @@ jobs: uses: actions-rs/cargo@v1 with: command: clippy - args: -- -D warnings + args: --all-targets -- -D warnings From 5b86270908162ec670506dd96ebeeb65840cb1b1 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 28 Nov 2022 12:08:12 +0100 Subject: [PATCH 148/178] Make clippy happy --- src/de/errors.rs | 2 +- src/de/mod.rs | 14 ++++++++------ src/ser/mod.rs | 5 +++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/de/errors.rs b/src/de/errors.rs index fd3e6e511..47876acf2 100644 --- a/src/de/errors.rs +++ b/src/de/errors.rs @@ -5,7 +5,7 @@ use std::{error, fmt}; pub type Result = core::result::Result; /// This type represents all possible errors that can occur when deserializing JSON data -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] #[non_exhaustive] pub enum Error { /// Control character (U+0000 to U+001F) found in string. Those must always be escaped. diff --git a/src/de/mod.rs b/src/de/mod.rs index 78bf4039e..edb561313 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -805,6 +805,8 @@ mod tests { assert!(from_str::<[i32; 2]>("[0, 1,]").is_err()); } + #[allow(clippy::let_unit_value)] + #[allow(clippy::unit_cmp)] #[test] fn tuple() { type Pair = (i64, i64); @@ -1193,18 +1195,18 @@ mod tests { #[test] fn deserialize_optional_vector() { - #[derive(Debug, Deserialize, PartialEq)] + #[derive(Debug, Deserialize, PartialEq, Eq)] pub struct Response { pub log: Option, pub messages: Vec, } - #[derive(Debug, Deserialize, PartialEq, serde_derive::Serialize)] + #[derive(Debug, Deserialize, PartialEq, Eq, serde_derive::Serialize)] pub struct Msg { pub name: String, } - #[derive(Debug, Deserialize, PartialEq)] + #[derive(Debug, Deserialize, PartialEq, Eq)] pub struct OptIn { pub name: Option, } @@ -1264,20 +1266,20 @@ mod tests { #[test] fn deserialize_embedded_enum() { - #[derive(Debug, Deserialize, PartialEq)] + #[derive(Debug, Deserialize, PartialEq, Eq)] #[serde(rename_all = "lowercase")] pub enum MyResult { Ok(Response), Err(String), } - #[derive(Debug, Deserialize, PartialEq)] + #[derive(Debug, Deserialize, PartialEq, Eq)] pub struct Response { pub log: Option, pub messages: Vec, } - #[derive(Debug, Deserialize, PartialEq)] + #[derive(Debug, Deserialize, PartialEq, Eq)] pub struct Msg { pub name: String, pub amount: Option, diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 98a280b51..db0099145 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -708,6 +708,7 @@ mod tests { serde_json::to_string(&wrapped).unwrap() ); + #[allow(clippy::let_unit_value)] let unit: Unit = (); assert_eq!(to_string(&unit).unwrap(), "null"); assert_eq!( @@ -1219,7 +1220,7 @@ mod tests { fn serialize_embedded_enum() { use serde_derive::Deserialize; - #[derive(Debug, Deserialize, Serialize, PartialEq)] + #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "lowercase")] pub enum MyResult { Unit(()), @@ -1227,7 +1228,7 @@ mod tests { Err(String), } - #[derive(Debug, Deserialize, Serialize, PartialEq)] + #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] pub struct Response { pub log: Option, pub count: i64, From 3a53f5165b8121f193b9fdbe67a8f9db7daba1f3 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 29 Nov 2022 11:48:10 +0100 Subject: [PATCH 149/178] Add #[serde(flatten)] tests for serialization and deserialization --- src/de/mod.rs | 35 ++++++++++++++++++++++++++++++++++- src/ser/mod.rs | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index edb561313..1c6fd50db 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -687,7 +687,7 @@ where #[cfg(test)] mod tests { use super::from_str; - use serde_derive::Deserialize; + use serde_derive::{Deserialize, Serialize}; #[derive(Debug, Deserialize, PartialEq)] enum Type { @@ -1018,6 +1018,39 @@ mod tests { assert_eq!(serde_json::from_str::(r#"null"#).unwrap(), Nothing); } + #[test] + fn struct_with_flatten() { + #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] + struct Pagination { + limit: u64, + offset: u64, + total: u64, + } + + #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] + struct Users { + users: Vec, + + #[serde(flatten)] + pagination: Pagination, + } + + let expected = Users { + users: vec!["joe".to_string(), "alice".to_string()], + pagination: Pagination { + offset: 100, + limit: 20, + total: 102, + }, + }; + + assert_eq!( + from_str::(r#"{"users":["joe","alice"],"limit":20,"offset":100,"total":102}"#) + .unwrap(), + expected, + ); + } + #[test] fn ignoring_extra_fields() { #[derive(Debug, Deserialize, PartialEq)] diff --git a/src/ser/mod.rs b/src/ser/mod.rs index db0099145..fbe1d746f 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -539,7 +539,7 @@ impl ser::SerializeStructVariant for Unreachable { mod tests { use super::to_string; - use serde_derive::Serialize; + use serde_derive::{Deserialize, Serialize}; #[test] fn bool() { @@ -988,6 +988,38 @@ mod tests { ); } + #[test] + fn struct_with_flatten() { + #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] + struct Pagination { + limit: u64, + offset: u64, + total: u64, + } + + #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] + struct Users { + users: Vec, + + #[serde(flatten)] + pagination: Pagination, + } + + let users = Users { + users: vec!["joe".to_string(), "alice".to_string()], + pagination: Pagination { + offset: 100, + limit: 20, + total: 102, + }, + }; + + assert_eq!( + to_string(&users).unwrap(), + r#"{"users":["joe","alice"],"limit":20,"offset":100,"total":102}"# + ); + } + #[test] fn btree_map() { use std::collections::BTreeMap; From 5ac78a2b539fb117cbb768a8512e57e606d35776 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 29 Nov 2022 11:49:32 +0100 Subject: [PATCH 150/178] Test serde_json compliance --- src/ser/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index fbe1d746f..f61606389 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -1018,6 +1018,11 @@ mod tests { to_string(&users).unwrap(), r#"{"users":["joe","alice"],"limit":20,"offset":100,"total":102}"# ); + assert_eq!( + to_string(&users).unwrap(), + serde_json::to_string(&users).unwrap(), + "serialization must match serde_json implementation" + ); } #[test] From 56c81ec1aa0b7e18a2046147896c4dfb1044926c Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 29 Nov 2022 11:54:47 +0100 Subject: [PATCH 151/178] Add CHANGELOG --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index da435c861..3c54589d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ project adheres to [Semantic Versioning](http://semver.org/). ### Added - Add support for map (de)serialization. +- Add support for `#[serde(flatten)]` (de)serialization ([#20]). + +[#20]: https://github.com/CosmWasm/serde-json-wasm/issues/20 ### Changed From 631e7fd7e744f9658f1d9ca88d77e52dfd9141b4 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 6 Dec 2022 19:16:40 +0100 Subject: [PATCH 152/178] Add text formatting tools --- .prettierignore | 2 ++ .prettierrc.json | 10 ++++++++++ README.md | 2 +- devtools/format_md.sh | 14 ++++++++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 .prettierignore create mode 100644 .prettierrc.json create mode 100755 devtools/format_md.sh diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..d62e48a9f --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +# Used for *.md formatting in devtools/format_md.sh +target/ diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000..6a82f4e8f --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,10 @@ +{ + "overrides": [ + { + "files": "*.md", + "options": { + "proseWrap": "always" + } + } + ] +} diff --git a/README.md b/README.md index dbcaa3fd1..af5619058 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A [serde-json] alternative for [CosmWasm] smart contracts. [serde-json]: https://crates.io/crates/serde_json -[CosmWasm]: https://cosmwasm.com/ +[cosmwasm]: https://cosmwasm.com/ ## License diff --git a/devtools/format_md.sh b/devtools/format_md.sh new file mode 100755 index 000000000..f45b8c690 --- /dev/null +++ b/devtools/format_md.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -o errexit -o nounset -o pipefail +command -v shellcheck >/dev/null && shellcheck "$0" + +# Running with -c makes the script only validate instead of editing in place. +op="write" +while getopts c option; do + case "${option}" in + c) op="check" ;; + *) ;; + esac +done + +npx prettier@2.7.1 --$op "./**/*.md" From 7284f73e2f91ec53126409baf2d3391539d7af23 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 6 Dec 2022 19:20:59 +0100 Subject: [PATCH 153/178] Add missing version links in CHANGELOG --- CHANGELOG.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c54589d2..1988e638c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## Unreleased +## [Unreleased] ### Added @@ -31,6 +31,10 @@ project adheres to [Semantic Versioning](http://semver.org/). - Add support for `#[serde(untagged)]` enums representation +## [0.3.2] + +Changelog missing, see diff link. + ## [0.3.1] - 2021-01-19 ### Added @@ -103,7 +107,10 @@ Initial release after forking from [serde-json-core](https://github.com/japaric/serde-json-core) at [bf5533a0](https://github.com/japaric/serde-json-core/commit/bf5533a042a0). -[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.3.1...HEAD +[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.4.1...HEAD +[0.4.1]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.4.0...v0.4.1 +[0.4.0]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.3.2...v0.4.0 +[0.3.2]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.3.1...v0.3.2 [0.3.1]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.3.0...v0.3.1 [0.3.0]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.3...v0.3.0 [0.2.3]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.2.2...v0.2.3 From 7da930834885eccfd4e16e2da8b0ca99d0d4661b Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 6 Dec 2022 19:23:56 +0100 Subject: [PATCH 154/178] Set version: 0.5.0 --- CHANGELOG.md | 5 ++++- Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1988e638c..2cb30a4be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [0.5.0] - 2022-12-06 + ### Added - Add support for map (de)serialization. @@ -107,7 +109,8 @@ Initial release after forking from [serde-json-core](https://github.com/japaric/serde-json-core) at [bf5533a0](https://github.com/japaric/serde-json-core/commit/bf5533a042a0). -[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.4.1...HEAD +[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.5.0...HEAD +[0.5.0]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.4.1...v0.5.0 [0.4.1]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.4.0...v0.4.1 [0.4.0]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.3.2...v0.4.0 [0.3.2]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.3.1...v0.3.2 diff --git a/Cargo.lock b/Cargo.lock index ec430f855..a1e92a578 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,7 +40,7 @@ checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" [[package]] name = "serde-json-wasm" -version = "0.4.1" +version = "0.5.0" dependencies = [ "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index 48b66ec72..328ea217f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "MIT OR Apache-2.0" name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/CosmWasm/serde-json-wasm" -version = "0.4.1" +version = "0.5.0" exclude = [ ".cargo/", ".github/", From 70a550ebaa68e007078039ee6062976e617ce7d2 Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Sun, 11 Dec 2022 16:09:08 +0100 Subject: [PATCH 155/178] add support for ser.collect_str --- src/ser/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index f61606389..8278b3111 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -429,11 +429,11 @@ impl<'a> ser::Serializer for &'a mut Serializer { self.serialize_struct(name, len) } - fn collect_str(self, _value: &T) -> Result + fn collect_str(self, value: &T) -> Result where T: fmt::Display, { - unreachable!() + self.serialize_str(&value.to_string()) } } From 9082d9585bee1cb49628aca41f0e3ac8a15c631f Mon Sep 17 00:00:00 2001 From: cyberhoward Date: Sun, 11 Dec 2022 16:10:10 +0100 Subject: [PATCH 156/178] update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cb30a4be..4d24cd9db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added + +- Add support for `str_collect` serialization. + + ## [0.5.0] - 2022-12-06 ### Added From 3b4963766e328f7b00eb7d9796e1755f8e83355b Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 27 Feb 2023 13:43:39 +0100 Subject: [PATCH 157/178] Add missing CHANGELOG section for version 0.3.2 --- CHANGELOG.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cb30a4be..ac41310f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,9 +33,17 @@ project adheres to [Semantic Versioning](http://semver.org/). - Add support for `#[serde(untagged)]` enums representation -## [0.3.2] +## [0.3.2] - 2021-12-14 -Changelog missing, see diff link. +### Added + +- Add support for u128/i128 serialization and deserialization ([#32], + [#33]).
**Please note:** this is + [incompatible with serde-json and schemars](https://github.com/CosmWasm/cosmwasm/issues/1605) + and for this reason discouraged to use. + +[#32]: https://github.com/CosmWasm/serde-json-wasm/issues/32 +[#33]: https://github.com/CosmWasm/serde-json-wasm/pull/33 ## [0.3.1] - 2021-01-19 From 13f70b82a8b710d2415690cf2a1d4188fcb6ebda Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 11 Apr 2023 14:18:55 +0200 Subject: [PATCH 158/178] Test collect_str and let it use the default implementation --- src/ser/mod.rs | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 8278b3111..d50b2fe2a 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -428,13 +428,6 @@ impl<'a> ser::Serializer for &'a mut Serializer { self.buf.push(b':'); self.serialize_struct(name, len) } - - fn collect_str(self, value: &T) -> Result - where - T: fmt::Display, - { - self.serialize_str(&value.to_string()) - } } /// Serializes the given data structure as a string of JSON text @@ -539,6 +532,7 @@ impl ser::SerializeStructVariant for Unreachable { mod tests { use super::to_string; + use serde::{Serialize, Serializer}; use serde_derive::{Deserialize, Serialize}; #[test] @@ -869,6 +863,32 @@ mod tests { assert_eq!(to_string(" \u{001f} ").unwrap(), r#"" \u001F ""#); } + #[test] + fn collect_str_can_be_used_in_custom_seralize_impl() { + struct SpecialType { + count: u32, + on: bool, + } + + impl Serialize for SpecialType { + // A custom Serialize implementation for SpecialType. SpecialType is giving us the chance to use + // an efficient collect_str implementation that is better than allocating the String and running + // serialize_str on it. + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_str(&format_args!("{}-{}", self.count, self.on)) + } + } + + let value = SpecialType { + count: 123, + on: false, + }; + assert_eq!(to_string(&value).unwrap(), r#""123-false""#); + } + #[test] fn newtype() { #[derive(Serialize)] From d92b365dbb2177f43eb0820007e9c301b2f3e7fd Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 11 Apr 2023 14:21:34 +0200 Subject: [PATCH 159/178] Fixup CHANGELOG --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 668e966ae..44281a805 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,10 @@ project adheres to [Semantic Versioning](http://semver.org/). ### Added -- Add support for `str_collect` serialization. +- Add support for `collect_str` serialization ([#51], [#55]). +[#51]: https://github.com/CosmWasm/serde-json-wasm/pull/51 +[#55]: https://github.com/CosmWasm/serde-json-wasm/pull/55 ## [0.5.0] - 2022-12-06 From 3a0b4a7bb93e051011f35aed971b914a998a2ac8 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 11 Apr 2023 18:11:02 +0200 Subject: [PATCH 160/178] Set version: 0.5.1 --- CHANGELOG.md | 5 ++++- Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44281a805..f0ea03ff3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [0.5.1] - 2023-04-11 + ### Added - Add support for `collect_str` serialization ([#51], [#55]). @@ -124,7 +126,8 @@ Initial release after forking from [serde-json-core](https://github.com/japaric/serde-json-core) at [bf5533a0](https://github.com/japaric/serde-json-core/commit/bf5533a042a0). -[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.5.0...HEAD +[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.5.1...HEAD +[0.5.1]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.5.0...v0.5.1 [0.5.0]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.4.1...v0.5.0 [0.4.1]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.4.0...v0.4.1 [0.4.0]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.3.2...v0.4.0 diff --git a/Cargo.lock b/Cargo.lock index a1e92a578..d065619c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,7 +40,7 @@ checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" [[package]] name = "serde-json-wasm" -version = "0.5.0" +version = "0.5.1" dependencies = [ "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index 328ea217f..dbec3b5d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "MIT OR Apache-2.0" name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/CosmWasm/serde-json-wasm" -version = "0.5.0" +version = "0.5.1" exclude = [ ".cargo/", ".github/", From 6e114183349d443e27784044a981f40642921c6d Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 27 Jun 2023 12:38:01 +0200 Subject: [PATCH 161/178] Improve serde_json compatibility testing --- src/ser/mod.rs | 192 +++++++++++++++++++++---------------------------- 1 file changed, 81 insertions(+), 111 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index d50b2fe2a..2491a876c 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -535,6 +535,20 @@ mod tests { use serde::{Serialize, Serializer}; use serde_derive::{Deserialize, Serialize}; + #[macro_export] + macro_rules! assert_serde_json_serialize_eq { + ($target:expr,) => { + assert_serde_json_serialize_eq!($target); + }; + ($target:expr) => { + assert_eq!( + crate::to_string($target).unwrap(), + ::serde_json::to_string($target).unwrap(), + "Serialization does not match serde_json" + ); + }; + } + #[test] fn bool() { assert_eq!(to_string(&true).unwrap(), "true"); @@ -640,9 +654,11 @@ mod tests { r#""9223372036854775808""# ); assert_eq!( - to_string::(&std::u128::MAX).unwrap(), + to_string::(&u128::MAX).unwrap(), r#""340282366920938463463374607431768211455""# ); + // Currently failing, see https://github.com/CosmWasm/serde-json-wasm/issues/54 + // assert_serde_json_serialize_eq!(&u128::MAX); assert_eq!(to_string::(&0).unwrap(), r#""0""#); assert_eq!(to_string::(&1).unwrap(), r#""1""#); @@ -666,14 +682,16 @@ mod tests { r#""9223372036854775808""# ); assert_eq!( - to_string::(&std::i128::MAX).unwrap(), + to_string::(&i128::MAX).unwrap(), r#""170141183460469231731687303715884105727""# ); assert_eq!(to_string::(&-1).unwrap(), r#""-1""#); assert_eq!( - to_string::(&std::i128::MIN).unwrap(), + to_string::(&i128::MIN).unwrap(), r#""-170141183460469231731687303715884105728""# ); + // Currently failing, see https://github.com/CosmWasm/serde-json-wasm/issues/54 + // assert_serde_json_serialize_eq!(&i128::MIN); } #[test] @@ -690,25 +708,16 @@ mod tests { let pair: Pair = (1, 2); assert_eq!(to_string(&pair).unwrap(), "[1,2]"); - assert_eq!( - to_string(&pair).unwrap(), - serde_json::to_string(&pair).unwrap() - ); + assert_serde_json_serialize_eq!(&pair); let wrapped: Wrapped = (5,); assert_eq!(to_string(&wrapped).unwrap(), "[5]"); - assert_eq!( - to_string(&wrapped).unwrap(), - serde_json::to_string(&wrapped).unwrap() - ); + assert_serde_json_serialize_eq!(&wrapped); #[allow(clippy::let_unit_value)] let unit: Unit = (); assert_eq!(to_string(&unit).unwrap(), "null"); - assert_eq!( - to_string(&unit).unwrap(), - serde_json::to_string(&unit).unwrap() - ); + assert_serde_json_serialize_eq!(&unit); type BigPair = (u128, u128); @@ -718,6 +727,8 @@ mod tests { to_string(&pair).unwrap(), r#"["340282366920938463463374607431768211455","340282366920938463463374607431768211455"]"# ); + // Currently failing, see https://github.com/CosmWasm/serde-json-wasm/issues/54 + // assert_serde_json_serialize_eq!(&pair); } #[test] @@ -729,10 +740,7 @@ mod tests { Exit, } assert_eq!(to_string(&Op::Exit).unwrap(), r#""Exit""#); - assert_eq!( - to_string(&Op::Exit).unwrap(), - serde_json::to_string(&Op::Exit).unwrap() - ); + assert_serde_json_serialize_eq!(&Op::Exit); // Numeric values are ignored 🤷 #[derive(Serialize)] @@ -741,15 +749,10 @@ mod tests { Ordered = 42, } assert_eq!(to_string(&Order::Unordered).unwrap(), r#""Unordered""#); - assert_eq!( - to_string(&Order::Unordered).unwrap(), - serde_json::to_string(&Order::Unordered).unwrap() - ); + assert_serde_json_serialize_eq!(&Order::Unordered); + assert_eq!(to_string(&Order::Ordered).unwrap(), r#""Ordered""#); - assert_eq!( - to_string(&Order::Ordered).unwrap(), - serde_json::to_string(&Order::Ordered).unwrap() - ); + assert_serde_json_serialize_eq!(&Order::Ordered); } #[test] @@ -761,20 +764,13 @@ mod tests { Add(i64, i64), } assert_eq!(to_string(&Op::Exit()).unwrap(), r#"{"Exit":[]}"#); - assert_eq!( - to_string(&Op::Exit()).unwrap(), - serde_json::to_string(&Op::Exit()).unwrap() - ); + assert_serde_json_serialize_eq!(&Op::Exit()); + assert_eq!(to_string(&Op::Square(2)).unwrap(), r#"{"Square":2}"#); - assert_eq!( - to_string(&Op::Square(2)).unwrap(), - serde_json::to_string(&Op::Square(2)).unwrap() - ); + assert_serde_json_serialize_eq!(&Op::Square(2)); + assert_eq!(to_string(&Op::Add(3, 4)).unwrap(), r#"{"Add":[3,4]}"#); - assert_eq!( - to_string(&Op::Add(3, 4)).unwrap(), - serde_json::to_string(&Op::Add(3, 4)).unwrap() - ); + assert_serde_json_serialize_eq!(&Op::Add(3, 4)); } #[test] @@ -786,26 +782,19 @@ mod tests { Add { a: i64, b: i64 }, } assert_eq!(to_string(&Op::Exit {}).unwrap(), r#"{"Exit":{}}"#); - assert_eq!( - to_string(&Op::Exit {}).unwrap(), - serde_json::to_string(&Op::Exit {}).unwrap() - ); + assert_serde_json_serialize_eq!(&Op::Exit {}); + assert_eq!( to_string(&Op::Square { input: 2 }).unwrap(), r#"{"Square":{"input":2}}"# ); - assert_eq!( - to_string(&Op::Square { input: 2 }).unwrap(), - serde_json::to_string(&Op::Square { input: 2 }).unwrap() - ); + assert_serde_json_serialize_eq!(&Op::Square { input: 2 }); + assert_eq!( to_string(&Op::Add { a: 3, b: 4 }).unwrap(), r#"{"Add":{"a":3,"b":4}}"# ); - assert_eq!( - to_string(&Op::Add { a: 3, b: 4 }).unwrap(), - serde_json::to_string(&Op::Add { a: 3, b: 4 }).unwrap() - ); + assert_serde_json_serialize_eq!(&Op::Add { a: 3, b: 4 }); } #[test] @@ -896,11 +885,16 @@ mod tests { #[derive(Serialize)] struct CommentId(u32); + // string assert_eq!( to_string(&Address("home".to_string())).unwrap(), r#""home""# ); + assert_serde_json_serialize_eq!(&Address("home".to_string())); + + // number assert_eq!(to_string(&CommentId(42)).unwrap(), r#"42"#); + assert_serde_json_serialize_eq!(&CommentId(42)); } #[test] @@ -911,6 +905,7 @@ mod tests { } assert_eq!(to_string(&Led { led: true }).unwrap(), r#"{"led":true}"#); + assert_serde_json_serialize_eq!(&Led { led: true }); } #[test] @@ -924,21 +919,19 @@ mod tests { to_string(&Temperature { temperature: 127 }).unwrap(), r#"{"temperature":127}"# ); - assert_eq!( to_string(&Temperature { temperature: 20 }).unwrap(), r#"{"temperature":20}"# ); - assert_eq!( to_string(&Temperature { temperature: -17 }).unwrap(), r#"{"temperature":-17}"# ); - assert_eq!( to_string(&Temperature { temperature: -128 }).unwrap(), r#"{"temperature":-128}"# ); + assert_serde_json_serialize_eq!(&Temperature { temperature: -128 }); } #[test] @@ -955,12 +948,16 @@ mod tests { .unwrap(), r#"{"description":"An ambient temperature sensor"}"# ); + assert_serde_json_serialize_eq!(&Property { + description: Some("An ambient temperature sensor"), + }); // XXX Ideally this should produce "{}" assert_eq!( to_string(&Property { description: None }).unwrap(), r#"{"description":null}"# ); + assert_serde_json_serialize_eq!(&Property { description: None }); } #[test] @@ -974,6 +971,7 @@ mod tests { to_string(&Temperature { temperature: 20 }).unwrap(), r#"{"temperature":20}"# ); + assert_serde_json_serialize_eq!(&Temperature { temperature: 20 }); } #[test] @@ -982,19 +980,13 @@ mod tests { struct Nothing; assert_eq!(to_string(&Nothing).unwrap(), r#"null"#); - assert_eq!( - to_string(&Nothing).unwrap(), - serde_json::to_string(&Nothing).unwrap() - ); + assert_serde_json_serialize_eq!(&Nothing); #[derive(Serialize)] struct Empty {} assert_eq!(to_string(&Empty {}).unwrap(), r#"{}"#); - assert_eq!( - to_string(&Empty {}).unwrap(), - serde_json::to_string(&Empty {}).unwrap() - ); + assert_serde_json_serialize_eq!(&Empty {}); #[derive(Serialize)] struct Tuple { @@ -1006,6 +998,7 @@ mod tests { to_string(&Tuple { a: true, b: false }).unwrap(), r#"{"a":true,"b":false}"# ); + assert_serde_json_serialize_eq!(&Tuple { a: true, b: false }); } #[test] @@ -1038,11 +1031,7 @@ mod tests { to_string(&users).unwrap(), r#"{"users":["joe","alice"],"limit":20,"offset":100,"total":102}"# ); - assert_eq!( - to_string(&users).unwrap(), - serde_json::to_string(&users).unwrap(), - "serialization must match serde_json implementation" - ); + assert_serde_json_serialize_eq!(&users); } #[test] @@ -1050,12 +1039,15 @@ mod tests { use std::collections::BTreeMap; // empty map - assert_eq!(to_string(&BTreeMap::<(), ()>::new()).unwrap(), r#"{}"#); + let empty = BTreeMap::<(), ()>::new(); + assert_eq!(to_string(&empty).unwrap(), r#"{}"#); + assert_serde_json_serialize_eq!(&empty); // One element with unit type let mut map = BTreeMap::<&str, ()>::new(); map.insert("set_element", ()); assert_eq!(to_string(&map).unwrap(), r#"{"set_element":null}"#); + assert_serde_json_serialize_eq!(&map); let mut two_values = BTreeMap::new(); two_values.insert("my_name", "joseph"); @@ -1064,6 +1056,7 @@ mod tests { to_string(&two_values).unwrap(), r#"{"her_name":"aline","my_name":"joseph"}"# ); + assert_serde_json_serialize_eq!(&two_values); let mut nested_map = BTreeMap::new(); nested_map.insert("two_entries", two_values.clone()); @@ -1074,6 +1067,7 @@ mod tests { to_string(&nested_map).unwrap(), r#"{"one_entry":{"her_name":"aline"},"two_entries":{"her_name":"aline","my_name":"joseph"}}"# ); + assert_serde_json_serialize_eq!(&nested_map); } #[test] @@ -1081,12 +1075,15 @@ mod tests { use std::collections::HashMap; // empty map - assert_eq!(to_string(&HashMap::<(), ()>::new()).unwrap(), r#"{}"#); + let empty = HashMap::<(), ()>::new(); + assert_eq!(to_string(&empty).unwrap(), r#"{}"#); + assert_serde_json_serialize_eq!(&empty); // One element let mut map = HashMap::new(); map.insert("my_age", 28); assert_eq!(to_string(&map).unwrap(), r#"{"my_age":28}"#); + assert_serde_json_serialize_eq!(&map); #[derive(Debug, Serialize, PartialEq, Eq, Hash)] pub struct NewType(String); @@ -1095,6 +1092,7 @@ mod tests { let mut map = HashMap::new(); map.insert(NewType(String::from("my_age")), 44); assert_eq!(to_string(&map).unwrap(), r#"{"my_age":44}"#); + assert_serde_json_serialize_eq!(&map); #[derive(Debug, Serialize, PartialEq, Eq, Hash)] #[serde(rename_all = "lowercase")] @@ -1106,6 +1104,7 @@ mod tests { let mut map = HashMap::new(); map.insert(MyResult::Err, 404); assert_eq!(to_string(&map).unwrap(), r#"{"err":404}"#); + assert_serde_json_serialize_eq!(&map); // HashMap does not have deterministic iteration order (except in the Wasm target). // So the two element map is serialized as one of two options. @@ -1117,48 +1116,7 @@ mod tests { serialized == r#"{"her_name":"aline","my_name":"joseph"}"# || serialized == r#"{"my_name":"joseph","her_name":"aline"}"# ); - } - - #[test] - fn map_serialization_matches_json_serde() { - use std::collections::BTreeMap; - - fn ser_actual(value: &T) -> String { - to_string(value).unwrap() - } - - fn ser_expected(value: &T) -> String { - serde_json::to_string(value).unwrap() - } - - let map = BTreeMap::<(), ()>::new(); - assert_eq!(ser_actual(&map), ser_expected(&map)); - - let mut two_values = BTreeMap::new(); - two_values.insert("my_name", "joseph"); - two_values.insert("her_name", "aline"); - assert_eq!(ser_actual(&two_values), ser_expected(&two_values)); - - let mut nested_map = BTreeMap::new(); - nested_map.insert("two_entries", two_values.clone()); - two_values.remove("my_name"); - nested_map.insert("one_entry", two_values); - assert_eq!(ser_actual(&nested_map), ser_expected(&nested_map)); - - // One element with unit type - let mut map = BTreeMap::<&str, ()>::new(); - map.insert("set_element", ()); - assert_eq!(ser_actual(&map), ser_expected(&map)); - - // numeric keys - let mut map = BTreeMap::new(); - map.insert(10i8, "my_age"); - assert_eq!(ser_actual(&map), ser_expected(&map)); - - // numeric values - let mut scores = BTreeMap::new(); - scores.insert("player A", 1234212); - assert_eq!(ser_actual(&scores), ser_expected(&scores)); + assert_serde_json_serialize_eq!(&two_values); } #[test] @@ -1169,16 +1127,19 @@ mod tests { let mut map = HashMap::new(); map.insert(10i8, "my_age"); assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); + assert_serde_json_serialize_eq!(&map); // i16 key let mut map = HashMap::new(); map.insert(10i16, "my_age"); assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); + assert_serde_json_serialize_eq!(&map); // i32 key let mut map = HashMap::new(); map.insert(10i32, "my_age"); assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); + assert_serde_json_serialize_eq!(&map); // i64 key let mut map = HashMap::new(); @@ -1189,11 +1150,13 @@ mod tests { let mut map = HashMap::new(); map.insert(10i128, "my_age"); assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); + assert_serde_json_serialize_eq!(&map); // u8 key let mut map = HashMap::new(); map.insert(10u8, "my_age"); assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); + assert_serde_json_serialize_eq!(&map); // u16 key let mut map = HashMap::new(); @@ -1204,16 +1167,19 @@ mod tests { let mut map = HashMap::new(); map.insert(10u32, "my_age"); assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); + assert_serde_json_serialize_eq!(&map); // u64 key let mut map = HashMap::new(); map.insert(10u64, "my_age"); assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); + assert_serde_json_serialize_eq!(&map); // u128 key let mut map = HashMap::new(); map.insert(10u128, "my_age"); assert_eq!(to_string(&map).unwrap(), r#"{"10":"my_age"}"#); + assert_serde_json_serialize_eq!(&map); } #[test] @@ -1297,12 +1263,14 @@ mod tests { assert_eq!(json, r#"{"err":"some error"}"#.to_string()); let loaded = crate::from_str(&json).expect("re-load err enum"); assert_eq!(err_input, loaded); + assert_serde_json_serialize_eq!(&err_input); let unit = MyResult::Unit(()); let json = to_string(&unit).expect("encode unit enum"); assert_eq!(json, r#"{"unit":null}"#.to_string()); let loaded = crate::from_str(&json).expect("re-load unit enum"); assert_eq!(unit, loaded); + assert_serde_json_serialize_eq!(&unit); let empty_list = MyResult::Ok(Response { log: Some("log message".to_string()), @@ -1316,6 +1284,7 @@ mod tests { ); let loaded = crate::from_str(&json).expect("re-load ok enum"); assert_eq!(empty_list, loaded); + assert_serde_json_serialize_eq!(&empty_list); let full_list = MyResult::Ok(Response { log: None, @@ -1329,5 +1298,6 @@ mod tests { ); let loaded = crate::from_str(&json).expect("re-load ok enum"); assert_eq!(full_list, loaded); + assert_serde_json_serialize_eq!(&full_list); } } From b859f543c16857cd273245c9155babddad41e791 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 27 Jun 2023 12:44:21 +0200 Subject: [PATCH 162/178] Remove comments about omitting None values --- src/ser/mod.rs | 1 - src/ser/struct_.rs | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 2491a876c..60d697168 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -952,7 +952,6 @@ mod tests { description: Some("An ambient temperature sensor"), }); - // XXX Ideally this should produce "{}" assert_eq!( to_string(&Property { description: None }).unwrap(), r#"{"description":null}"# diff --git a/src/ser/struct_.rs b/src/ser/struct_.rs index 502d03460..5a15036ab 100644 --- a/src/ser/struct_.rs +++ b/src/ser/struct_.rs @@ -21,7 +21,6 @@ impl<'a> ser::SerializeStruct for SerializeStruct<'a> { where T: ser::Serialize, { - // XXX if `value` is `None` we not produce any output for this field if !self.first { self.ser.buf.push(b','); } @@ -50,7 +49,6 @@ impl<'a> ser::SerializeStructVariant for SerializeStruct<'a> { where T: ser::Serialize, { - // XXX if `value` is `None` we not produce any output for this field if !self.first { self.ser.buf.push(b','); } From d98dab60b9245acee3e38ddf924ee24426fd6565 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 27 Jun 2023 12:45:18 +0200 Subject: [PATCH 163/178] Update serde_json --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d065619c9..a3386ade4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,9 +60,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.79" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" dependencies = [ "itoa", "ryu", diff --git a/Cargo.toml b/Cargo.toml index dbec3b5d7..a56dbfa5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,4 +27,4 @@ serde = { version = "^1.0.80", default-features = false, features = ["alloc"] } [dev-dependencies] serde_derive = "^1.0.80" -serde_json = "^1.0.59" +serde_json = "^1.0.99" From 765f7ea488518eabd3e5796ca394c8f1b4761595 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 28 Jul 2023 11:33:04 +0200 Subject: [PATCH 164/178] Serialize u128 and i128 as numbers --- src/ser/mod.rs | 69 +++++++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 40 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 60d697168..3a2e2d68b 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -194,11 +194,7 @@ impl<'a> ser::Serializer for &'a mut Serializer { fn serialize_i128(self, v: i128) -> Result { // -170141183460469231731687303715884105728 - self.buf.push(b'"'); - let res: Result = serialize_signed!(self, 40, v, i128, u128); - res?; - self.buf.push(b'"'); - Ok(()) + serialize_signed!(self, 40, v, i128, u128) } fn serialize_u8(self, v: u8) -> Result { @@ -223,11 +219,7 @@ impl<'a> ser::Serializer for &'a mut Serializer { fn serialize_u128(self, v: u128) -> Result { // 340282366920938463463374607431768211455 - self.buf.push(b'"'); - let res: Result = serialize_unsigned!(self, 39, v); - res?; - self.buf.push(b'"'); - Ok(()) + serialize_unsigned!(self, 39, v) } fn serialize_f32(self, _v: f32) -> Result { @@ -632,66 +624,64 @@ mod tests { "-9223372036854775808" ); - assert_eq!(to_string::(&0).unwrap(), r#""0""#); - assert_eq!(to_string::(&1).unwrap(), r#""1""#); - assert_eq!(to_string::(&456789).unwrap(), r#""456789""#); - assert_eq!(to_string::(&4294967295).unwrap(), r#""4294967295""#); - assert_eq!(to_string::(&4294967296).unwrap(), r#""4294967296""#); + assert_eq!(to_string::(&0).unwrap(), r#"0"#); + assert_eq!(to_string::(&1).unwrap(), r#"1"#); + assert_eq!(to_string::(&456789).unwrap(), r#"456789"#); + assert_eq!(to_string::(&4294967295).unwrap(), r#"4294967295"#); + assert_eq!(to_string::(&4294967296).unwrap(), r#"4294967296"#); assert_eq!( to_string::(&9007199254740991).unwrap(), - r#""9007199254740991""# + r#"9007199254740991"# ); // Number.MAX_SAFE_INTEGER assert_eq!( to_string::(&9007199254740992).unwrap(), - r#""9007199254740992""# + r#"9007199254740992"# ); // Number.MAX_SAFE_INTEGER+1 assert_eq!( to_string::(&9223372036854775807).unwrap(), - r#""9223372036854775807""# + r#"9223372036854775807"# ); assert_eq!( to_string::(&9223372036854775808).unwrap(), - r#""9223372036854775808""# + r#"9223372036854775808"# ); assert_eq!( to_string::(&u128::MAX).unwrap(), - r#""340282366920938463463374607431768211455""# + r#"340282366920938463463374607431768211455"# ); - // Currently failing, see https://github.com/CosmWasm/serde-json-wasm/issues/54 - // assert_serde_json_serialize_eq!(&u128::MAX); - - assert_eq!(to_string::(&0).unwrap(), r#""0""#); - assert_eq!(to_string::(&1).unwrap(), r#""1""#); - assert_eq!(to_string::(&456789).unwrap(), r#""456789""#); - assert_eq!(to_string::(&4294967295).unwrap(), r#""4294967295""#); - assert_eq!(to_string::(&4294967296).unwrap(), r#""4294967296""#); + assert_serde_json_serialize_eq!(&u128::MAX); + + assert_eq!(to_string::(&0).unwrap(), r#"0"#); + assert_eq!(to_string::(&1).unwrap(), r#"1"#); + assert_eq!(to_string::(&456789).unwrap(), r#"456789"#); + assert_eq!(to_string::(&4294967295).unwrap(), r#"4294967295"#); + assert_eq!(to_string::(&4294967296).unwrap(), r#"4294967296"#); assert_eq!( to_string::(&9007199254740991).unwrap(), - r#""9007199254740991""# + r#"9007199254740991"# ); // Number.MAX_SAFE_INTEGER assert_eq!( to_string::(&9007199254740992).unwrap(), - r#""9007199254740992""# + r#"9007199254740992"# ); // Number.MAX_SAFE_INTEGER+1 assert_eq!( to_string::(&9223372036854775807).unwrap(), - r#""9223372036854775807""# + r#"9223372036854775807"# ); assert_eq!( to_string::(&9223372036854775808).unwrap(), - r#""9223372036854775808""# + r#"9223372036854775808"# ); assert_eq!( to_string::(&i128::MAX).unwrap(), - r#""170141183460469231731687303715884105727""# + r#"170141183460469231731687303715884105727"# ); - assert_eq!(to_string::(&-1).unwrap(), r#""-1""#); + assert_eq!(to_string::(&-1).unwrap(), r#"-1"#); assert_eq!( to_string::(&i128::MIN).unwrap(), - r#""-170141183460469231731687303715884105728""# + r#"-170141183460469231731687303715884105728"# ); - // Currently failing, see https://github.com/CosmWasm/serde-json-wasm/issues/54 - // assert_serde_json_serialize_eq!(&i128::MIN); + assert_serde_json_serialize_eq!(&i128::MIN); } #[test] @@ -725,10 +715,9 @@ mod tests { assert_eq!( to_string(&pair).unwrap(), - r#"["340282366920938463463374607431768211455","340282366920938463463374607431768211455"]"# + r#"[340282366920938463463374607431768211455,340282366920938463463374607431768211455]"# ); - // Currently failing, see https://github.com/CosmWasm/serde-json-wasm/issues/54 - // assert_serde_json_serialize_eq!(&pair); + assert_serde_json_serialize_eq!(&pair); } #[test] From 4b7d2c097f939d0e1055f74bbb0b9685631b5609 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 28 Jul 2023 13:15:13 +0200 Subject: [PATCH 165/178] Deserialize u128 and i128 as numbers --- src/de/map.rs | 5 ++-- src/de/mod.rs | 74 +++++++++++++++------------------------------------ 2 files changed, 24 insertions(+), 55 deletions(-) diff --git a/src/de/map.rs b/src/de/map.rs index 17b88800f..8d2d0beb3 100644 --- a/src/de/map.rs +++ b/src/de/map.rs @@ -160,8 +160,7 @@ impl<'de, 'a> de::Deserializer<'de> for MapKey<'a, 'de> { where V: Visitor<'de>, { - // default implementation includes string unparsing - self.de.deserialize_i128(visitor) + deserialize_signed_key!(self, visitor, i128, visit_i128) } fn deserialize_u8(self, visitor: V) -> Result @@ -196,7 +195,7 @@ impl<'de, 'a> de::Deserializer<'de> for MapKey<'a, 'de> { where V: Visitor<'de>, { - self.de.deserialize_u128(visitor) + deserialize_unsigned_key!(self, visitor, u128, visit_u128) } fn deserialize_f32(self, _visitor: V) -> Result diff --git a/src/de/mod.rs b/src/de/mod.rs index 1c6fd50db..755b4ccf8 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -358,25 +358,7 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? { - b'"' => self.eat_char(), - _ => return Err(Error::InvalidType), - }; - - let result = match self.peek() { - // after rust merged or-patterns feature, these two clause can be merged. - // error[E0658]: or-patterns syntax is experimental - Some(b'0'..=b'9') => deserialize_signed!(self, visitor, i128, visit_i128), - Some(b'-') => deserialize_signed!(self, visitor, i128, visit_i128), - _ => return Err(Error::InvalidType), - }; - match self.peek() { - Some(b'"') => { - self.eat_char(); - result - } - _ => Err(Error::InvalidType), - } + deserialize_signed!(self, visitor, i128, visit_i128) } fn deserialize_u8(self, visitor: V) -> Result @@ -411,25 +393,7 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? { - b'"' => { - self.eat_char(); - } - _ => return Err(Error::InvalidType), - }; - - let result = match self.peek() { - Some(b'-') => return Err(Error::InvalidNumber), - Some(b'0'..=b'9') => deserialize_unsigned!(self, visitor, u128, visit_u128), - _ => return Err(Error::InvalidType), - }; - match self.peek() { - Some(b'"') => { - self.eat_char(); - result - } - _ => Err(Error::InvalidType), - } + deserialize_unsigned!(self, visitor, u128, visit_u128) } fn deserialize_f32(self, _visitor: V) -> Result @@ -755,43 +719,49 @@ mod tests { #[test] fn integer128() { - assert_eq!(from_str::(r#"0"#), Err(crate::de::Error::InvalidType)); - assert_eq!(from_str::(r#""0""#), Ok(0)); - assert_eq!(from_str::(r#""1""#), Ok(1)); - assert_eq!(from_str::(r#""-1""#), Ok(-1)); + assert_eq!( + from_str::(r#""0""#), + Err(crate::de::Error::InvalidType) + ); + assert_eq!(from_str::(r#"0"#), Ok(0)); + assert_eq!(from_str::(r#"1"#), Ok(1)); + assert_eq!(from_str::(r#"-1"#), Ok(-1)); // max i128 assert_eq!( - from_str::(r#""170141183460469231731687303715884105727""#), + from_str::(r#"170141183460469231731687303715884105727"#), Ok(170141183460469231731687303715884105727) ); assert_eq!( - from_str::(r#""170141183460469231731687303715884105728""#), + from_str::(r#"170141183460469231731687303715884105728"#), Err(crate::de::Error::InvalidNumber) ); // min i128 assert_eq!( - from_str::(r#""-170141183460469231731687303715884105728""#), + from_str::(r#"-170141183460469231731687303715884105728"#), Ok(-170141183460469231731687303715884105728) ); assert_eq!( - from_str::(r#""-170141183460469231731687303715884105729""#), + from_str::(r#"-170141183460469231731687303715884105729"#), Err(crate::de::Error::InvalidNumber) ); - assert_eq!(from_str::(r#"0"#), Err(crate::de::Error::InvalidType)); - assert_eq!(from_str::(r#""0""#), Ok(0)); - assert_eq!(from_str::(r#""1""#), Ok(1)); assert_eq!( - from_str::(r#""-1""#), + from_str::(r#""0""#), + Err(crate::de::Error::InvalidType) + ); + assert_eq!(from_str::(r#"0"#), Ok(0)); + assert_eq!(from_str::(r#"1"#), Ok(1)); + assert_eq!( + from_str::(r#"-1"#), Err(crate::de::Error::InvalidNumber) ); // max u128 assert_eq!( - from_str::(r#""340282366920938463463374607431768211455""#), + from_str::(r#"340282366920938463463374607431768211455"#), Ok(340282366920938463463374607431768211455) ); assert_eq!( - from_str::(r#""340282366920938463463374607431768211456""#), + from_str::(r#"340282366920938463463374607431768211456"#), Err(crate::de::Error::InvalidNumber) ) } From 73bd5eccaf4c1330e8267688a2e222b83036bdc1 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 28 Jul 2023 13:34:33 +0200 Subject: [PATCH 166/178] Add changelog entry for u128/i128 serialization --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0ea03ff3..e7cbd6e36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Changed + +- Serialize / deserialize `u128`/`i128` types as numbers instead of strings + ([#59]).
**Please note:** this breaks deserialization of `u128`/`i128` + serialized with older versions of `serde-json-wasm`. + +[#59]: https://github.com/CosmWasm/serde-json-wasm/pull/59 + ## [0.5.1] - 2023-04-11 ### Added From bb5c7b24c25afedaf08f2a1b3064fda7c59c800f Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 24 Jul 2023 04:13:56 +0200 Subject: [PATCH 167/178] =?UTF-8?q?Don=E2=80=99t=20convert=20literals=20to?= =?UTF-8?q?=20String=20in=20test=20assertions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit assert_eq and assert_ne works perfectly well when one argument is a String and another is &str. That is, it’s not necessary to convert the literal argument to String for the test to compile and work correctly. With that in mind, remove unnecessary `.to_string()` calls. This makes code easier to read (as it’s less ‘busy’). --- src/de/unescape.rs | 122 ++++++++++++++++++++++----------------------- src/ser/mod.rs | 11 ++-- 2 files changed, 65 insertions(+), 68 deletions(-) diff --git a/src/de/unescape.rs b/src/de/unescape.rs index 079d438ba..7f5b5b5e2 100644 --- a/src/de/unescape.rs +++ b/src/de/unescape.rs @@ -171,71 +171,71 @@ mod tests { #[test] fn unescape_works() { // Unchanged because no unescaping happens - assert_eq!(ue(b""), "".to_string()); - assert_eq!(ue(b"a"), "a".to_string()); - assert_eq!(ue(b"ab"), "ab".to_string()); - assert_eq!(ue(b"abc"), "abc".to_string()); - assert_eq!(ue(b"a/c"), "a/c".to_string()); - assert_eq!(ue(b"\x20"), " ".to_string()); // first non-control character - assert_eq!(ue(b"\xF0\x9F\x91\x8F"), "👏".to_string()); // U+1F44F + assert_eq!(ue(b""), ""); + assert_eq!(ue(b"a"), "a"); + assert_eq!(ue(b"ab"), "ab"); + assert_eq!(ue(b"abc"), "abc"); + assert_eq!(ue(b"a/c"), "a/c"); + assert_eq!(ue(b"\x20"), " "); // first non-control character + assert_eq!(ue(b"\xF0\x9F\x91\x8F"), "👏"); // U+1F44F // even number of backslashes - assert_eq!(ue(br#"\\"#), "\\".to_string()); - assert_eq!(ue(br#"\\\\"#), "\\\\".to_string()); - assert_eq!(ue(br#"\\\\\\"#), "\\\\\\".to_string()); - assert_eq!(ue(br#"\\\\\\\\"#), "\\\\\\\\".to_string()); + assert_eq!(ue(br#"\\"#), "\\"); + assert_eq!(ue(br#"\\\\"#), "\\\\"); + assert_eq!(ue(br#"\\\\\\"#), "\\\\\\"); + assert_eq!(ue(br#"\\\\\\\\"#), "\\\\\\\\"); // The 8 short escape sequences \", \\, \/, \b, \f, \n, \r, \t (alone, start, end, middle) - assert_eq!(ue(br#"\""#), "\"".to_string()); - assert_eq!(ue(br#"\\"#), "\\".to_string()); - assert_eq!(ue(br#"\/"#), "/".to_string()); - assert_eq!(ue(br#"\b"#), "\x08".to_string()); - assert_eq!(ue(br#"\f"#), "\x0C".to_string()); - assert_eq!(ue(br#"\n"#), "\n".to_string()); - assert_eq!(ue(br#"\r"#), "\r".to_string()); - assert_eq!(ue(br#"\t"#), "\t".to_string()); - assert_eq!(ue(br#"\"abc"#), "\"abc".to_string()); - assert_eq!(ue(br#"\\abc"#), "\\abc".to_string()); - assert_eq!(ue(br#"\/abc"#), "/abc".to_string()); - assert_eq!(ue(br#"\babc"#), "\x08abc".to_string()); - assert_eq!(ue(br#"\fabc"#), "\x0Cabc".to_string()); - assert_eq!(ue(br#"\nabc"#), "\nabc".to_string()); - assert_eq!(ue(br#"\rabc"#), "\rabc".to_string()); - assert_eq!(ue(br#"\tabc"#), "\tabc".to_string()); - assert_eq!(ue(br#"abc\""#), "abc\"".to_string()); - assert_eq!(ue(br#"abc\\"#), "abc\\".to_string()); - assert_eq!(ue(br#"abc\/"#), "abc/".to_string()); - assert_eq!(ue(br#"abc\b"#), "abc\x08".to_string()); - assert_eq!(ue(br#"abc\f"#), "abc\x0C".to_string()); - assert_eq!(ue(br#"abc\n"#), "abc\n".to_string()); - assert_eq!(ue(br#"abc\r"#), "abc\r".to_string()); - assert_eq!(ue(br#"abc\t"#), "abc\t".to_string()); - assert_eq!(ue(br#"xy\"abc"#), "xy\"abc".to_string()); - assert_eq!(ue(br#"xy\\abc"#), "xy\\abc".to_string()); - assert_eq!(ue(br#"xy\/abc"#), "xy/abc".to_string()); - assert_eq!(ue(br#"xy\babc"#), "xy\x08abc".to_string()); - assert_eq!(ue(br#"xy\fabc"#), "xy\x0Cabc".to_string()); - assert_eq!(ue(br#"xy\nabc"#), "xy\nabc".to_string()); - assert_eq!(ue(br#"xy\rabc"#), "xy\rabc".to_string()); - assert_eq!(ue(br#"xy\tabc"#), "xy\tabc".to_string()); + assert_eq!(ue(br#"\""#), "\""); + assert_eq!(ue(br#"\\"#), "\\"); + assert_eq!(ue(br#"\/"#), "/"); + assert_eq!(ue(br#"\b"#), "\x08"); + assert_eq!(ue(br#"\f"#), "\x0C"); + assert_eq!(ue(br#"\n"#), "\n"); + assert_eq!(ue(br#"\r"#), "\r"); + assert_eq!(ue(br#"\t"#), "\t"); + assert_eq!(ue(br#"\"abc"#), "\"abc"); + assert_eq!(ue(br#"\\abc"#), "\\abc"); + assert_eq!(ue(br#"\/abc"#), "/abc"); + assert_eq!(ue(br#"\babc"#), "\x08abc"); + assert_eq!(ue(br#"\fabc"#), "\x0Cabc"); + assert_eq!(ue(br#"\nabc"#), "\nabc"); + assert_eq!(ue(br#"\rabc"#), "\rabc"); + assert_eq!(ue(br#"\tabc"#), "\tabc"); + assert_eq!(ue(br#"abc\""#), "abc\""); + assert_eq!(ue(br#"abc\\"#), "abc\\"); + assert_eq!(ue(br#"abc\/"#), "abc/"); + assert_eq!(ue(br#"abc\b"#), "abc\x08"); + assert_eq!(ue(br#"abc\f"#), "abc\x0C"); + assert_eq!(ue(br#"abc\n"#), "abc\n"); + assert_eq!(ue(br#"abc\r"#), "abc\r"); + assert_eq!(ue(br#"abc\t"#), "abc\t"); + assert_eq!(ue(br#"xy\"abc"#), "xy\"abc"); + assert_eq!(ue(br#"xy\\abc"#), "xy\\abc"); + assert_eq!(ue(br#"xy\/abc"#), "xy/abc"); + assert_eq!(ue(br#"xy\babc"#), "xy\x08abc"); + assert_eq!(ue(br#"xy\fabc"#), "xy\x0Cabc"); + assert_eq!(ue(br#"xy\nabc"#), "xy\nabc"); + assert_eq!(ue(br#"xy\rabc"#), "xy\rabc"); + assert_eq!(ue(br#"xy\tabc"#), "xy\tabc"); // Short escape sequences mixed - assert_eq!(ue(br#" \" \" \" "#), " \" \" \" ".to_string()); - assert_eq!(ue(br#" \t \n \r "#), " \t \n \r ".to_string()); - assert_eq!(ue(br#" \"\"\" "#), " \"\"\" ".to_string()); - assert_eq!(ue(br#" \t\n\r "#), " \t\n\r ".to_string()); + assert_eq!(ue(br#" \" \" \" "#), " \" \" \" "); + assert_eq!(ue(br#" \t \n \r "#), " \t \n \r "); + assert_eq!(ue(br#" \"\"\" "#), " \"\"\" "); + assert_eq!(ue(br#" \t\n\r "#), " \t\n\r "); // Unicode - assert_eq!(ue(br#" \u0001 "#), " \u{0001} ".to_string()); - assert_eq!(ue(br#" \u0010 "#), " \u{0010} ".to_string()); - assert_eq!(ue(br#" \u0100 "#), " \u{0100} ".to_string()); - assert_eq!(ue(br#" \u1000 "#), " \u{1000} ".to_string()); - assert_eq!(ue(br#" \uABCD "#), " \u{abcd} ".to_string()); - assert_eq!(ue(br#" \uabcd "#), " \u{abcd} ".to_string()); - assert_eq!(ue(br#" \uAbCd "#), " \u{abcd} ".to_string()); - assert_eq!(ue(br#" \uABCDefg "#), " \u{abcd}efg ".to_string()); - assert_eq!(ue(br#" \uabcdefg "#), " \u{abcd}efg ".to_string()); - assert_eq!(ue(br#" \uAbCdefg "#), " \u{abcd}efg ".to_string()); + assert_eq!(ue(br#" \u0001 "#), " \u{0001} "); + assert_eq!(ue(br#" \u0010 "#), " \u{0010} "); + assert_eq!(ue(br#" \u0100 "#), " \u{0100} "); + assert_eq!(ue(br#" \u1000 "#), " \u{1000} "); + assert_eq!(ue(br#" \uABCD "#), " \u{abcd} "); + assert_eq!(ue(br#" \uabcd "#), " \u{abcd} "); + assert_eq!(ue(br#" \uAbCd "#), " \u{abcd} "); + assert_eq!(ue(br#" \uABCDefg "#), " \u{abcd}efg "); + assert_eq!(ue(br#" \uabcdefg "#), " \u{abcd}efg "); + assert_eq!(ue(br#" \uAbCdefg "#), " \u{abcd}efg "); } #[test] @@ -304,11 +304,11 @@ mod tests { #[test] fn unescape_works_for_surrogate_pairs() { - assert_eq!(ue(br#" \uD83D\uDC4F "#), " \u{1F44F} ".to_string()); - assert_eq!(ue(br#" \uD83D\uDC4F\uD83D\uDC4F "#), " 👏👏 ".to_string()); + assert_eq!(ue(br#" \uD83D\uDC4F "#), " \u{1F44F} "); + assert_eq!(ue(br#" \uD83D\uDC4F\uD83D\uDC4F "#), " 👏👏 "); - assert_eq!(ue(br#" \uD83E\uDD7A "#), " \u{1F97A} ".to_string()); - assert_eq!(ue(br#" \uD83E\uDD7A\uD83D\uDC4F "#), " 🥺👏 ".to_string()); + assert_eq!(ue(br#" \uD83E\uDD7A "#), " \u{1F97A} "); + assert_eq!(ue(br#" \uD83E\uDD7A\uD83D\uDC4F "#), " 🥺👏 "); } #[test] diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 3a2e2d68b..9fa2403cf 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -1248,14 +1248,14 @@ mod tests { let err_input = MyResult::Err("some error".to_string()); let json = to_string(&err_input).expect("encode err enum"); - assert_eq!(json, r#"{"err":"some error"}"#.to_string()); + assert_eq!(json, r#"{"err":"some error"}"#); let loaded = crate::from_str(&json).expect("re-load err enum"); assert_eq!(err_input, loaded); assert_serde_json_serialize_eq!(&err_input); let unit = MyResult::Unit(()); let json = to_string(&unit).expect("encode unit enum"); - assert_eq!(json, r#"{"unit":null}"#.to_string()); + assert_eq!(json, r#"{"unit":null}"#); let loaded = crate::from_str(&json).expect("re-load unit enum"); assert_eq!(unit, loaded); assert_serde_json_serialize_eq!(&unit); @@ -1268,7 +1268,7 @@ mod tests { let json = to_string(&empty_list).expect("encode ok enum"); assert_eq!( json, - r#"{"ok":{"log":"log message","count":137,"list":[]}}"#.to_string() + r#"{"ok":{"log":"log message","count":137,"list":[]}}"# ); let loaded = crate::from_str(&json).expect("re-load ok enum"); assert_eq!(empty_list, loaded); @@ -1280,10 +1280,7 @@ mod tests { list: vec![18u32, 34, 12], }); let json = to_string(&full_list).expect("encode ok enum"); - assert_eq!( - json, - r#"{"ok":{"log":null,"count":137,"list":[18,34,12]}}"#.to_string() - ); + assert_eq!(json, r#"{"ok":{"log":null,"count":137,"list":[18,34,12]}}"#); let loaded = crate::from_str(&json).expect("re-load ok enum"); assert_eq!(full_list, loaded); assert_serde_json_serialize_eq!(&full_list); From 141157116444ecb6226b95a01dd3f1d6ab0ddb58 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Thu, 3 Aug 2023 17:49:26 +0200 Subject: [PATCH 168/178] =?UTF-8?q?ser:=20don=E2=80=99t=20unnecessarily=20?= =?UTF-8?q?allocate=20a=20String=20when=20serialising=20a=20character?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use on-stack buffer to encode a character rather than allocating a new String. It’s faster and more memory-efficient. --- src/ser/map.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ser/map.rs b/src/ser/map.rs index c82c2ee34..be69edf5a 100644 --- a/src/ser/map.rs +++ b/src/ser/map.rs @@ -166,7 +166,8 @@ impl<'a> ser::Serializer for MapKeySerializer<'a> { } fn serialize_char(self, value: char) -> Result<()> { - self.ser.serialize_str(&value.to_string()) + let mut buf = [0u8; 4]; + self.ser.serialize_str(value.encode_utf8(&mut buf)) } fn serialize_bytes(self, _value: &[u8]) -> Result<()> { From 0abb3654f97bd095b7d477234124e46b7fbe2595 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 24 Jul 2023 04:13:56 +0200 Subject: [PATCH 169/178] wip --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 4ce4e51e3..69abab874 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,6 +54,7 @@ #![deny(missing_docs)] #![deny(rust_2018_compatibility)] #![deny(rust_2018_idioms)] +#![cfg_attr(not(feature = "std"), no_std)] pub mod de; pub mod ser; From 00c79817558c4294b33c45bc904cd2ae779c42b1 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Fri, 14 Jul 2023 01:36:20 +0200 Subject: [PATCH 170/178] Make library no_std and introduce std and unstable features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove all references to std from the library and introduce std and unstable features which enable corresponding serde features. In default build, std is enabled and nothing changes. However, if std is disabled, the library becomes no_std. Downside of lack of std is that Error enum no longer implements std::error;:Error. To remedy that, unstable feature uses core::error::Error but requires a nightly compiler. For the most part, this commit is mostly just replacing references to std crate with core and alloc crates. On exception is std::error::Error which is replaced by serde’s StdError such that Error enums implement whatever trait serde uses. With std or unstable feature enabled it’s just an alias to {std,core}::error::Error. Issue: https://github.com/CosmWasm/cosmwasm/issues/1484 Issue: https://github.com/CosmWasm/serde-json-wasm/issues/40 --- CHANGELOG.md | 7 ++++ Cargo.lock | 4 +-- Cargo.toml | 7 +++- src/de/errors.rs | 30 ++++++++---------- src/de/mod.rs | 12 +++++-- src/de/unescape.rs | 4 ++- src/lib.rs | 24 ++++++++++---- src/ser/map.rs | 6 ++-- src/ser/mod.rs | 79 ++++++++++++++++++---------------------------- 9 files changed, 91 insertions(+), 82 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7cbd6e36..01c3e82c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,17 @@ project adheres to [Semantic Versioning](http://semver.org/). ### Changed +- Introduce `std` (enabled by default) and `unstable` features ([#58]). They + enable corresponding serde’s features and if either is enabled, `Error` + implements `std::error::Error` trait. By itself, `serde-json-wasm` is now + `no_std`; it’s up to serde’s features whether the entire build is. **Please + not:** this potentially breaks `default-features = false` builds. + - Serialize / deserialize `u128`/`i128` types as numbers instead of strings ([#59]).
**Please note:** this breaks deserialization of `u128`/`i128` serialized with older versions of `serde-json-wasm`. +[#58]: https://github.com/CosmWasm/serde-json-wasm/pull/58 [#59]: https://github.com/CosmWasm/serde-json-wasm/pull/59 ## [0.5.1] - 2023-04-11 diff --git a/Cargo.lock b/Cargo.lock index a3386ade4..ec182a22d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,9 +34,9 @@ checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] name = "serde" -version = "1.0.136" +version = "1.0.181" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "6d3e73c93c3240c0bda063c239298e633114c69a888c3e37ca8bb33f343e9890" [[package]] name = "serde-json-wasm" diff --git a/Cargo.toml b/Cargo.toml index a56dbfa5f..49f01e333 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,8 +23,13 @@ exclude = [ ] [dependencies] -serde = { version = "^1.0.80", default-features = false, features = ["alloc"] } +serde = { version = "^1.0.181", default-features = false, features = ["alloc"] } [dev-dependencies] serde_derive = "^1.0.80" serde_json = "^1.0.99" + +[features] +default = ["std"] +std = ["serde/std"] +unstable = ["serde/unstable"] diff --git a/src/de/errors.rs b/src/de/errors.rs index 47876acf2..bc5a07f7e 100644 --- a/src/de/errors.rs +++ b/src/de/errors.rs @@ -1,10 +1,17 @@ +use alloc::string::{String, ToString}; + use serde::de; -use std::{error, fmt}; /// Deserialization result pub type Result = core::result::Result; -/// This type represents all possible errors that can occur when deserializing JSON data +/// This type represents all possible errors that can occur when deserializing +/// JSON data +/// +/// It implements [`std::error::Error`] trait so long as either `std` or +/// `unstable` features are enabled. `std` is enabled by default and disabling +/// it makes the crate `no_std`. `unstable` makes sit necessary to build code +/// with nightly compiler. #[derive(Debug, PartialEq, Eq)] #[non_exhaustive] pub enum Error { @@ -72,27 +79,16 @@ pub enum Error { Custom(String), } -impl error::Error for Error { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - None - } - - fn description(&self) -> &str { - "(use display)" - } -} +impl de::StdError for Error {} impl de::Error for Error { - fn custom(msg: T) -> Self - where - T: fmt::Display, - { + fn custom(msg: T) -> Self { Error::Custom(msg.to_string()) } } -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!( f, "{}", diff --git a/src/de/mod.rs b/src/de/mod.rs index 755b4ccf8..1919cfcd0 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -6,6 +6,8 @@ mod map; mod seq; mod unescape; +use alloc::string::String; + pub use errors::{Error, Result}; use serde::de::{self, Visitor}; @@ -13,7 +15,6 @@ use serde::de::{self, Visitor}; use self::enum_::{StructVariantAccess, UnitVariantAccess}; use self::map::MapAccess; use self::seq::SeqAccess; -use std::str::from_utf8; /// Deserializer will parse serde-json-wasm flavored JSON into a /// serde-annotated struct @@ -126,7 +127,7 @@ impl<'a> Deserializer<'a> { )?)) } else { Ok(StringLike::Borrowed( - from_utf8(&self.slice[start..end]) + core::str::from_utf8(&self.slice[start..end]) .map_err(|_| Error::InvalidUnicodeCodePoint)?, )) }; @@ -651,6 +652,11 @@ where #[cfg(test)] mod tests { use super::from_str; + + use alloc::string::{String, ToString}; + use alloc::vec; + use alloc::vec::Vec; + use serde_derive::{Deserialize, Serialize}; #[derive(Debug, Deserialize, PartialEq)] @@ -1103,7 +1109,7 @@ mod tests { #[test] fn numbered_key_maps() { - use std::collections::BTreeMap; + use alloc::collections::BTreeMap; // u8 let mut ranking: BTreeMap = BTreeMap::new(); diff --git a/src/de/unescape.rs b/src/de/unescape.rs index 7f5b5b5e2..38af980c4 100644 --- a/src/de/unescape.rs +++ b/src/de/unescape.rs @@ -1,4 +1,6 @@ -use std::convert::TryFrom; +use alloc::string::String; +use alloc::vec::Vec; +use core::convert::TryFrom; use super::errors::{Error, Result}; diff --git a/src/lib.rs b/src/lib.rs index 69abab874..c63ceda63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,7 +54,15 @@ #![deny(missing_docs)] #![deny(rust_2018_compatibility)] #![deny(rust_2018_idioms)] -#![cfg_attr(not(feature = "std"), no_std)] +// Note: Even though we declare the crate as `no_std`, by default `std` feature +// is enabled which enables serde’s `std` feature which makes our dependency +// non-`no_std`. This `no_std` declaration only makes sure that our code +// doesn’t depend on `std` directly (except for tests). +#![no_std] + +extern crate alloc; +#[cfg(test)] +extern crate std; pub mod de; pub mod ser; @@ -66,7 +74,11 @@ pub use self::ser::{to_string, to_vec}; #[cfg(test)] mod test { - use std::collections::BTreeMap; + use alloc::borrow::ToOwned; + use alloc::collections::BTreeMap; + use alloc::string::{String, ToString}; + use alloc::vec; + use alloc::vec::Vec; use super::*; use serde_derive::{Deserialize, Serialize}; @@ -105,7 +117,7 @@ mod test { fn can_serde() { let min = Item { model: Model::Comment, - title: "".to_string(), + title: String::new(), content: None, list: vec![], published: false, @@ -122,12 +134,12 @@ mod test { }, title: "Nice message".to_string(), content: Some("Happy \"blogging\" 👏\n\n\tCheers, I'm out\0\0\0".to_string()), - list: vec![0, 1, 2, 3, 42, 154841, std::u32::MAX], + list: vec![0, 1, 2, 3, 42, 154841, u32::MAX], published: true, comments: vec![CommentId(2), CommentId(700)], stats: Stats { - views: std::u64::MAX, - score: std::i64::MIN, + views: u64::MAX, + score: i64::MIN, }, balances, }; diff --git a/src/ser/map.rs b/src/ser/map.rs index be69edf5a..624775435 100644 --- a/src/ser/map.rs +++ b/src/ser/map.rs @@ -1,5 +1,3 @@ -use std::fmt; - use serde::{ser, Serialize}; use crate::ser::{Error, Result, Serializer}; @@ -55,7 +53,7 @@ struct MapKeySerializer<'a> { } pub(crate) fn key_must_be_a_string() -> Error { - Error::Custom("JSON object key is required to be a string type.".to_string()) + Error::Custom("JSON object key is required to be a string type.".into()) } macro_rules! serialize_unsigned_key { @@ -252,7 +250,7 @@ impl<'a> ser::Serializer for MapKeySerializer<'a> { fn collect_str(self, _value: &T) -> Result<()> where - T: ?Sized + fmt::Display, + T: ?Sized + core::fmt::Display, { unreachable!() } diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 9fa2403cf..33441b852 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -1,11 +1,10 @@ //! Serialize a Rust data structure into JSON data -use std::{error, fmt}; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use serde::ser; -use std::vec::Vec; - use self::map::SerializeMap; use self::seq::SerializeSeq; use self::struct_::SerializeStruct; @@ -18,6 +17,11 @@ mod struct_; pub type Result = ::core::result::Result; /// This type represents all possible errors that can occur when serializing JSON data +/// +/// It implements [`std::error::Error`] trait so long as either `std` or +/// `unstable` features are enabled. `std` is enabled by default and disabling +/// it makes the crate `no_std`. `unstable` makes sit necessary to build code +/// with nightly compiler. #[derive(Debug)] #[non_exhaustive] pub enum Error { @@ -40,18 +44,10 @@ impl From for Error { } } -impl error::Error for Error { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - None - } - - fn description(&self) -> &str { - "(use display)" - } -} +impl ser::StdError for Error {} -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Error::BufferFull => write!(f, "Buffer is full"), Error::Custom(msg) => write!(f, "{}", &msg), @@ -443,10 +439,7 @@ where } impl ser::Error for Error { - fn custom(msg: T) -> Self - where - T: fmt::Display, - { + fn custom(msg: T) -> Self { Error::Custom(msg.to_string()) } } @@ -522,8 +515,14 @@ impl ser::SerializeStructVariant for Unreachable { #[cfg(test)] mod tests { - use super::to_string; + + use alloc::collections::BTreeMap; + use alloc::string::{String, ToString}; + use alloc::vec; + use alloc::vec::Vec; + use std::collections::HashMap; + use serde::{Serialize, Serializer}; use serde_derive::{Deserialize, Serialize}; @@ -551,37 +550,37 @@ mod tests { fn number() { assert_eq!(to_string::(&0).unwrap(), "0"); assert_eq!(to_string::(&1).unwrap(), "1"); - assert_eq!(to_string::(&std::u8::MAX).unwrap(), "255"); + assert_eq!(to_string::(&u8::MAX).unwrap(), "255"); assert_eq!(to_string::(&0).unwrap(), "0"); assert_eq!(to_string::(&1).unwrap(), "1"); assert_eq!(to_string::(&127).unwrap(), "127"); assert_eq!(to_string::(&-1).unwrap(), "-1"); - assert_eq!(to_string::(&std::i8::MIN).unwrap(), "-128"); + assert_eq!(to_string::(&i8::MIN).unwrap(), "-128"); assert_eq!(to_string::(&0).unwrap(), "0"); assert_eq!(to_string::(&1).unwrap(), "1"); assert_eq!(to_string::(&550).unwrap(), "550"); - assert_eq!(to_string::(&std::u16::MAX).unwrap(), "65535"); + assert_eq!(to_string::(&u16::MAX).unwrap(), "65535"); assert_eq!(to_string::(&0).unwrap(), "0"); assert_eq!(to_string::(&1).unwrap(), "1"); assert_eq!(to_string::(&550).unwrap(), "550"); - assert_eq!(to_string::(&std::i16::MAX).unwrap(), "32767"); + assert_eq!(to_string::(&i16::MAX).unwrap(), "32767"); assert_eq!(to_string::(&-1).unwrap(), "-1"); - assert_eq!(to_string::(&std::i16::MIN).unwrap(), "-32768"); + assert_eq!(to_string::(&i16::MIN).unwrap(), "-32768"); assert_eq!(to_string::(&0).unwrap(), "0"); assert_eq!(to_string::(&1).unwrap(), "1"); assert_eq!(to_string::(&456789).unwrap(), "456789"); - assert_eq!(to_string::(&std::u32::MAX).unwrap(), "4294967295"); + assert_eq!(to_string::(&u32::MAX).unwrap(), "4294967295"); assert_eq!(to_string::(&0).unwrap(), "0"); assert_eq!(to_string::(&1).unwrap(), "1"); assert_eq!(to_string::(&456789).unwrap(), "456789"); - assert_eq!(to_string::(&std::i32::MAX).unwrap(), "2147483647"); + assert_eq!(to_string::(&i32::MAX).unwrap(), "2147483647"); assert_eq!(to_string::(&-1).unwrap(), "-1"); - assert_eq!(to_string::(&std::i32::MIN).unwrap(), "-2147483648"); + assert_eq!(to_string::(&i32::MIN).unwrap(), "-2147483648"); assert_eq!(to_string::(&0).unwrap(), "0"); assert_eq!(to_string::(&1).unwrap(), "1"); @@ -596,10 +595,7 @@ mod tests { to_string::(&9007199254740992).unwrap(), "9007199254740992" ); // Number.MAX_SAFE_INTEGER+1 - assert_eq!( - to_string::(&std::u64::MAX).unwrap(), - "18446744073709551615" - ); + assert_eq!(to_string::(&u64::MAX).unwrap(), "18446744073709551615"); assert_eq!(to_string::(&0).unwrap(), "0"); assert_eq!(to_string::(&1).unwrap(), "1"); @@ -614,15 +610,9 @@ mod tests { to_string::(&9007199254740992).unwrap(), "9007199254740992" ); // Number.MAX_SAFE_INTEGER+1 - assert_eq!( - to_string::(&std::i64::MAX).unwrap(), - "9223372036854775807" - ); + assert_eq!(to_string::(&i64::MAX).unwrap(), "9223372036854775807"); assert_eq!(to_string::(&-1).unwrap(), "-1"); - assert_eq!( - to_string::(&std::i64::MIN).unwrap(), - "-9223372036854775808" - ); + assert_eq!(to_string::(&i64::MIN).unwrap(), "-9223372036854775808"); assert_eq!(to_string::(&0).unwrap(), r#"0"#); assert_eq!(to_string::(&1).unwrap(), r#"1"#); @@ -711,7 +701,7 @@ mod tests { type BigPair = (u128, u128); - let pair: BigPair = (std::u128::MAX, std::u128::MAX); + let pair: BigPair = (u128::MAX, u128::MAX); assert_eq!( to_string(&pair).unwrap(), @@ -1007,7 +997,7 @@ mod tests { } let users = Users { - users: vec!["joe".to_string(), "alice".to_string()], + users: vec!["joe".into(), "alice".into()], pagination: Pagination { offset: 100, limit: 20, @@ -1024,8 +1014,6 @@ mod tests { #[test] fn btree_map() { - use std::collections::BTreeMap; - // empty map let empty = BTreeMap::<(), ()>::new(); assert_eq!(to_string(&empty).unwrap(), r#"{}"#); @@ -1060,8 +1048,6 @@ mod tests { #[test] fn hash_map() { - use std::collections::HashMap; - // empty map let empty = HashMap::<(), ()>::new(); assert_eq!(to_string(&empty).unwrap(), r#"{}"#); @@ -1109,8 +1095,6 @@ mod tests { #[test] fn number_key() { - use std::collections::HashMap; - // i8 key let mut map = HashMap::new(); map.insert(10i8, "my_age"); @@ -1173,7 +1157,6 @@ mod tests { #[test] fn invalid_json_key() { use crate::ser::map::key_must_be_a_string; - use std::collections::HashMap; #[derive(Debug, Serialize, PartialEq, Eq, Hash)] #[serde(rename_all = "lowercase")] From 2b24610eb9a4778afab67c167b5106fd1fd0b950 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 4 Aug 2023 17:19:03 +0200 Subject: [PATCH 171/178] Fix typos --- CHANGELOG.md | 2 +- src/de/errors.rs | 2 +- src/ser/mod.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01c3e82c9..b5f494616 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ project adheres to [Semantic Versioning](http://semver.org/). enable corresponding serde’s features and if either is enabled, `Error` implements `std::error::Error` trait. By itself, `serde-json-wasm` is now `no_std`; it’s up to serde’s features whether the entire build is. **Please - not:** this potentially breaks `default-features = false` builds. + note:** this potentially breaks `default-features = false` builds. - Serialize / deserialize `u128`/`i128` types as numbers instead of strings ([#59]).
**Please note:** this breaks deserialization of `u128`/`i128` diff --git a/src/de/errors.rs b/src/de/errors.rs index bc5a07f7e..a167dc4da 100644 --- a/src/de/errors.rs +++ b/src/de/errors.rs @@ -10,7 +10,7 @@ pub type Result = core::result::Result; /// /// It implements [`std::error::Error`] trait so long as either `std` or /// `unstable` features are enabled. `std` is enabled by default and disabling -/// it makes the crate `no_std`. `unstable` makes sit necessary to build code +/// it makes the crate `no_std`. `unstable` makes it necessary to build code /// with nightly compiler. #[derive(Debug, PartialEq, Eq)] #[non_exhaustive] diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 33441b852..dd24afd3d 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -20,7 +20,7 @@ pub type Result = ::core::result::Result; /// /// It implements [`std::error::Error`] trait so long as either `std` or /// `unstable` features are enabled. `std` is enabled by default and disabling -/// it makes the crate `no_std`. `unstable` makes sit necessary to build code +/// it makes the crate `no_std`. `unstable` makes it necessary to build code /// with nightly compiler. #[derive(Debug)] #[non_exhaustive] From 05c08e6750ae7826b17b90bc0c7f52635cd4770a Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 4 Aug 2023 17:28:37 +0200 Subject: [PATCH 172/178] Set version: 1.0.0 --- CHANGELOG.md | 8 ++++++-- Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5f494616..2d36e7e3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,13 +7,16 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [1.0.0] - 2023-08-04 + ### Changed - Introduce `std` (enabled by default) and `unstable` features ([#58]). They enable corresponding serde’s features and if either is enabled, `Error` implements `std::error::Error` trait. By itself, `serde-json-wasm` is now - `no_std`; it’s up to serde’s features whether the entire build is. **Please + `no_std`; it’s up to serde’s features whether the entire build is.
**Please note:** this potentially breaks `default-features = false` builds. + If you need the `Error` trait impl, enable one of the two features explicitly. - Serialize / deserialize `u128`/`i128` types as numbers instead of strings ([#59]).
**Please note:** this breaks deserialization of `u128`/`i128` @@ -141,7 +144,8 @@ Initial release after forking from [serde-json-core](https://github.com/japaric/serde-json-core) at [bf5533a0](https://github.com/japaric/serde-json-core/commit/bf5533a042a0). -[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.5.1...HEAD +[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v1.0.0...HEAD +[1.0.0]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.5.1...v1.0.0 [0.5.1]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.5.0...v0.5.1 [0.5.0]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.4.1...v0.5.0 [0.4.1]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.4.0...v0.4.1 diff --git a/Cargo.lock b/Cargo.lock index ec182a22d..061239f11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,7 +40,7 @@ checksum = "6d3e73c93c3240c0bda063c239298e633114c69a888c3e37ca8bb33f343e9890" [[package]] name = "serde-json-wasm" -version = "0.5.1" +version = "1.0.0" dependencies = [ "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index 49f01e333..8389629cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "MIT OR Apache-2.0" name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/CosmWasm/serde-json-wasm" -version = "0.5.1" +version = "1.0.0" exclude = [ ".cargo/", ".github/", From e78f9e28b3a2151d3175ee88ab2a001bf9515429 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 23 Jan 2024 16:22:54 +0100 Subject: [PATCH 173/178] Add recursion limit --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/de/errors.rs | 4 ++++ src/de/mod.rs | 59 ++++++++++++++++++++++++++++++++++++++---------- src/lib.rs | 25 ++++++++++++++++++++ 5 files changed, 78 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 061239f11..2ea4c2f24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,7 +40,7 @@ checksum = "6d3e73c93c3240c0bda063c239298e633114c69a888c3e37ca8bb33f343e9890" [[package]] name = "serde-json-wasm" -version = "1.0.0" +version = "1.0.1" dependencies = [ "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index 8389629cf..14daa54d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "MIT OR Apache-2.0" name = "serde-json-wasm" readme = "README.md" repository = "https://github.com/CosmWasm/serde-json-wasm" -version = "1.0.0" +version = "1.0.1" exclude = [ ".cargo/", ".github/", diff --git a/src/de/errors.rs b/src/de/errors.rs index a167dc4da..9b79e9edd 100644 --- a/src/de/errors.rs +++ b/src/de/errors.rs @@ -75,6 +75,9 @@ pub enum Error { /// JSON has a comma after the last value in an array or map. TrailingComma, + /// JSON is nested too deeply, exceeeded the recursion limit. + RecursionLimitExceeded, + /// Custom error message from serde Custom(String), } @@ -128,6 +131,7 @@ impl core::fmt::Display for Error { value." } Error::TrailingComma => "JSON has a comma after the last value in an array or map.", + Error::RecursionLimitExceeded => "JSON is nested too deeply, exceeeded the recursion limit.", Error::Custom(msg) => msg, } ) diff --git a/src/de/mod.rs b/src/de/mod.rs index 1919cfcd0..1eb599e24 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -21,6 +21,9 @@ use self::seq::SeqAccess; pub struct Deserializer<'b> { slice: &'b [u8], index: usize, + + /// Remaining depth until we hit the recursion limit + remaining_depth: u8, } enum StringLike<'a> { @@ -30,7 +33,11 @@ enum StringLike<'a> { impl<'a> Deserializer<'a> { fn new(slice: &'a [u8]) -> Deserializer<'_> { - Deserializer { slice, index: 0 } + Deserializer { + slice, + index: 0, + remaining_depth: 128, + } } fn eat_char(&mut self) { @@ -287,16 +294,22 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { } } b'[' => { - self.eat_char(); - let ret = visitor.visit_seq(SeqAccess::new(self))?; + check_recursion! { + self.eat_char(); + let ret = visitor.visit_seq(SeqAccess::new(self)); + } + let ret = ret?; self.end_seq()?; Ok(ret) } b'{' => { - self.eat_char(); - let ret = visitor.visit_map(MapAccess::new(self))?; + check_recursion! { + self.eat_char(); + let ret = visitor.visit_map(MapAccess::new(self)); + } + let ret = ret?; self.end_map()?; @@ -513,8 +526,11 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { { match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? { b'[' => { - self.eat_char(); - let ret = visitor.visit_seq(SeqAccess::new(self))?; + check_recursion! { + self.eat_char(); + let ret = visitor.visit_seq(SeqAccess::new(self)); + } + let ret = ret?; self.end_seq()?; @@ -550,9 +566,11 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { let peek = self.parse_whitespace().ok_or(Error::EofWhileParsingValue)?; if peek == b'{' { - self.eat_char(); - - let ret = visitor.visit_map(MapAccess::new(self))?; + check_recursion! { + self.eat_char(); + let ret = visitor.visit_map(MapAccess::new(self)); + } + let ret = ret?; self.end_map()?; @@ -588,8 +606,11 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { b'"' => visitor.visit_enum(UnitVariantAccess::new(self)), // if it is a struct enum b'{' => { - self.eat_char(); - visitor.visit_enum(StructVariantAccess::new(self)) + check_recursion! { + self.eat_char(); + let value = visitor.visit_enum(StructVariantAccess::new(self)); + } + value } _ => Err(Error::ExpectedSomeIdent), } @@ -649,6 +670,20 @@ where from_slice(s.as_bytes()) } +macro_rules! check_recursion { + ($this:ident $($body:tt)*) => { + $this.remaining_depth -= 1; + if $this.remaining_depth == 0 { + return Err($crate::de::Error::RecursionLimitExceeded); + } + + $this $($body)* + + $this.remaining_depth += 1; + }; +} +pub(crate) use check_recursion; + #[cfg(test)] mod tests { use super::from_str; diff --git a/src/lib.rs b/src/lib.rs index c63ceda63..ebb488ab6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -227,4 +227,29 @@ mod test { item ); } + + #[test] + fn no_stack_overflow() { + const AMOUNT: usize = 2000; + let mut json = String::from(r#"{"":"#); + + #[derive(Debug, Deserialize, Serialize)] + pub struct Person { + name: String, + age: u8, + phones: Vec, + } + + for _ in 0..AMOUNT { + json.push('['); + } + for _ in 0..AMOUNT { + json.push(']'); + } + + json.push_str(r#"] }[[[[[[[[[[[[[[[[[[[[[ ""","age":35,"phones":["#); + + let err = from_str::(&json).unwrap_err(); + assert_eq!(err, crate::de::Error::RecursionLimitExceeded); + } } From 249825a468aaf92b5271654c044e8536357292f3 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 23 Jan 2024 16:54:38 +0100 Subject: [PATCH 174/178] Fix typo --- src/de/errors.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/de/errors.rs b/src/de/errors.rs index 9b79e9edd..d66d01017 100644 --- a/src/de/errors.rs +++ b/src/de/errors.rs @@ -75,7 +75,7 @@ pub enum Error { /// JSON has a comma after the last value in an array or map. TrailingComma, - /// JSON is nested too deeply, exceeeded the recursion limit. + /// JSON is nested too deeply, exceeded the recursion limit. RecursionLimitExceeded, /// Custom error message from serde @@ -131,7 +131,7 @@ impl core::fmt::Display for Error { value." } Error::TrailingComma => "JSON has a comma after the last value in an array or map.", - Error::RecursionLimitExceeded => "JSON is nested too deeply, exceeeded the recursion limit.", + Error::RecursionLimitExceeded => "JSON is nested too deeply, exceeded the recursion limit.", Error::Custom(msg) => msg, } ) From 5f09b9fcd80f4e933f64eb671c04c2accf806b61 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 23 Jan 2024 17:58:04 +0100 Subject: [PATCH 175/178] Update changelog --- CHANGELOG.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d36e7e3b..c13ce6dd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,16 +7,24 @@ project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [1.0.1] - 2024-01-23 + +### Changed + +- Add recursion limit to deserialization ([#65]). + +[#65]: https://github.com/CosmWasm/serde-json-wasm/pull/65 + ## [1.0.0] - 2023-08-04 ### Changed -- Introduce `std` (enabled by default) and `unstable` features ([#58]). They +- Introduce `std` (enabled by default) and `unstable` features ([#58]). They enable corresponding serde’s features and if either is enabled, `Error` - implements `std::error::Error` trait. By itself, `serde-json-wasm` is now - `no_std`; it’s up to serde’s features whether the entire build is.
**Please - note:** this potentially breaks `default-features = false` builds. - If you need the `Error` trait impl, enable one of the two features explicitly. + implements `std::error::Error` trait. By itself, `serde-json-wasm` is now + `no_std`; it’s up to serde’s features whether the entire build is.
+ **Please note:** this potentially breaks `default-features = false` builds. If + you need the `Error` trait impl, enable one of the two features explicitly. - Serialize / deserialize `u128`/`i128` types as numbers instead of strings ([#59]).
**Please note:** this breaks deserialization of `u128`/`i128` From 2ab22a6f3a741b4db507c464185d86beefc5bab6 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 23 Jan 2024 17:59:15 +0100 Subject: [PATCH 176/178] Fix changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c13ce6dd1..f84e1593e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -152,7 +152,8 @@ Initial release after forking from [serde-json-core](https://github.com/japaric/serde-json-core) at [bf5533a0](https://github.com/japaric/serde-json-core/commit/bf5533a042a0). -[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v1.0.0...HEAD +[unreleased]: https://github.com/CosmWasm/serde-json-wasm/compare/v1.0.1...HEAD +[1.0.1]: https://github.com/CosmWasm/serde-json-wasm/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.5.1...v1.0.0 [0.5.1]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.5.0...v0.5.1 [0.5.0]: https://github.com/CosmWasm/serde-json-wasm/compare/v0.4.1...v0.5.0 From 22159215d60abb374a57dda3b124629fd12e9664 Mon Sep 17 00:00:00 2001 From: Dariusz Depta <141360751+DariuszDepta@users.noreply.github.com> Date: Fri, 29 Aug 2025 14:16:03 +0200 Subject: [PATCH 177/178] Added security policy. --- SECURITY.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..d74ab1965 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,6 @@ +# Security Policy + +This repository is part of the **CosmWasm** stack. +Please see the [Advisories] for its security policy. + +[Advisories]: https://github.com/CosmWasm/advisories/blob/main/SECURITY.md From 8eaaa86af5417fccb2946e8b94d758a21cc206ea Mon Sep 17 00:00:00 2001 From: Dariusz Depta <141360751+DariuszDepta@users.noreply.github.com> Date: Fri, 29 Aug 2025 15:19:28 +0200 Subject: [PATCH 178/178] Added NOTICE file. --- NOTICE | 13 +++++++++++++ README.md | 6 +++--- 2 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 NOTICE diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..8ccb4baee --- /dev/null +++ b/NOTICE @@ -0,0 +1,13 @@ +Copyright (c) 2021-2025 Confio GmbH + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md index af5619058..db996130b 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ A [serde-json] alternative for [CosmWasm] smart contracts. [serde-json]: https://crates.io/crates/serde_json -[cosmwasm]: https://cosmwasm.com/ +[cosmwasm]: https://cosmwasm.com +[notice-url]: NOTICE ## License Licensed under either of -- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) and [NOTICE][notice-url] - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option.