From 671864eef409aa80b6bc62f0729d9d85a273fbb3 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Wed, 8 Oct 2014 21:25:01 -0700 Subject: [PATCH] Support parsing json with tests --- serde2/src/de.rs | 224 ++++++++---- serde2/src/json/de.rs | 815 ++++++++++++++++++++++++++++++++++++++++++ serde2/src/lib.rs | 2 +- 3 files changed, 963 insertions(+), 78 deletions(-) create mode 100644 serde2/src/json/de.rs diff --git a/serde2/src/de.rs b/serde2/src/de.rs index cd667743..ea4f8f0b 100644 --- a/serde2/src/de.rs +++ b/serde2/src/de.rs @@ -1,5 +1,6 @@ use std::collections::{HashMap, TreeMap}; use std::hash::Hash; +use std::num; /////////////////////////////////////////////////////////////////////////////// @@ -30,18 +31,42 @@ pub trait Visitor, R, E> { Err(d.syntax_error()) } - fn visit_int(&mut self, d: &mut D, _v: int) -> Result { + fn visit_bool(&mut self, d: &mut D, _v: bool) -> Result { Err(d.syntax_error()) } - fn visit_str(&mut self, d: &mut D, v: &str) -> Result { - self.visit_string(d, v.to_string()) + fn visit_int(&mut self, d: &mut D, v: int) -> Result { + self.visit_i64(d, v as i64) } - fn visit_string(&mut self, d: &mut D, _v: String) -> Result { + fn visit_i64(&mut self, d: &mut D, _v: i64) -> Result { Err(d.syntax_error()) } + fn visit_uint(&mut self, d: &mut D, v: uint) -> Result { + self.visit_u64(d, v as u64) + } + + fn visit_u64(&mut self, d: &mut D, _v: u64) -> Result { + Err(d.syntax_error()) + } + + fn visit_f32(&mut self, d: &mut D, v: f32) -> Result { + self.visit_f64(d, v as f64) + } + + fn visit_f64(&mut self, d: &mut D, _v: f64) -> Result { + Err(d.syntax_error()) + } + + fn visit_str(&mut self, d: &mut D, _v: &str) -> Result { + Err(d.syntax_error()) + } + + fn visit_string(&mut self, d: &mut D, v: String) -> Result { + self.visit_str(d, v.as_slice()) + } + fn visit_option< V: OptionVisitor, >(&mut self, d: &mut D, _visitor: V) -> Result { @@ -107,24 +132,12 @@ impl< fn visit_null(&mut self, _d: &mut D) -> Result<(), E> { Ok(()) } - } - d.visit(&mut Visitor) - } -} - -/////////////////////////////////////////////////////////////////////////////// - -impl< - D: Deserializer, - E, -> Deserialize for int { - fn deserialize(d: &mut D) -> Result { - struct Visitor; - - impl, E> self::Visitor for Visitor { - fn visit_int(&mut self, _d: &mut D, v: int) -> Result { - Ok(v) + fn visit_seq< + V: SeqVisitor, + >(&mut self, d: &mut D, mut visitor: V) -> Result<(), E> { + try!(visitor.end(d)); + Ok(()) } } @@ -134,6 +147,67 @@ impl< /////////////////////////////////////////////////////////////////////////////// +impl< + D: Deserializer, + E, +> Deserialize for bool { + fn deserialize(d: &mut D) -> Result { + struct Visitor; + + impl, E> self::Visitor for Visitor { + fn visit_bool(&mut self, _d: &mut D, v: bool) -> Result { + Ok(v) + } + } + + d.visit(&mut Visitor) + } +} + +/////////////////////////////////////////////////////////////////////////////// + +macro_rules! impl_deserialize_num_method { + ($dst_ty:ty, $src_ty:ty, $method:ident) => { + fn $method(&mut self, d: &mut D, v: $src_ty) -> Result<$dst_ty, E> { + match num::cast(v) { + Some(v) => Ok(v), + None => Err(d.syntax_error()), + } + } + } +} + +macro_rules! impl_deserialize_num { + ($ty:ty) => { + impl, E> Deserialize for $ty { + #[inline] + fn deserialize(d: &mut D) -> Result<$ty, E> { + struct Visitor; + + impl, E> self::Visitor for Visitor { + impl_deserialize_num_method!($ty, int, visit_int) + impl_deserialize_num_method!($ty, i64, visit_i64) + impl_deserialize_num_method!($ty, uint, visit_uint) + impl_deserialize_num_method!($ty, u64, visit_u64) + impl_deserialize_num_method!($ty, f32, visit_f32) + impl_deserialize_num_method!($ty, f64, visit_f64) + } + + d.visit(&mut Visitor) + } + } + } +} + +impl_deserialize_num!(int) +impl_deserialize_num!(i64) +impl_deserialize_num!(uint) +impl_deserialize_num!(u64) +impl_deserialize_num!(f32) +impl_deserialize_num!(f64) + +/////////////////////////////////////////////////////////////////////////////// + impl< D: Deserializer, E, @@ -142,6 +216,10 @@ impl< struct Visitor; impl, E> self::Visitor for Visitor { + fn visit_str(&mut self, _d: &mut D, v: &str) -> Result { + Ok(v.to_string()) + } + fn visit_string(&mut self, _d: &mut D, v: String) -> Result { Ok(v) } @@ -217,66 +295,58 @@ impl< } } -impl< - T0: Deserialize, - T1: Deserialize, - D: Deserializer, - E, -> Deserialize for (T0, T1) { - fn deserialize(d: &mut D) -> Result<(T0, T1), E> { - struct Visitor; +/////////////////////////////////////////////////////////////////////////////// - impl< - T0: Deserialize, - T1: Deserialize, - D: Deserializer, - E, - > self::Visitor for Visitor { - fn visit_seq< - V: SeqVisitor, - >(&mut self, d: &mut D, mut visitor: V) -> Result<(T0, T1), E> { - let mut state = 0u; - let mut t0 = None; - let mut t1 = None; - - loop { - match state { - 0 => { - state += 1; - match try!(visitor.visit(d)) { - Some(value) => { - t0 = Some(value); - } - None => { - return Err(d.end_of_stream_error()); - } - } - } - 1 => { - state += 1; - match try!(visitor.visit(d)) { - Some(value) => { - t1 = Some(value); - } - None => { - return Err(d.end_of_stream_error()); - } - } - } - _ => { - try!(visitor.end(d)); - - return Ok((t0.unwrap(), t1.unwrap())); - } - } - } - } - } - - d.visit(&mut Visitor) +macro_rules! peel { + ($name:ident, $($other:ident,)*) => { + impl_deserialize_tuple!($($other,)*) } } +macro_rules! impl_deserialize_tuple { + () => {}; + ( $($name:ident,)+ ) => { + peel!($($name,)*) + + impl< + D: Deserializer, + E, + $($name: Deserialize),+ + > Deserialize for ($($name,)+) { + #[inline] + #[allow(non_snake_case)] + fn deserialize(d: &mut D) -> Result<($($name,)+), E> { + struct Visitor; + + impl< + D: Deserializer, + E, + $($name: Deserialize,)+ + > self::Visitor for Visitor { + fn visit_seq< + V: SeqVisitor, + >(&mut self, d: &mut D, mut visitor: V) -> Result<($($name,)+), E> { + $( + let $name = match try!(visitor.visit(d)) { + Some(value) => value, + None => { return Err(d.end_of_stream_error()); } + }; + )+; + + try!(visitor.end(d)); + + Ok(($($name,)+)) + } + } + + d.visit(&mut Visitor) + } + } + } +} + +impl_deserialize_tuple! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, } + /////////////////////////////////////////////////////////////////////////////// impl< diff --git a/serde2/src/json/de.rs b/serde2/src/json/de.rs new file mode 100644 index 00000000..ba462e03 --- /dev/null +++ b/serde2/src/json/de.rs @@ -0,0 +1,815 @@ +use std::char; +use std::fmt; +use std::num; +use std::str::ScalarValue; +use std::str; + +use de; +use de::Deserializer; + +#[deriving(Clone, PartialEq, Eq)] +pub enum ErrorCode { + EOFWhileParsingList, + EOFWhileParsingObject, + EOFWhileParsingString, + EOFWhileParsingValue, + ExpectedColon, + InvalidEscape, + InvalidNumber, + InvalidSyntax(SyntaxExpectation), + InvalidUnicodeCodePoint, + KeyMustBeAString, + LoneLeadingSurrogateInHexEscape, + MissingField(&'static str), + NotFourDigit, + NotUtf8, + TrailingCharacters, + UnexpectedEndOfHexEscape, + UnknownVariant, + UnrecognizedHex, +} + +/// The failed expectation of InvalidSyntax +#[deriving(Clone, PartialEq, Eq, Show)] +pub enum SyntaxExpectation { + ListCommaOrEnd, + ObjectCommaOrEnd, + SomeValue, + SomeIdent, + EnumMapStart, + EnumVariantString, + EnumToken, + EnumEndToken, + EnumEnd, +} + +impl fmt::Show for ErrorCode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + EOFWhileParsingList => "EOF While parsing list".fmt(f), + EOFWhileParsingObject => "EOF While parsing object".fmt(f), + EOFWhileParsingString => "EOF While parsing string".fmt(f), + EOFWhileParsingValue => "EOF While parsing value".fmt(f), + ExpectedColon => "expected `:`".fmt(f), + InvalidEscape => "invalid escape".fmt(f), + InvalidNumber => "invalid number".fmt(f), + InvalidSyntax(expect) => { + write!(f, "invalid syntax, expected: {}", expect) + } + InvalidUnicodeCodePoint => "invalid unicode code point".fmt(f), + KeyMustBeAString => "key must be a string".fmt(f), + LoneLeadingSurrogateInHexEscape => "lone leading surrogate in hex escape".fmt(f), + MissingField(field) => { + write!(f, "missing field \"{}\"", field) + } + NotFourDigit => "invalid \\u escape (not four digits)".fmt(f), + NotUtf8 => "contents not utf-8".fmt(f), + TrailingCharacters => "trailing characters".fmt(f), + UnexpectedEndOfHexEscape => "unexpected end of hex escape".fmt(f), + UnknownVariant => "unknown variant".fmt(f), + UnrecognizedHex => "invalid \\u escape (unrecognized hex)".fmt(f), + } + } +} + +#[deriving(PartialEq, Eq, Show)] +pub enum Error { + SyntaxError(ErrorCode, uint, uint), +} + +pub struct Parser { + rdr: Iter, + ch: Option, + line: uint, + col: uint, +} + +impl< + Iter: Iterator, +> Parser { + /// Creates the JSON parser. + pub fn new(rdr: Iter) -> Parser { + let mut p = Parser { + rdr: rdr, + ch: Some('\x00'), + line: 1, + col: 0, + }; + p.bump(); + return p; + } + + pub fn end(&mut self) -> Result<(), Error> { + if self.eof() { + Ok(()) + } else { + Err(self.error(TrailingCharacters)) + } + } + + fn eof(&self) -> bool { self.ch.is_none() } + + fn ch_or_null(&self) -> char { self.ch.unwrap_or('\x00') } + + fn bump(&mut self) { + self.ch = self.rdr.next(); + + if self.ch_is('\n') { + self.line += 1; + self.col = 1; + } else { + self.col += 1; + } + } + + fn next_char(&mut self) -> Option { + self.bump(); + self.ch + } + + fn ch_is(&self, c: char) -> bool { + self.ch == Some(c) + } + + fn parse_whitespace(&mut self) { + while self.ch_is(' ') || + self.ch_is('\n') || + self.ch_is('\t') || + self.ch_is('\r') { self.bump(); } + } + + fn error(&mut self, reason: ErrorCode) -> Error { + //self.state_stack.clear(); + SyntaxError(reason, self.line, self.col) + } + + fn parse_value< + R, + V: de::Visitor, R, Error>, + >(&mut self, visitor: &mut V) -> Result { + self.parse_whitespace(); + + if self.eof() { + return Err(self.error(EOFWhileParsingValue)); + } + + match self.ch_or_null() { + 'n' => { + try!(self.parse_ident("ull")); + visitor.visit_null(self) + } + 't' => { + try!(self.parse_ident("rue")); + visitor.visit_bool(self, true) + } + 'f' => { + try!(self.parse_ident("alse")); + visitor.visit_bool(self, false) + } + '0' ... '9' | '-' => self.parse_number(visitor), + '"' => { + let s = try!(self.parse_string()); + visitor.visit_string(self, s) + } + '[' => { + self.bump(); + visitor.visit_seq(self, SeqVisitor { first: true }) + } + '{' => { + self.bump(); + visitor.visit_map(self, MapVisitor { first: true }) + } + _ => { + Err(self.error(InvalidSyntax(SomeValue))) + } + } + } + + fn parse_ident(&mut self, ident: &str) -> Result<(), Error> { + if ident.chars().all(|c| Some(c) == self.next_char()) { + self.bump(); + Ok(()) + } else { + Err(self.error(InvalidSyntax(SomeIdent))) + } + } + + fn parse_number< + R, + V: de::Visitor, R, Error>, + >(&mut self, visitor: &mut V) -> Result { + let mut neg = 1; + + if self.ch_is('-') { + self.bump(); + neg = -1; + } + + let res = try!(self.parse_integer()); + + if self.ch_is('.') || self.ch_is('e') || self.ch_is('E') { + let neg = neg as f64; + let mut res = res as f64; + + if self.ch_is('.') { + res = try!(self.parse_decimal(res)); + } + + if self.ch_is('e') || self.ch_is('E') { + res = try!(self.parse_exponent(res)); + } + + visitor.visit_f64(self, neg * res) + } else { + visitor.visit_i64(self, neg * res) + } + } + + fn parse_integer(&mut self) -> Result { + let mut res = 0; + + match self.ch_or_null() { + '0' => { + self.bump(); + + // There can be only one leading '0'. + match self.ch_or_null() { + '0' ... '9' => { + return Err(self.error(InvalidNumber)); + } + _ => () + } + }, + '1' ... '9' => { + while !self.eof() { + match self.ch_or_null() { + c @ '0' ... '9' => { + res *= 10; + res += (c as i64) - ('0' as i64); + self.bump(); + } + _ => break, + } + } + } + _ => { + return Err(self.error(InvalidNumber)); + } + } + + Ok(res) + } + + fn parse_decimal(&mut self, res: f64) -> Result { + self.bump(); + + // Make sure a digit follows the decimal place. + match self.ch_or_null() { + '0' ... '9' => (), + _ => { + return Err(self.error(InvalidNumber)); + } + } + + let mut res = res; + let mut dec = 1.0; + while !self.eof() { + match self.ch_or_null() { + c @ '0' ... '9' => { + dec /= 10.0; + res += (((c as int) - ('0' as int)) as f64) * dec; + self.bump(); + } + _ => break, + } + } + + Ok(res) + } + + fn parse_exponent(&mut self, mut res: f64) -> Result { + self.bump(); + + let mut exp = 0u; + let mut neg_exp = false; + + if self.ch_is('+') { + self.bump(); + } else if self.ch_is('-') { + self.bump(); + neg_exp = true; + } + + // Make sure a digit follows the exponent place. + match self.ch_or_null() { + '0' ... '9' => (), + _ => { + return Err(self.error(InvalidNumber)); + } + } + while !self.eof() { + match self.ch_or_null() { + c @ '0' ... '9' => { + exp *= 10; + exp += (c as uint) - ('0' as uint); + + self.bump(); + } + _ => break + } + } + + let exp: f64 = num::pow(10u as f64, exp); + if neg_exp { + res /= exp; + } else { + res *= exp; + } + + Ok(res) + } + + fn decode_hex_escape(&mut self) -> Result { + let mut i = 0u; + let mut n = 0u16; + while i < 4u && !self.eof() { + self.bump(); + n = match self.ch_or_null() { + c @ '0' ... '9' => n * 16_u16 + ((c as u16) - ('0' as u16)), + 'a' | 'A' => n * 16_u16 + 10_u16, + 'b' | 'B' => n * 16_u16 + 11_u16, + 'c' | 'C' => n * 16_u16 + 12_u16, + 'd' | 'D' => n * 16_u16 + 13_u16, + 'e' | 'E' => n * 16_u16 + 14_u16, + 'f' | 'F' => n * 16_u16 + 15_u16, + _ => { + return Err(self.error(InvalidEscape)); + } + }; + + i += 1u; + } + + // Error out if we didn't parse 4 digits. + if i != 4u { + return Err(self.error(InvalidEscape)); + } + + Ok(n) + } + + fn parse_string(&mut self) -> Result { + let mut escape = false; + let mut res = String::new(); + + loop { + self.bump(); + if self.eof() { + return Err(self.error(EOFWhileParsingString)); + } + + if escape { + match self.ch_or_null() { + '"' => res.push('"'), + '\\' => res.push('\\'), + '/' => res.push('/'), + 'b' => res.push('\x08'), + 'f' => res.push('\x0c'), + 'n' => res.push('\n'), + 'r' => res.push('\r'), + 't' => res.push('\t'), + 'u' => match try!(self.decode_hex_escape()) { + 0xDC00 ... 0xDFFF => { + return Err(self.error(LoneLeadingSurrogateInHexEscape)); + } + + // Non-BMP characters are encoded as a sequence of + // two hex escapes, representing UTF-16 surrogates. + n1 @ 0xD800 ... 0xDBFF => { + let c1 = self.next_char(); + let c2 = self.next_char(); + match (c1, c2) { + (Some('\\'), Some('u')) => (), + _ => { + return Err(self.error(UnexpectedEndOfHexEscape)); + } + } + + let buf = [n1, try!(self.decode_hex_escape())]; + match str::utf16_items(buf.as_slice()).next() { + Some(ScalarValue(c)) => res.push(c), + _ => { + return Err(self.error(LoneLeadingSurrogateInHexEscape)); + } + } + } + + n => match char::from_u32(n as u32) { + Some(c) => res.push(c), + None => { + return Err(self.error(InvalidUnicodeCodePoint)); + } + }, + }, + _ => { + return Err(self.error(InvalidEscape)); + } + } + escape = false; + } else if self.ch_is('\\') { + escape = true; + } else { + match self.ch { + Some('"') => { + self.bump(); + return Ok(res); + }, + Some(c) => res.push(c), + None => unreachable!() + } + } + } + } +} + +impl> Deserializer for Parser { + #[inline] + fn visit< + R, + V: de::Visitor, R, Error>, + >(&mut self, visitor: &mut V) -> Result { + self.parse_value(visitor) + } + + fn syntax_error(&mut self) -> Error { + SyntaxError(InvalidSyntax(SomeValue), self.line, self.col) + } + + fn end_of_stream_error(&mut self) -> Error { + SyntaxError(EOFWhileParsingValue, self.line, self.col) + } +} + +struct SeqVisitor { + first: bool, +} + +impl> de::SeqVisitor, Error> for SeqVisitor { + fn visit< + T: de::Deserialize, Error>, + >(&mut self, d: &mut Parser) -> Result, Error> { + d.parse_whitespace(); + + if d.ch_is(']') { + d.bump(); + return Ok(None); + } + + if self.first { + self.first = false; + } else { + if d.ch_is(',') { + d.bump(); + } else if d.eof() { + return Err(d.error(EOFWhileParsingList)); + } else { + return Err(d.error(InvalidSyntax(ListCommaOrEnd))); + } + } + + let value = try!(de::Deserialize::deserialize(d)); + Ok(Some(value)) + } + + fn end(&mut self, d: &mut Parser) -> Result<(), Error> { + if d.ch_is(']') { + d.bump(); + Ok(()) + } else if d.eof() { + Err(d.error(EOFWhileParsingList)) + } else { + Err(d.error(TrailingCharacters)) + } + } +} + +struct MapVisitor { + first: bool, +} + +impl> de::MapVisitor, Error> for MapVisitor { + fn visit< + K: de::Deserialize, Error>, + V: de::Deserialize, Error>, + >(&mut self, d: &mut Parser) -> Result, Error> { + d.parse_whitespace(); + + if d.ch_is('}') { + d.bump(); + return Ok(None); + } + + if self.first { + self.first = false; + } else { + if d.ch_is(',') { + d.bump(); + d.parse_whitespace(); + } else if d.eof() { + return Err(d.error(EOFWhileParsingObject)); + } else { + return Err(d.error(InvalidSyntax(ObjectCommaOrEnd))); + } + } + + if d.eof() { + return Err(d.error(EOFWhileParsingValue)); + } + + if !d.ch_is('"') { + return Err(d.error(KeyMustBeAString)); + } + + let key = try!(de::Deserialize::deserialize(d)); + + d.parse_whitespace(); + + if d.ch_is(':') { + d.bump(); + } else if d.eof() { + return Err(d.error(EOFWhileParsingObject)); + } else { + return Err(d.error(ExpectedColon)); + } + + d.parse_whitespace(); + + let value = try!(de::Deserialize::deserialize(d)); + + Ok(Some((key, value))) + } + + fn end(&mut self, d: &mut Parser) -> Result<(), Error> { + if d.ch_is(']') { + d.bump(); + Ok(()) + } else if d.eof() { + Err(d.error(EOFWhileParsingList)) + } else { + Err(d.error(TrailingCharacters)) + } + } +} + +/// Decodes a json value from an `Iterator`. +pub fn from_iter< + Iter: Iterator, + T: de::Deserialize, Error> +>(iter: Iter) -> Result { + let mut parser = Parser::new(iter); + let value = try!(de::Deserialize::deserialize(&mut parser)); + + // Make sure the whole stream has been consumed. + try!(parser.end()); + Ok(value) +} + +/// Decodes a json value from a string +pub fn from_str< + 'a, + T: de::Deserialize>, Error> +>(s: &'a str) -> Result { + from_iter(s.chars()) +} + + +#[cfg(test)] +mod tests { + use std::str; + use std::fmt::Show; + use std::collections::TreeMap; + + use de::Deserialize; + use super::{Parser, Error, from_str}; + use super::{ + ListCommaOrEnd, + ObjectCommaOrEnd, + SomeIdent, + SomeValue, + }; + use super::{ + EOFWhileParsingList, + EOFWhileParsingObject, + EOFWhileParsingString, + EOFWhileParsingValue, + ExpectedColon, + InvalidNumber, + InvalidSyntax, + KeyMustBeAString, + TrailingCharacters, + }; + use super::SyntaxError; + + macro_rules! treemap { + ($($k:expr => $v:expr),*) => ({ + let mut _m = TreeMap::new(); + $(_m.insert($k, $v);)* + _m + }) + } + + fn test_parse_ok< + 'a, + T: PartialEq + Show + Deserialize>, Error>, + >(errors: Vec<(&'a str, T)>) { + for (s, value) in errors.into_iter() { + let v: Result = from_str(s); + assert_eq!(v, Ok(value)); + + /* + let v: Json = from_iter(s.chars()).unwrap(); + assert_eq!(v, value.to_json()); + */ + } + } + + fn test_parse_err< + 'a, + T: PartialEq + Show + Deserialize>, Error> + >(errors: Vec<(&'a str, Error)>) { + for (s, err) in errors.into_iter() { + let v: Result = from_str(s); + assert_eq!(v, Err(err)); + } + } + + #[test] + fn test_parse_null() { + test_parse_ok(vec![ + ("null", ()), + ]); + } + + #[test] + fn test_parse_bool() { + test_parse_err::(vec![ + ("t", SyntaxError(InvalidSyntax(SomeIdent), 1, 2)), + ("truz", SyntaxError(InvalidSyntax(SomeIdent), 1, 4)), + ("f", SyntaxError(InvalidSyntax(SomeIdent), 1, 2)), + ("faz", SyntaxError(InvalidSyntax(SomeIdent), 1, 3)), + ("truea", SyntaxError(TrailingCharacters, 1, 5)), + ("falsea", SyntaxError(TrailingCharacters, 1, 6)), + ]); + + test_parse_ok(vec![ + ("true", true), + ("false", false), + ]); + } + + #[test] + fn test_parse_numbers() { + test_parse_err::(vec![ + ("+", SyntaxError(InvalidSyntax(SomeValue), 1, 1)), + (".", SyntaxError(InvalidSyntax(SomeValue), 1, 1)), + ("-", SyntaxError(InvalidNumber, 1, 2)), + ("00", SyntaxError(InvalidNumber, 1, 2)), + ("1.", SyntaxError(InvalidNumber, 1, 3)), + ("1e", SyntaxError(InvalidNumber, 1, 3)), + ("1e+", SyntaxError(InvalidNumber, 1, 4)), + ("1a", SyntaxError(TrailingCharacters, 1, 2)), + ]); + + test_parse_ok(vec![ + ("3", 3i64), + ("-2", -2), + ("-1234", -1234), + ]); + + test_parse_ok(vec![ + ("3.0", 3.0f64), + ("3.1", 3.1), + ("-1.2", -1.2), + ("0.4", 0.4), + ("0.4e5", 0.4e5), + ("0.4e15", 0.4e15), + ("0.4e-01", 0.4e-01), + ]); + } + + #[test] + fn test_parse_string() { + test_parse_err::(vec![ + ("\"", SyntaxError(EOFWhileParsingString, 1, 2)), + ("\"lol", SyntaxError(EOFWhileParsingString, 1, 5)), + ("\"lol\"a", SyntaxError(TrailingCharacters, 1, 6)), + ]); + + test_parse_ok(vec![ + ("\"\"", "".to_string()), + ("\"foo\"", "foo".to_string()), + ("\"\\\"\"", "\"".to_string()), + ("\"\\b\"", "\x08".to_string()), + ("\"\\n\"", "\n".to_string()), + ("\"\\r\"", "\r".to_string()), + ("\"\\t\"", "\t".to_string()), + ("\"\\u12ab\"", "\u12ab".to_string()), + ("\"\\uAB12\"", "\uAB12".to_string()), + ]); + } + + #[test] + fn test_parse_list() { + test_parse_err::>(vec![ + ("[", SyntaxError(EOFWhileParsingValue, 1, 2)), + ("[ ", SyntaxError(EOFWhileParsingValue, 1, 3)), + ("[1", SyntaxError(EOFWhileParsingList, 1, 3)), + ("[1,", SyntaxError(EOFWhileParsingValue, 1, 4)), + ("[1,]", SyntaxError(InvalidSyntax(SomeValue), 1, 4)), + ("[1 2]", SyntaxError(InvalidSyntax(ListCommaOrEnd), 1, 4)), + ("[]a", SyntaxError(TrailingCharacters, 1, 3)), + ]); + + test_parse_ok(vec![ + ("[]", vec![]), + ("[ ]", vec![]), + ("[null]", vec![()]), + ("[ null ]", vec![()]), + ]); + + test_parse_ok(vec![ + ("[true]", vec![true]), + ]); + + test_parse_ok(vec![ + ("[3,1]", vec![3i, 1]), + ("[ 3 , 1 ]", vec![3i, 1]), + ]); + + test_parse_ok(vec![ + ("[[3], [1, 2]]", vec![vec![3i], vec![1, 2]]), + ]); + + test_parse_ok(vec![ + ("[]", ()), + ]); + + test_parse_ok(vec![ + ("[1]", (1u,)), + ]); + + test_parse_ok(vec![ + ("[1, 2]", (1u, 2u)), + ]); + + test_parse_ok(vec![ + ("[1, 2, 3]", (1u, 2u, 3u)), + ]); + } + + #[test] + fn test_parse_object() { + test_parse_err::>(vec![ + ("{", SyntaxError(EOFWhileParsingValue, 1, 2)), + ("{ ", SyntaxError(EOFWhileParsingValue, 1, 3)), + ("{1", SyntaxError(KeyMustBeAString, 1, 2)), + ("{ \"a\"", SyntaxError(EOFWhileParsingObject, 1, 6)), + ("{\"a\"", SyntaxError(EOFWhileParsingObject, 1, 5)), + ("{\"a\" ", SyntaxError(EOFWhileParsingObject, 1, 6)), + ("{\"a\" 1", SyntaxError(ExpectedColon, 1, 6)), + ("{\"a\":", SyntaxError(EOFWhileParsingValue, 1, 6)), + ("{\"a\":1", SyntaxError(EOFWhileParsingObject, 1, 7)), + ("{\"a\":1 1", SyntaxError(InvalidSyntax(ObjectCommaOrEnd), 1, 8)), + ("{\"a\":1,", SyntaxError(EOFWhileParsingValue, 1, 8)), + ("{}a", SyntaxError(TrailingCharacters, 1, 3)), + ]); + + test_parse_ok(vec![ + ("{}", treemap!()), + ("{ }", treemap!()), + ( + "{\"a\":3}", + treemap!("a".to_string() => 3i) + ), + ( + "{ \"a\" : 3 }", + treemap!("a".to_string() => 3i) + ), + ( + "{\"a\":3,\"b\":4}", + treemap!("a".to_string() => 3i, "b".to_string() => 4) + ), + ( + "{ \"a\" : 3 , \"b\" : 4 }", + treemap!("a".to_string() => 3i, "b".to_string() => 4), + ), + ]); + + test_parse_ok(vec![ + ( + "{\"a\": {\"b\": 3, \"c\": 4}}", + treemap!("a".to_string() => treemap!("b".to_string() => 3i, "c".to_string() => 4i)), + ), + ]); + } +} diff --git a/serde2/src/lib.rs b/serde2/src/lib.rs index 91df934f..ca1e7a14 100644 --- a/serde2/src/lib.rs +++ b/serde2/src/lib.rs @@ -6,4 +6,4 @@ pub mod ser; pub mod de; -//pub mod json; +pub mod json;