json5-0.4.1/.cargo_vcs_info.json0000644000000001120000000000000120650ustar { "git": { "sha1": "5066f2e0a2726ceaeeb0a7786804787b3dc801f1" } } json5-0.4.1/.gitignore000064400000000000000000000006530000000000000126350ustar 00000000000000 # Created by https://www.gitignore.io/api/rust ### Rust ### # Generated by Cargo # will have compiled files and executables /target/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk # End of https://www.gitignore.io/api/rust json5-0.4.1/Cargo.toml0000644000000021450000000000000100730ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "json5" version = "0.4.1" authors = ["Callum Oakley "] description = "A Rust JSON5 serializer and deserializer which speaks Serde." readme = "README.md" keywords = ["json5", "parse", "parser", "serde", "json"] license = "ISC" repository = "https://github.com/callum-oakley/json5-rs" [dependencies.pest] version = "2.0" [dependencies.pest_derive] version = "2.0" [dependencies.serde] version = "1.0" [dev-dependencies.matches] version = "0.1.8" [dev-dependencies.serde_derive] version = "1.0" [dev-dependencies.serde_json] version = "1.0" json5-0.4.1/Cargo.toml.orig000064400000000000000000000007420000000000000135330ustar 00000000000000[package] name = "json5" version = "0.4.1" authors = ["Callum Oakley "] description = "A Rust JSON5 serializer and deserializer which speaks Serde." license = "ISC" repository = "https://github.com/callum-oakley/json5-rs" readme = "README.md" keywords = ["json5", "parse", "parser", "serde", "json"] edition = "2018" [dependencies] pest = "2.0" pest_derive = "2.0" serde = "1.0" [dev-dependencies] matches = "0.1.8" serde_derive = "1.0" serde_json = "1.0" json5-0.4.1/LICENCE000064400000000000000000000013270000000000000116310ustar 00000000000000Copyright 2018 Callum Oakley Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. json5-0.4.1/README.md000064400000000000000000000025110000000000000121170ustar 00000000000000# JSON5 [![crates.io](https://img.shields.io/crates/v/json5.svg)](https://crates.io/crates/json5) [![docs.rs](https://docs.rs/json5/badge.svg)](https://docs.rs/json5) A Rust [JSON5] serializer and deserializer which speaks [Serde]. ## API Deserialize a JSON5 string with `from_str`. Go the other way with `to_string`. The serializer is very basic at the moment, it just produces plain old JSON. See the [Serde documentation] for details on implementing `Serialize` and `Deserialize`. (Usually it's just a case of sprinkling in some derives.) The [Serde data model] is mostly supported, with the exception of bytes and borrowed strings. ## Example Read some config into a struct. ```rust use json5; use serde_derive::Deserialize; #[derive(Deserialize, Debug, PartialEq)] struct Config { message: String, n: i32, } fn main() { let config = " { // A traditional message. message: 'hello world', // A number for some reason. n: 42, } "; assert_eq!( json5::from_str(config), Ok(Config { message: "hello world".to_string(), n: 42, }), ); } ``` [JSON5]: https://json5.org/ [Serde]: https://serde.rs/ [Serde documentation]: https://serde.rs/custom-serialization.html [Serde data model]: https://serde.rs/data-model.html json5-0.4.1/src/de.rs000064400000000000000000000407620000000000000123770ustar 00000000000000use pest::iterators::Pair; use pest::Parser as P; use pest_derive::Parser; use serde::de; use serde::forward_to_deserialize_any; use std::char; use std::collections::VecDeque; use std::f64; use crate::error::{self, Error, Result}; #[derive(Parser)] #[grammar = "json5.pest"] struct Parser; /// Deserialize an instance of type `T` from a string of JSON5 text. Can fail if the input is /// invalid JSON5, or doesn’t match the structure of the target type. pub fn from_str<'a, T>(s: &'a str) -> Result where T: de::Deserialize<'a>, { let mut deserializer = Deserializer::from_str(s)?; T::deserialize(&mut deserializer) } pub struct Deserializer<'de> { pair: Option>, } impl<'de> Deserializer<'de> { /// Creates a JSON5 deserializer from a `&str`. This parses the input at construction time, so /// can fail if the input is not valid JSON5. pub fn from_str(input: &'de str) -> Result { let pair = Parser::parse(Rule::text, input)?.next().unwrap(); Ok(Deserializer::from_pair(pair)) } fn from_pair(pair: Pair<'de, Rule>) -> Self { Deserializer { pair: Some(pair) } } } impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { type Error = Error; fn deserialize_any(self, visitor: V) -> Result where V: de::Visitor<'de>, { let pair = self.pair.take().unwrap(); let span = pair.as_span(); let mut res = (move || match pair.as_rule() { Rule::null => visitor.visit_unit(), Rule::boolean => visitor.visit_bool(parse_bool(&pair)), Rule::string | Rule::identifier => visitor.visit_string(parse_string(pair)?), Rule::number => { if is_int(pair.as_str()) { visitor.visit_i64(parse_integer(&pair)?) } else { visitor.visit_f64(parse_number(&pair)?) } } Rule::array => visitor.visit_seq(Seq::new(pair)), Rule::object => visitor.visit_map(Map::new(pair)), _ => unreachable!(), })(); error::set_location(&mut res, &span); res } fn deserialize_enum( self, _name: &'static str, _variants: &'static [&'static str], visitor: V, ) -> Result where V: de::Visitor<'de>, { let pair = self.pair.take().unwrap(); let span = pair.as_span(); let mut res = (move || visitor.visit_enum(Enum { pair }))(); error::set_location(&mut res, &span); res } // The below will get us the right types, but won't necessarily give // meaningful results if the source is out of the range of the target type. fn deserialize_i8(self, visitor: V) -> Result where V: de::Visitor<'de>, { let pair = self.pair.take().unwrap(); let span = pair.as_span(); let mut res = (move || visitor.visit_i8(parse_number(&pair)? as i8))(); error::set_location(&mut res, &span); res } fn deserialize_i16(self, visitor: V) -> Result where V: de::Visitor<'de>, { let pair = self.pair.take().unwrap(); let span = pair.as_span(); let mut res = (move || visitor.visit_i16(parse_number(&pair)? as i16))(); error::set_location(&mut res, &span); res } fn deserialize_i32(self, visitor: V) -> Result where V: de::Visitor<'de>, { let pair = self.pair.take().unwrap(); let span = pair.as_span(); let mut res = (move || visitor.visit_i32(parse_number(&pair)? as i32))(); error::set_location(&mut res, &span); res } fn deserialize_i64(self, visitor: V) -> Result where V: de::Visitor<'de>, { let pair = self.pair.take().unwrap(); let span = pair.as_span(); let mut res = (move || visitor.visit_i64(parse_number(&pair)? as i64))(); error::set_location(&mut res, &span); res } fn deserialize_i128(self, visitor: V) -> Result where V: de::Visitor<'de>, { let pair = self.pair.take().unwrap(); let span = pair.as_span(); let mut res = (move || visitor.visit_i128(parse_number(&pair)? as i128))(); error::set_location(&mut res, &span); res } fn deserialize_u8(self, visitor: V) -> Result where V: de::Visitor<'de>, { let pair = self.pair.take().unwrap(); let span = pair.as_span(); let mut res = (move || visitor.visit_u8(parse_number(&pair)? as u8))(); error::set_location(&mut res, &span); res } fn deserialize_u16(self, visitor: V) -> Result where V: de::Visitor<'de>, { let pair = self.pair.take().unwrap(); let span = pair.as_span(); let mut res = (move || visitor.visit_u16(parse_number(&pair)? as u16))(); error::set_location(&mut res, &span); res } fn deserialize_u32(self, visitor: V) -> Result where V: de::Visitor<'de>, { let pair = self.pair.take().unwrap(); let span = pair.as_span(); let mut res = (move || visitor.visit_u32(parse_number(&pair)? as u32))(); error::set_location(&mut res, &span); res } fn deserialize_u64(self, visitor: V) -> Result where V: de::Visitor<'de>, { let pair = self.pair.take().unwrap(); let span = pair.as_span(); let mut res = (move || visitor.visit_u64(parse_number(&pair)? as u64))(); error::set_location(&mut res, &span); res } fn deserialize_u128(self, visitor: V) -> Result where V: de::Visitor<'de>, { let pair = self.pair.take().unwrap(); let span = pair.as_span(); let mut res = (move || visitor.visit_u128(parse_number(&pair)? as u128))(); error::set_location(&mut res, &span); res } fn deserialize_f32(self, visitor: V) -> Result where V: de::Visitor<'de>, { let pair = self.pair.take().unwrap(); let span = pair.as_span(); let mut res = (move || visitor.visit_f32(parse_number(&pair)? as f32))(); error::set_location(&mut res, &span); res } fn deserialize_f64(self, visitor: V) -> Result where V: de::Visitor<'de>, { let pair = self.pair.take().unwrap(); let span = pair.as_span(); let mut res = (move || visitor.visit_f64(parse_number(&pair)?))(); error::set_location(&mut res, &span); res } fn deserialize_option(self, visitor: V) -> Result where V: de::Visitor<'de>, { let pair = self.pair.take().unwrap(); let span = pair.as_span(); let mut res = (move || match pair.as_rule() { Rule::null => visitor.visit_none(), _ => visitor.visit_some(&mut Deserializer::from_pair(pair)), })(); error::set_location(&mut res, &span); res } fn deserialize_newtype_struct(self, _name: &str, visitor: V) -> Result where V: de::Visitor<'de>, { let span = self.pair.as_ref().unwrap().as_span(); let mut res = (move || visitor.visit_newtype_struct(self))(); error::set_location(&mut res, &span); res } forward_to_deserialize_any! { bool char str string bytes byte_buf unit unit_struct seq tuple tuple_struct map struct identifier ignored_any } } fn parse_bool(pair: &Pair<'_, Rule>) -> bool { match pair.as_str() { "true" => true, "false" => false, _ => unreachable!(), } } fn parse_string_component(pair: Pair<'_, Rule>) -> Result { let mut result = String::new(); let mut component_iter = pair.into_inner(); while let Some(component) = component_iter.next() { match component.as_rule() { Rule::char_literal => result.push_str(component.as_str()), Rule::char_escape_sequence => result.push_str(parse_char_escape_sequence(&component)), Rule::nul_escape_sequence => result.push_str("\u{0000}"), Rule::hex_escape_sequence => { let hex_escape = parse_hex(component.as_str())?; match char::from_u32(hex_escape) { Some(c) => result.push(c), None => return Err(de::Error::custom("error parsing hex prefix")), } } Rule::unicode_escape_sequence => { match parse_hex(component.as_str())? { 0xDC00..=0xDFFF => { // Expecting a low surrogate (trail surrogate) return Err(de::Error::custom("unexpected unicode trail surrogate")); } // Non-BMP characters are encoded as a sequence of to hex escapes, // representing UTF-16 surrogate rc1 @ 0xD800..=0xDBFF => { let rc2 = match component_iter.next() { Some(pc2) => match parse_hex(pc2.as_str())? { rc2 @ 0xDC00..=0xDFFF => rc2, _ => { return Err(de::Error::custom( "expecting unicode trail surrogate", )) } }, None => { // Missing a low surrogate (trail surrogate) return Err(de::Error::custom("missing unicode trail surrogate")); } }; // Join together let rc = ((rc1 - 0xD800) << 10) | (rc2 - 0xDC00) + 0x1_0000; match char::from_u32(rc) { Some(c) => { result.push(c); } None => { return Err(de::Error::custom("invalid non-BMP unicode sequence")); } } } rc => match char::from_u32(rc) { Some(c) => { result.push(c); } None => { return Err(de::Error::custom("invalid unicode character")); } }, } } _ => unreachable!(), } } Ok(result) } fn parse_string(pair: Pair<'_, Rule>) -> Result { let span = pair.as_span(); let mut res = parse_string_component(pair); error::set_location(&mut res, &span); res } fn parse_char_escape_sequence<'a>(pair: &'a Pair<'_, Rule>) -> &'a str { match pair.as_str() { "b" => "\u{0008}", "f" => "\u{000C}", "n" => "\n", "r" => "\r", "t" => "\t", "v" => "\u{000B}", c => c, } } fn parse_number(pair: &Pair<'_, Rule>) -> Result { match pair.as_str() { "Infinity" => Ok(f64::INFINITY), "-Infinity" => Ok(f64::NEG_INFINITY), "NaN" | "-NaN" => Ok(f64::NAN), s if is_hex_literal(s) => parse_hex(&s[2..]).map(f64::from), s => { if let Ok(r) = s.parse::() { if r.is_finite() { Ok(r) } else { Err(de::Error::custom("error parsing number: too large")) } } else { Err(de::Error::custom("error parsing number")) } } } } fn parse_integer(pair: &Pair<'_, Rule>) -> Result { match pair.as_str() { s if is_hex_literal(s) => Ok(parse_hex(&s[2..])? as i64), s => s .parse() .or_else(|_| Err(de::Error::custom("error parsing integer"))), } } fn is_int(s: &str) -> bool { !s.contains('.') && (is_hex_literal(s) || (!s.contains('e') && !s.contains('E'))) && !is_infinite(s) && !is_nan(s) } fn parse_hex(s: &str) -> Result { u32::from_str_radix(s, 16).or_else(|_| Err(de::Error::custom("error parsing hex"))) } fn is_hex_literal(s: &str) -> bool { s.len() > 2 && (&s[..2] == "0x" || &s[..2] == "0X") } fn is_infinite(s: &str) -> bool { s == "Infinity" || s == "-Infinity" } fn is_nan(s: &str) -> bool { s == "NaN" || s == "-NaN" } struct Seq<'de> { pairs: VecDeque>, } impl<'de> Seq<'de> { pub fn new(pair: Pair<'de, Rule>) -> Self { Self { pairs: pair.into_inner().collect(), } } } impl<'de> de::SeqAccess<'de> for Seq<'de> { type Error = Error; fn size_hint(&self) -> Option { Some(self.pairs.len()) } fn next_element_seed(&mut self, seed: T) -> Result> where T: de::DeserializeSeed<'de>, { if let Some(pair) = self.pairs.pop_front() { seed.deserialize(&mut Deserializer::from_pair(pair)) .map(Some) } else { Ok(None) } } } struct Map<'de> { pairs: VecDeque>, } impl<'de> Map<'de> { pub fn new(pair: Pair<'de, Rule>) -> Self { Self { pairs: pair.into_inner().collect(), } } } impl<'de> de::MapAccess<'de> for Map<'de> { type Error = Error; fn size_hint(&self) -> Option { Some(self.pairs.len() / 2) } fn next_key_seed(&mut self, seed: K) -> Result> where K: de::DeserializeSeed<'de>, { if let Some(pair) = self.pairs.pop_front() { seed.deserialize(&mut Deserializer::from_pair(pair)) .map(Some) } else { Ok(None) } } fn next_value_seed(&mut self, seed: V) -> Result where V: de::DeserializeSeed<'de>, { seed.deserialize(&mut Deserializer::from_pair( self.pairs.pop_front().unwrap(), )) } } struct Enum<'de> { pair: Pair<'de, Rule>, } impl<'de> de::EnumAccess<'de> for Enum<'de> { type Error = Error; type Variant = Variant<'de>; fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant)> where V: de::DeserializeSeed<'de>, { let span = self.pair.as_span(); let mut res = (move || match self.pair.as_rule() { Rule::string => { let tag = seed.deserialize(&mut Deserializer::from_pair(self.pair))?; Ok((tag, Variant { pair: None })) } Rule::object => { let mut pairs = self.pair.into_inner(); if let Some(tag_pair) = pairs.next() { let tag = seed.deserialize(&mut Deserializer::from_pair(tag_pair))?; Ok((tag, Variant { pair: pairs.next() })) } else { Err(de::Error::custom("expected a nonempty object")) } } _ => Err(de::Error::custom("expected a string or an object")), })(); error::set_location(&mut res, &span); res } } struct Variant<'de> { pair: Option>, } impl<'de, 'a> de::VariantAccess<'de> for Variant<'de> { type Error = Error; fn unit_variant(self) -> Result<()> { Ok(()) } fn newtype_variant_seed(self, seed: T) -> Result where T: de::DeserializeSeed<'de>, { seed.deserialize(&mut Deserializer::from_pair(self.pair.unwrap())) } fn tuple_variant(self, _len: usize, visitor: V) -> Result where V: de::Visitor<'de>, { match self.pair { Some(pair) => match pair.as_rule() { Rule::array => visitor.visit_seq(Seq::new(pair)), _ => Err(de::Error::custom("expected an array")), }, None => Err(de::Error::custom("expected an array")), } } fn struct_variant(self, _fields: &'static [&'static str], visitor: V) -> Result where V: de::Visitor<'de>, { match self.pair { Some(pair) => match pair.as_rule() { Rule::object => visitor.visit_map(Map::new(pair)), _ => Err(de::Error::custom("expected an object")), }, None => Err(de::Error::custom("expected an object")), } } } json5-0.4.1/src/error.rs000064400000000000000000000047530000000000000131400ustar 00000000000000use pest::Span; use serde::{de, ser}; use std::fmt::{self, Display}; use crate::de::Rule; /// Alias for a `Result` with error type `json5::Error` pub type Result = std::result::Result; /// One-based line and column at which the error was detected. #[derive(Clone, Debug, PartialEq)] pub struct Location { /// The one-based line number of the error. pub line: usize, /// The one-based column number of the error. pub column: usize, } impl From<&Span<'_>> for Location { fn from(s: &Span<'_>) -> Self { let (line, column) = s.start_pos().line_col(); Self { line, column } } } /// A bare bones error type which currently just collapses all the underlying errors in to a single /// string... This is fine for displaying to the user, but not very useful otherwise. Work to be /// done here. #[derive(Clone, Debug, PartialEq)] pub enum Error { /// Just shove everything in a single variant for now. Message { /// The error message. msg: String, /// The location of the error, if applicable. location: Option, }, } impl From> for Error { fn from(err: pest::error::Error) -> Self { let (line, column) = match err.line_col { pest::error::LineColLocation::Pos((l, c)) => (l, c), pest::error::LineColLocation::Span((l, c), (_, _)) => (l, c), }; Error::Message { msg: err.to_string(), location: Some(Location { line, column }), } } } impl ser::Error for Error { fn custom(msg: T) -> Self { Error::Message { msg: msg.to_string(), location: None, } } } impl de::Error for Error { fn custom(msg: T) -> Self { Error::Message { msg: msg.to_string(), location: None, } } } impl Display for Error { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Error::Message { ref msg, .. } => write!(formatter, "{}", msg), } } } impl std::error::Error for Error {} /// Adds location information from `span`, if `res` is an error. pub fn set_location(res: &mut Result, span: &Span<'_>) { if let Err(ref mut e) = res { let Error::Message { location, .. } = e; if location.is_none() { let (line, column) = span.start_pos().line_col(); *location = Some(Location { line, column }); } } } json5-0.4.1/src/json5.pest000064400000000000000000000053450000000000000133720ustar 00000000000000// see https://spec.json5.org/#syntactic-grammar and // https://spec.json5.org/#lexical-grammar COMMENT = _{ "/*" ~ (!"*/" ~ ANY)* ~ "*/" | "//" ~ (!line_terminator ~ ANY)* } WHITESPACE = _{ "\u{0009}" | "\u{000B}" | "\u{000C}" | "\u{0020}" | "\u{00A0}" | "\u{FEFF}" | SPACE_SEPARATOR | line_terminator } array = { "[" ~ "]" | "[" ~ value ~ ("," ~ value)* ~ ","? ~ "]" } boolean = @{ "true" | "false" } char_escape_sequence = @{ single_escape_char | non_escape_char } char_literal = @{ !("\\" | line_terminator) ~ ANY } decimal_integer_literal = _{ "0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* } decimal_literal = _{ decimal_integer_literal ~ "." ~ ASCII_DIGIT* ~ exponent_part? | "." ~ ASCII_DIGIT+~ exponent_part? | decimal_integer_literal ~ exponent_part? } double_quote_char = _{ "\\" ~ escape_sequence | line_continuation | !"\"" ~ char_literal } escape_char = _{ single_escape_char | ASCII_DIGIT | "x" | "u" } escape_sequence = _{ char_escape_sequence | nul_escape_sequence | "x" ~ hex_escape_sequence | "u" ~ unicode_escape_sequence } exponent_part = _{ ^"e" ~ ("+" | "-")? ~ ASCII_DIGIT+ } hex_escape_sequence = @{ ASCII_HEX_DIGIT{2} } hex_integer_literal = _{ ^"0x" ~ ASCII_HEX_DIGIT+ } identifier = ${ identifier_start ~ identifier_part* } identifier_part = _{ identifier_start | &( NONSPACING_MARK | DIACRITIC | // not sure about this, spec says "Combining spacing mark (Mc)" DECIMAL_NUMBER | CONNECTOR_PUNCTUATION | "\u{200C}" | "\u{200D}" ) ~ char_literal } identifier_start = _{ &(unicode_letter | "$" | "_") ~ char_literal | "\\u" ~ unicode_escape_sequence } key = _{ identifier | string } line_continuation = _{ "\\" ~ line_terminator_sequence } line_terminator = _{ "\u{000A}" | "\u{000D}" | "\u{2028}" | "\u{2029}" } line_terminator_sequence = _{ "\u{000D}" ~ "\u{000A}" | line_terminator } non_escape_char = _{ !(escape_char | line_terminator) ~ ANY } nul_escape_sequence = @{ "0" } null = @{ "null" } number = @{ ("+" | "-")? ~ numeric_literal } numeric_literal = _{ hex_integer_literal | decimal_literal | "Infinity" | "NaN" } object = { "{" ~ "}" | "{" ~ pair ~ ("," ~ pair)* ~ ","? ~ "}" } pair = _{ key ~ ":" ~ value } single_escape_char = _{ "'" | "\"" | "\\" | "b" | "f" | "n" | "r" | "t" | "v" } single_quote_char = _{ "\\" ~ escape_sequence | line_continuation | !"'" ~ char_literal } string = ${ "\"" ~ double_quote_char* ~ "\"" | "'" ~ single_quote_char* ~ "'" } text = _{ SOI ~ value ~ EOI } unicode_escape_sequence = @{ ASCII_HEX_DIGIT{4} } unicode_letter = _{ UPPERCASE_LETTER | LOWERCASE_LETTER | TITLECASE_LETTER | MODIFIER_LETTER | OTHER_LETTER | LETTER_NUMBER } value = _{ null | boolean | string | number | object | array } json5-0.4.1/src/lib.rs000064400000000000000000000131020000000000000125410ustar 00000000000000//! JSON5 is a superset of [JSON][] with an expanded syntax including some productions from //! [ECMAScript 5.1][]. //! //! In particular, JSON5 allows comments, trailing commas, object keys without quotes, single //! quoted strings and more. See the [JSON5 project page][] for full details. //! //! ```json5,ignore //! { //! // comments //! unquoted: 'and you can quote me on that', //! singleQuotes: 'I can use "double quotes" here', //! lineBreaks: "Look, Mom! \ //! No \\n's!", //! hexadecimal: 0xdecaf, //! leadingDecimalPoint: .8675309, andTrailing: 8675309., //! positiveSign: +1, //! trailingComma: 'in objects', andIn: ['arrays',], //! "backwardsCompatible": "with JSON", //! } //! ``` //! //! This crate provides functions for deserializing JSON5 text into a Rust datatype and for //! serializing a Rust datatype as JSON5 text, both via the [Serde framework][]. //! //! # Deserialization //! //! Implementing Serde’s [`Deserialize`][] trait on your type will allow you to parse JSON5 //! text into a value of that type with [`from_str`][]. //! //! ```rust //! use serde_derive::Deserialize; //! //! #[derive(Deserialize, Debug, PartialEq)] //! struct Config { //! message: String, //! n: i32, //! } //! //! let config = " //! { //! // A traditional message. //! message: 'hello world', //! //! // A number for some reason. //! n: 42, //! } //! "; //! //! assert_eq!( //! json5::from_str(config), //! Ok(Config { //! message: "hello world".to_string(), //! n: 42, //! }), //! ); //! ``` //! //! Also, you could deserialize into serde_json::Value //! //! ```rust //! use json5; //! use serde_json::{Value, json}; //! //! let config = " //! { //! // A traditional message. //! message: 'hello world', //! //! // A number for some reason. //! n: 42, //! } //! "; //! //! assert_eq!( //! json5::from_str::(&config), //! Ok(json!({ //! "message": "hello world", //! "n": 42 //! })) //! ); //! ``` //! //! There are many ways to customize the deserialization (e.g. deserializing `camelCase` field //! names into a struct with `snake_case` fields). See the Serde docs, especially the //! [Attributes][], [Custom serialization][] and [Examples][] sections. //! //! # Serialization //! //! Similarly, implementing [`Serialize`][] on a Rust type allows you to produce a JSON5 //! serialization of values of that type with [`to_string`][]. At present the serializer will just //! produce JSON (since it's a valid subset of JSON5), but future work will allow specifying the //! output style (single over double quotes, trailing commas, indentation etc.). //! //! ```rust //! use serde_derive::Serialize; //! use std::collections::HashMap; //! //! #[derive(Serialize, Debug)] //! #[serde(untagged)] //! enum Val { //! Null, //! Bool(bool), //! Number(f64), //! String(String), //! Array(Vec), //! Object(HashMap), //! } //! let mut map = HashMap::new(); //! map.insert( //! "a".to_owned(), //! Val::Array(vec![ //! Val::Null, //! Val::Bool(true), //! Val::Number(42.), //! Val::Number(42.42), //! Val::Number(f64::NAN), //! Val::String("hello".to_owned()), //! ]) //! ); //! assert_eq!( //! json5::to_string(&Val::Object(map)), //! Ok("{\"a\":[null,true,42,42.42,NaN,\"hello\"]}".to_owned()), //! ) //! ``` //! //! You could also build from serde_json //! //! ```rust //! use serde_json::{json, Value, Map, Number}; //! assert_eq!( //! json5::to_string( //! &json!({"a": [null, true, 42, 42.42, f64::NAN, "hello"]}) //! ), //! Ok("{\"a\":[null,true,42,42.42,null,\"hello\"]}".to_owned()) //! ); //! let mut map = Map::new(); //! map.insert( //! "a".to_owned(), //! Value::Array(vec![ //! Value::Null, //! Value::Bool(true), //! Value::Number(Number::from_f64(42.).unwrap()), //! Value::Number(Number::from_f64(42.42).unwrap()), //! Value::String("hello".to_owned()), //! ]) //! ); //! assert_eq!( //! json5::to_string(&Value::Object(map)), //! Ok("{\"a\":[null,true,42,42.42,\"hello\"]}".to_owned()), //! ) //! ``` //! //! There are many ways to customize the serialization (e.g. serializing `snake_case` struct fields //! as `camelCase`). See the Serde docs, especially the [Attributes][], [Custom serialization][] //! and [Examples][] sections. //! //! # Limitations //! //! At the time of writing the following is unsupported: //! //! - deserializing into borrowed types (e.g. fields of type `&str`) //! //! - serializing or deserializing [byte arrays][] //! //! - specifying the style of JSON5 output from the serializer (single over double quotes, trailing //! commas, indentation etc.) //! //! [JSON]: https://tools.ietf.org/html/rfc7159 //! [ECMAScript 5.1]: https://www.ecma-international.org/ecma-262/5.1/ //! [JSON5 project page]: https://json5.org/ //! [Serde framework]: https://serde.rs/ //! [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html //! [`from_str`]: fn.from_str.html //! [Attributes]: https://serde.rs/attributes.html //! [Custom serialization]: https://serde.rs/custom-serialization.html //! [Examples]: https://serde.rs/examples.html //! [`Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html //! [`to_string`]: fn.to_string.html //! [byte arrays]: https://serde.rs/data-model.html#types #![warn(missing_docs)] #![warn(rust_2018_idioms)] mod de; mod error; mod ser; pub use crate::de::{from_str, Deserializer}; pub use crate::error::{Error, Location, Result}; pub use crate::ser::to_string; json5-0.4.1/src/ser.rs000064400000000000000000000212270000000000000125730ustar 00000000000000use serde::ser::{self, Serialize}; use std::{f32, f64}; use crate::error::{Error, Result}; /// Attempts to serialize the input as a JSON5 string (actually a JSON string). pub fn to_string(value: &T) -> Result where T: Serialize, { let mut serializer = Serializer { output: String::new(), }; value.serialize(&mut serializer)?; Ok(serializer.output) } struct Serializer { output: String, // TODO settings for formatting (single vs double quotes, whitespace etc) } impl Serializer { fn call_to_string(&mut self, v: &T) -> Result<()> where T: ToString, { self.output += &v.to_string(); Ok(()) } } impl<'a> ser::Serializer for &'a mut Serializer { type Ok = (); type Error = Error; type SerializeSeq = Self; type SerializeTuple = Self; type SerializeTupleStruct = Self; type SerializeTupleVariant = Self; type SerializeMap = Self; type SerializeStruct = Self; type SerializeStructVariant = Self; fn serialize_bool(self, v: bool) -> Result<()> { self.call_to_string(&v) } fn serialize_i8(self, v: i8) -> Result<()> { self.call_to_string(&v) } fn serialize_i16(self, v: i16) -> Result<()> { self.call_to_string(&v) } fn serialize_i32(self, v: i32) -> Result<()> { self.call_to_string(&v) } fn serialize_i64(self, v: i64) -> Result<()> { self.call_to_string(&v) } fn serialize_u8(self, v: u8) -> Result<()> { self.call_to_string(&v) } fn serialize_u16(self, v: u16) -> Result<()> { self.call_to_string(&v) } fn serialize_u32(self, v: u32) -> Result<()> { self.call_to_string(&v) } fn serialize_u64(self, v: u64) -> Result<()> { self.call_to_string(&v) } fn serialize_f32(self, v: f32) -> Result<()> { if v == f32::INFINITY { self.output += "Infinity"; } else if v == f32::NEG_INFINITY { self.output += "-Infinity"; } else if v.is_nan() { self.output += "NaN"; } else { self.call_to_string(&v)?; } Ok(()) } fn serialize_f64(self, v: f64) -> Result<()> { if v == f64::INFINITY { self.output += "Infinity"; } else if v == f64::NEG_INFINITY { self.output += "-Infinity"; } else if v.is_nan() { self.output += "NaN"; } else { self.call_to_string(&v)?; } Ok(()) } fn serialize_char(self, v: char) -> Result<()> { self.serialize_str(&v.to_string()) } fn serialize_str(self, v: &str) -> Result<()> { self.output += "\""; self.output += &escape(v); self.output += "\""; Ok(()) } fn serialize_bytes(self, _v: &[u8]) -> Result<()> { unimplemented!() // TODO } fn serialize_none(self) -> Result<()> { self.serialize_unit() } fn serialize_some(self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(self) } fn serialize_unit(self) -> Result<()> { self.output += "null"; Ok(()) } fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { self.serialize_unit() } fn serialize_unit_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, ) -> Result<()> { self.serialize_str(variant) } fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(self) } fn serialize_newtype_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, value: &T, ) -> Result<()> where T: ?Sized + Serialize, { self.output += "{"; variant.serialize(&mut *self)?; // TODO drop the quotes where possible self.output += ":"; value.serialize(&mut *self)?; self.output += "}"; Ok(()) } fn serialize_seq(self, _len: Option) -> Result { self.output += "["; Ok(self) } fn serialize_tuple(self, len: usize) -> Result { self.serialize_seq(Some(len)) } fn serialize_tuple_struct( self, _name: &'static str, len: usize, ) -> Result { self.serialize_seq(Some(len)) } fn serialize_tuple_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, _len: usize, ) -> Result { self.output += "{"; variant.serialize(&mut *self)?; self.output += ":["; Ok(self) } fn serialize_map(self, _len: Option) -> Result { self.output += "{"; Ok(self) } fn serialize_struct(self, _name: &'static str, len: usize) -> Result { self.serialize_map(Some(len)) } fn serialize_struct_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, _len: usize, ) -> Result { self.output += "{"; variant.serialize(&mut *self)?; self.output += ":{"; Ok(self) } } impl<'a> ser::SerializeSeq for &'a mut Serializer { type Ok = (); type Error = Error; fn serialize_element(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { if !self.output.ends_with('[') { self.output += ","; } value.serialize(&mut **self) } fn end(self) -> Result<()> { self.output += "]"; Ok(()) } } impl<'a> ser::SerializeTuple for &'a mut Serializer { type Ok = (); type Error = Error; fn serialize_element(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { ser::SerializeSeq::serialize_element(self, value) } fn end(self) -> Result<()> { ser::SerializeSeq::end(self) } } impl<'a> ser::SerializeTupleStruct for &'a mut Serializer { type Ok = (); type Error = Error; fn serialize_field(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { ser::SerializeSeq::serialize_element(self, value) } fn end(self) -> Result<()> { ser::SerializeSeq::end(self) } } impl<'a> ser::SerializeTupleVariant for &'a mut Serializer { type Ok = (); type Error = Error; fn serialize_field(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { ser::SerializeSeq::serialize_element(self, value) } fn end(self) -> Result<()> { self.output += "]}"; Ok(()) } } impl<'a> ser::SerializeMap for &'a mut Serializer { type Ok = (); type Error = Error; fn serialize_key(&mut self, key: &T) -> Result<()> where T: ?Sized + Serialize, { if !self.output.ends_with('{') { self.output += ","; } key.serialize(&mut **self) } fn serialize_value(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { self.output += ":"; value.serialize(&mut **self) } fn end(self) -> Result<()> { self.output += "}"; Ok(()) } } impl<'a> ser::SerializeStruct for &'a mut Serializer { type Ok = (); type Error = Error; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { ser::SerializeMap::serialize_key(self, key)?; ser::SerializeMap::serialize_value(self, value) } fn end(self) -> Result<()> { ser::SerializeMap::end(self) } } impl<'a> ser::SerializeStructVariant for &'a mut Serializer { type Ok = (); type Error = Error; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { ser::SerializeStruct::serialize_field(self, key, value) } fn end(self) -> Result<()> { self.output += "}}"; Ok(()) } } fn escape(v: &str) -> String { v.chars() .flat_map(|c| match c { '"' => vec!['\\', c], '\n' => vec!['\\', 'n'], '\r' => vec!['\\', 'r'], '\t' => vec!['\\', 't'], '/' => vec!['\\', '/'], '\\' => vec!['\\', '\\'], '\u{0008}' => vec!['\\', 'b'], '\u{000c}' => vec!['\\', 'f'], c => vec![c], }) .collect() } json5-0.4.1/tests/adapted_from_js_reference.rs000064400000000000000000000112300000000000000175050ustar 00000000000000use std::collections::HashMap; use std::f64; mod common; use crate::common::{deserializes_to, deserializes_to_nan_f64}; // The following tests are adapted from https://github.com/json5/json5/blob/d828908384ce8dc40d8dde017ae82afd1b952d79/test/parse.js // objects #[test] fn parses_empty_objects() { let m: HashMap = HashMap::new(); deserializes_to("{}", m); } #[test] fn parses_double_string_property_names() { let mut m = HashMap::new(); m.insert("a".to_owned(), 1); deserializes_to("{\"a\":1}", m); } #[test] fn parses_single_string_property_names() { let mut m = HashMap::new(); m.insert("a".to_owned(), 1); deserializes_to("{'a':1}", m); } #[test] fn parses_unquoted_property_names() { let mut m = HashMap::new(); m.insert("a".to_owned(), 1); deserializes_to("{a:1}", m); } #[test] fn parses_special_character_property_names() { let mut m = HashMap::new(); m.insert("$_".to_owned(), 1); m.insert("_$".to_owned(), 2); m.insert("a\u{200C}".to_owned(), 3); deserializes_to("{$_:1,_$:2,a\u{200C}:3}", m); } #[test] fn parses_unicode_property_names() { let mut m = HashMap::new(); m.insert("ùńîċõďë".to_owned(), 9); deserializes_to("{ùńîċõďë:9}", m); } #[test] fn parses_escaped_property_names() { let mut m = HashMap::new(); m.insert("ab".to_owned(), 1); m.insert("$_".to_owned(), 2); m.insert("_$".to_owned(), 3); deserializes_to("{\\u0061\\u0062:1,\\u0024\\u005F:2,\\u005F\\u0024:3}", m); } #[test] fn parses_multiple_properties() { let mut m = HashMap::new(); m.insert("abc".to_owned(), 1); m.insert("def".to_owned(), 2); deserializes_to("{abc:1,def:2}", m); } #[test] fn parses_nested_objects() { let mut inner = HashMap::new(); inner.insert("b".to_owned(), 2); let mut outer = HashMap::new(); outer.insert("a".to_owned(), inner); deserializes_to("{a:{b:2}}", outer); } // arrays #[test] fn parses_empty_arrays() { let v: Vec = vec![]; deserializes_to("[]", v); } #[test] fn parses_array_values() { deserializes_to("[1]", vec![1]); } #[test] fn parses_multiple_array_values() { deserializes_to("[1,2]", vec![1, 2]); } #[test] fn parses_nested_arrays() { deserializes_to("[1,[2,3]]", (1, vec![2, 3])); } #[test] fn parses_nulls() { deserializes_to("null", ()); } #[test] fn parses_true() { deserializes_to("true", true); } #[test] fn parses_false() { deserializes_to("false", false); } // numbers #[test] fn parses_leading_zeroes() { deserializes_to("[0,0,0e0]", vec![0, 0, 0]); } #[test] fn parses_integers() { deserializes_to("[1,23,456,7890]", vec![1, 23, 456, 7890]); } #[test] fn parses_signed_numbers() { deserializes_to("[-1,+2,-.1,-0]", vec![-1., 2., -0.1, -0.]); } #[test] fn parses_leading_decimal_points() { deserializes_to("[.1,.23]", vec![0.1, 0.23]); } #[test] fn parses_fractional_numbers() { deserializes_to("[1.0,1.23]", vec![1., 1.23]); } #[test] fn parses_exponents() { deserializes_to( "[1e0,1e1,1e01,1.e0,1.1e0,1e-1,1e+1]", vec![1., 10., 10., 1., 1.1, 0.1, 10.], ); } #[test] fn parses_hexadecimal_numbers() { deserializes_to("[0x1,0x10,0xff,0xFF]", vec![1, 16, 255, 255]); } #[test] fn parses_signed_and_unsiged_infinity() { deserializes_to( "[Infinity,-Infinity]", vec![f64::INFINITY, f64::NEG_INFINITY], ); } #[test] fn parses_signed_and_unsigned_nan() { deserializes_to_nan_f64("NaN"); deserializes_to_nan_f64("-NaN"); } // strings #[test] fn parses_double_quoted_strings() { deserializes_to("\"abc\"", "abc".to_owned()); } #[test] fn parses_single_quoted_strings() { deserializes_to("'abc'", "abc".to_owned()); } #[test] fn parses_nested_quotes_strings() { deserializes_to("['\"',\"'\"]", vec!["\"".to_owned(), "'".to_owned()]); } #[test] fn parses_escaped_characters() { deserializes_to( "'\\b\\f\\n\\r\\t\\v\\0\\x0f\\u01fF\\\n\\\r\n\\\r\\\u{2028}\\\u{2029}\\a\\'\\\"'", "\u{0008}\u{000C}\n\r\t\u{000B}\0\x0f\u{01FF}a'\"".to_owned(), ); } // comments #[test] fn parses_single_line_comments() { let m: HashMap = HashMap::new(); deserializes_to("{//comment\n}", m); } #[test] fn parses_single_line_comments_at_end_of_input() { let m: HashMap = HashMap::new(); deserializes_to("{}//comment", m); } #[test] fn parses_multi_line_comments() { let m: HashMap = HashMap::new(); deserializes_to("{/*comment\n** */}", m); } #[test] fn parses_whitespace() { let m: HashMap = HashMap::new(); deserializes_to( "{\t\u{000B}\u{000C} \u{00A0}\u{FEFF}\n\r\u{2028}\u{2029}\u{2003}}", m, ); } json5-0.4.1/tests/assets/infinite.json5000064400000000000000000000000530000000000000160700ustar 00000000000000{ inf: Infinity, neg_inf: -Infinity, } json5-0.4.1/tests/assets/json5_dot_org_example.json5000064400000000000000000000005250000000000000205550ustar 00000000000000{ // comments unquoted: 'and you can quote me on that', singleQuotes: 'I can use "double quotes" here', lineBreaks: "Look, Mom! \ No \\n's!", hexadecimal: 0xdecaf, leadingDecimalPoint: .8675309, andTrailing: 8675309., positiveSign: +1, trailingComma: 'in objects', andIn: ['arrays',], "backwardsCompatible": "with JSON", } json5-0.4.1/tests/assets/nan.json5000064400000000000000000000000410000000000000150340ustar 00000000000000{ nan: NaN, neg_nan: -NaN, } json5-0.4.1/tests/common.rs000064400000000000000000000030250000000000000136410ustar 00000000000000use json5::{Error, Location}; use matches::assert_matches; use serde_derive::{Deserialize, Serialize}; use std::collections::HashMap; use std::f64; #[derive(Serialize, Deserialize, PartialEq, Debug)] #[serde(untagged)] pub enum Val { Null, Bool(bool), Number(f64), String(String), Array(Vec), Object(HashMap), } #[allow(unused)] pub fn deserializes_to<'a, T>(s: &'a str, v: T) where T: ::std::fmt::Debug + ::std::cmp::PartialEq + serde::de::Deserialize<'a>, { assert_matches!(json5::from_str::(s), Ok(value) if value == v); } #[allow(unused)] pub fn deserializes_to_nan_f32<'a>(s: &'a str) { assert_matches!(json5::from_str::(s), Ok(value) if value.is_nan()); } #[allow(unused)] pub fn deserializes_to_nan_f64<'a>(s: &'a str) { assert_matches!(json5::from_str::(s), Ok(value) if value.is_nan()); } #[allow(unused)] pub fn deserializes_with_error<'a, T>(s: &'a str, error_expected: Error) where T: ::std::fmt::Debug + ::std::cmp::PartialEq + serde::de::Deserialize<'a>, { assert_matches!(json5::from_str::(s), Err(e) if e == error_expected); } #[allow(unused)] pub fn serializes_to(v: T, s: &'static str) where T: ::std::fmt::Debug + ::std::cmp::PartialEq + serde::ser::Serialize, { assert_matches!(json5::to_string::(&v), Ok(value) if value == s); } #[allow(unused)] pub fn make_error(msg: impl Into, line: usize, column: usize) -> Error { Error::Message { msg: msg.into(), location: Some(Location { line, column }), } } json5-0.4.1/tests/de.rs000064400000000000000000000536540000000000000127560ustar 00000000000000use serde::de; use serde_derive::Deserialize; use std::collections::HashMap; use std::fmt; mod common; use crate::common::{ deserializes_to, deserializes_to_nan_f32, deserializes_to_nan_f64, deserializes_with_error, make_error, }; /// Defines a struct `A` with a `de::Deserializer` implementation that returns an error. Works for /// visitors that accept a single value. macro_rules! error_struct { ($type:ty, $visit_fn:ident, $deserialize_fn:ident) => { #[derive(Debug, PartialEq)] struct A; impl<'de> de::Deserialize<'de> for A { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = A; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("...") } fn $visit_fn(self, _v: $type) -> Result where E: de::Error, { Err(de::Error::custom("oops")) } } deserializer.$deserialize_fn(Visitor) } } }; } #[test] fn deserializes_bool() { deserializes_to("true", true); deserializes_to("false", false); error_struct!(bool, visit_bool, deserialize_bool); deserializes_with_error::("\n true", make_error("oops", 2, 2)); } #[test] fn deserializes_i8() { let x: i8 = 42; deserializes_to("0x2A", x); deserializes_to("0x2a", x); deserializes_to("0X2A", x); deserializes_to("0X2a", x); deserializes_to("0x00002A", x); deserializes_to("42", x); deserializes_to("42.", x); deserializes_to("42.0", x); deserializes_to("42e0", x); deserializes_to("4.2e1", x); deserializes_to(".42e2", x); deserializes_to("0.42e2", x); deserializes_to("-42", -x); deserializes_to("-42.", -x); deserializes_to("-42.0", -x); deserializes_to("-42e0", -x); deserializes_to("-4.2e1", -x); deserializes_to("-.42e2", -x); deserializes_to("-0.42e2", -x); error_struct!(i8, visit_i8, deserialize_i8); deserializes_with_error::("\n 42", make_error("oops", 2, 2)); } #[test] fn deserializes_u8() { let x: u8 = 42; deserializes_to("0x2A", x); deserializes_to("0x2a", x); deserializes_to("0X2A", x); deserializes_to("0X2a", x); deserializes_to("0x00002A", x); deserializes_to("42", x); deserializes_to("42.", x); deserializes_to("42.0", x); deserializes_to("42e0", x); deserializes_to("4.2e1", x); deserializes_to(".42e2", x); deserializes_to("0.42e2", x); error_struct!(u8, visit_u8, deserialize_u8); deserializes_with_error::("\n 42", make_error("oops", 2, 2)); } #[test] fn deserializes_i16() { let x: i16 = 42; deserializes_to("0x2A", x); deserializes_to("0x2a", x); deserializes_to("0X2A", x); deserializes_to("0X2a", x); deserializes_to("0x00002A", x); deserializes_to("42", x); deserializes_to("42.", x); deserializes_to("42.0", x); deserializes_to("42e0", x); deserializes_to("4.2e1", x); deserializes_to(".42e2", x); deserializes_to("0.42e2", x); deserializes_to("-42", -x); deserializes_to("-42.", -x); deserializes_to("-42.0", -x); deserializes_to("-42e0", -x); deserializes_to("-4.2e1", -x); deserializes_to("-.42e2", -x); deserializes_to("-0.42e2", -x); error_struct!(i16, visit_i16, deserialize_i16); deserializes_with_error::("\n 42", make_error("oops", 2, 2)); } #[test] fn deserializes_u16() { let x: u16 = 42; deserializes_to("0x2A", x); deserializes_to("0x2a", x); deserializes_to("0X2A", x); deserializes_to("0X2a", x); deserializes_to("0x00002A", x); deserializes_to("42", x); deserializes_to("42.", x); deserializes_to("42.0", x); deserializes_to("42e0", x); deserializes_to("4.2e1", x); deserializes_to(".42e2", x); deserializes_to("0.42e2", x); error_struct!(u16, visit_u16, deserialize_u16); deserializes_with_error::("\n 42", make_error("oops", 2, 2)); } #[test] fn deserializes_i32() { let x: i32 = 42; deserializes_to("0x2A", x); deserializes_to("0x2a", x); deserializes_to("0X2A", x); deserializes_to("0X2a", x); deserializes_to("0x00002A", x); deserializes_to("42", x); deserializes_to("42.", x); deserializes_to("42.0", x); deserializes_to("42e0", x); deserializes_to("4.2e1", x); deserializes_to(".42e2", x); deserializes_to("0.42e2", x); deserializes_to("-42", -x); deserializes_to("-42.", -x); deserializes_to("-42.0", -x); deserializes_to("-42e0", -x); deserializes_to("-4.2e1", -x); deserializes_to("-.42e2", -x); deserializes_to("-0.42e2", -x); error_struct!(i32, visit_i32, deserialize_i32); deserializes_with_error::("\n 42", make_error("oops", 2, 2)); } #[test] fn deserializes_u32() { let x: u32 = 42; deserializes_to("0x2A", x); deserializes_to("0x2a", x); deserializes_to("0X2A", x); deserializes_to("0X2a", x); deserializes_to("0x00002A", x); deserializes_to("42", x); deserializes_to("42.", x); deserializes_to("42.0", x); deserializes_to("42e0", x); deserializes_to("4.2e1", x); deserializes_to(".42e2", x); deserializes_to("0.42e2", x); error_struct!(u32, visit_u32, deserialize_u32); deserializes_with_error::("\n 42", make_error("oops", 2, 2)); } #[test] fn deserializes_i64() { let x: i64 = 42; deserializes_to("0x2A", x); deserializes_to("0x2a", x); deserializes_to("0X2A", x); deserializes_to("0X2a", x); deserializes_to("0x00002A", x); deserializes_to("42", x); deserializes_to("42.", x); deserializes_to("42.0", x); deserializes_to("42e0", x); deserializes_to("4.2e1", x); deserializes_to(".42e2", x); deserializes_to("0.42e2", x); deserializes_to("-42", -x); deserializes_to("-42.", -x); deserializes_to("-42.0", -x); deserializes_to("-42e0", -x); deserializes_to("-4.2e1", -x); deserializes_to("-.42e2", -x); deserializes_to("-0.42e2", -x); error_struct!(i64, visit_i64, deserialize_i64); deserializes_with_error::("\n 42", make_error("oops", 2, 2)); let over_i64 = format!("\n {}0", i64::max_value()); deserializes_with_error::( over_i64.as_str(), make_error("error parsing integer", 2, 2), ); } #[test] fn deserializes_u64() { let x: u64 = 42; deserializes_to("0x2A", x); deserializes_to("0x2a", x); deserializes_to("0X2A", x); deserializes_to("0X2a", x); deserializes_to("0x00002A", x); deserializes_to("42", x); deserializes_to("42.", x); deserializes_to("42.0", x); deserializes_to("42e0", x); deserializes_to("4.2e1", x); deserializes_to(".42e2", x); deserializes_to("0.42e2", x); deserializes_to("Infinity", std::f32::INFINITY); deserializes_to("-Infinity", std::f32::NEG_INFINITY); deserializes_to_nan_f32("NaN"); error_struct!(u64, visit_u64, deserialize_u64); deserializes_with_error::("\n 42", make_error("oops", 2, 2)); } #[test] fn deserializes_f32() { let x: f32 = 42.42; deserializes_to("42.42", x); deserializes_to("42.42e0", x); deserializes_to("4.242e1", x); deserializes_to(".4242e2", x); deserializes_to("0.4242e2", x); deserializes_to("-42.42", -x); deserializes_to("-42.42", -x); deserializes_to("-42.42", -x); deserializes_to("-42.42e0", -x); deserializes_to("-4.242e1", -x); deserializes_to("-.4242e2", -x); deserializes_to("-0.4242e2", -x); deserializes_to("Infinity", std::f32::INFINITY); deserializes_to("-Infinity", std::f32::NEG_INFINITY); deserializes_to_nan_f32("NaN"); deserializes_to_nan_f32("-NaN"); error_struct!(f32, visit_f32, deserialize_f32); deserializes_with_error::("\n 42", make_error("oops", 2, 2)); } #[test] fn deserializes_f64() { let x: f64 = 42.42; deserializes_to("42.42", x); deserializes_to("42.42e0", x); deserializes_to("4.242e1", x); deserializes_to(".4242e2", x); deserializes_to("0.4242e2", x); deserializes_to("-42.42", -x); deserializes_to("-42.42", -x); deserializes_to("-42.42", -x); deserializes_to("-42.42e0", -x); deserializes_to("-4.242e1", -x); deserializes_to("-.4242e2", -x); deserializes_to("-0.4242e2", -x); deserializes_to("Infinity", std::f64::INFINITY); deserializes_to("-Infinity", std::f64::NEG_INFINITY); deserializes_to_nan_f64("NaN"); deserializes_to_nan_f64("-NaN"); error_struct!(f64, visit_f64, deserialize_f64); deserializes_with_error::("\n 42", make_error("oops", 2, 2)); deserializes_with_error::( "\n 1e309", make_error("error parsing number: too large", 2, 2), ); } #[test] fn deserializes_char() { deserializes_to("'x'", 'x'); deserializes_to("\"자\"", '자'); deserializes_to(r#""\"""#, '"'); deserializes_to(r#""\r""#, '\r'); deserializes_to(r#""\n""#, '\n'); deserializes_to(r#""\t""#, '\t'); deserializes_to(r#""\\""#, '\\'); deserializes_to(r#""\/""#, '/'); deserializes_to(r#""\b""#, '\u{0008}'); deserializes_to(r#""\f""#, '\u{000c}'); // `deserialize_char` calls `visit_str` error_struct!(&str, visit_str, deserialize_char); deserializes_with_error::("\n 'x'", make_error("oops", 2, 2)); } #[test] #[ignore] // TODO currently unsupported fn deserializes_str() { deserializes_to("'Hello!'", "Hello!"); deserializes_to("\"안녕하세요\"", "안녕하세요"); deserializes_to(r#""\uD83C\uDDEF\uD83C\uDDF5""#, "\u{1F1EF}\u{1F1F5}"); } #[test] fn deserializes_string() { deserializes_to("'Hello!'", "Hello!".to_owned()); deserializes_to("\"안녕하세요\"", "안녕하세요".to_owned()); deserializes_to( r#""\uD83C\uDDEF\uD83C\uDDF5""#, "\u{1F1EF}\u{1F1F5}".to_owned(), ); error_struct!(&str, visit_str, deserialize_string); deserializes_with_error::("\n 'Hello!'", make_error("oops", 2, 2)); } #[test] #[ignore] // TODO currently unsupported fn deserializes_bytes() {} #[test] #[ignore] // TODO currently unsupported fn deserializes_byte_buf() {} #[test] fn deserializes_option() { deserializes_to::>("null", None); deserializes_to("42", Some(42)); deserializes_to("42", Some(Some(42))); } #[test] fn deserializes_option_error() { #[derive(Debug, PartialEq)] struct A; impl<'de> de::Deserialize<'de> for A { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = A; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("...") } fn visit_some(self, _deserializer: D) -> Result where D: de::Deserializer<'de>, { Err(de::Error::custom("oops")) } } deserializer.deserialize_option(Visitor) } } deserializes_with_error::("\n 42", make_error("oops", 2, 2)); } #[test] fn deserializes_unit() { deserializes_to("null", ()); #[derive(Deserialize, Debug, PartialEq)] struct A; deserializes_to("null", A); } #[test] fn deserializes_unit_error() { #[derive(Debug, PartialEq)] struct A; impl<'de> de::Deserialize<'de> for A { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = A; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("...") } fn visit_unit(self) -> Result where E: de::Error, { Err(de::Error::custom("oops")) } } deserializer.deserialize_unit(Visitor) } } deserializes_with_error::("\n null", make_error("oops", 2, 2)); } #[test] fn deserializes_newtype_struct() { #[derive(Deserialize, PartialEq, Debug)] struct A(i32); #[derive(Deserialize, PartialEq, Debug)] struct B(f64); deserializes_to("42", A(42)); deserializes_to("42", B(42.)); } #[test] fn deserializes_newtype_struct_error() { #[derive(Debug, PartialEq)] struct A; impl<'de> de::Deserialize<'de> for A { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = A; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("...") } fn visit_newtype_struct(self, _deserializer: D) -> Result where D: de::Deserializer<'de>, { Err(de::Error::custom("oops")) } } deserializer.deserialize_newtype_struct("A", Visitor) } } deserializes_with_error::("\n 42", make_error("oops", 2, 2)); } #[test] fn deserializes_seq() { #[derive(Deserialize, PartialEq, Debug)] #[serde(untagged)] enum Val { Number(f64), Bool(bool), String(String), } deserializes_to("[1, 2, 3]", vec![1, 2, 3]); deserializes_to( "[42, true, 'hello']", vec![ Val::Number(42.), Val::Bool(true), Val::String("hello".to_owned()), ], ); } #[test] fn deserializes_seq_size_hint() { #[derive(Debug, PartialEq)] struct Size(usize); impl<'de> de::Deserialize<'de> for Size { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = Size; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("...") } fn visit_seq(self, seq: A) -> Result where A: de::SeqAccess<'de>, { Ok(Size(seq.size_hint().unwrap())) } } deserializer.deserialize_seq(Visitor) } } deserializes_to("[]", Size(0)); deserializes_to("[42, true, 'hello']", Size(3)); deserializes_to("[42, true, [1, 2]]", Size(3)); } #[test] fn deserializes_seq_error() { #[derive(Debug, PartialEq)] struct A; impl<'de> de::Deserialize<'de> for A { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = A; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("...") } fn visit_seq(self, _a: A) -> Result where A: de::SeqAccess<'de>, { Err(de::Error::custom("oops")) } } deserializer.deserialize_seq(Visitor) } } deserializes_with_error::("\n [ true ]", make_error("oops", 2, 2)); } #[test] fn deserializes_tuple() { deserializes_to("[1, 2, 3]", (1, 2, 3)); } #[test] fn deserializes_tuple_struct() { #[derive(Deserialize, PartialEq, Debug)] struct A(i32, f64); #[derive(Deserialize, PartialEq, Debug)] struct B(f64, i32); deserializes_to("[1, 2]", A(1, 2.)); deserializes_to("[1, 2]", B(1., 2)); } #[test] fn deserializes_tuple_error() { #[derive(Debug, PartialEq)] struct A; impl<'de> de::Deserialize<'de> for A { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = A; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("...") } fn visit_seq(self, _a: A) -> Result where A: de::SeqAccess<'de>, { Err(de::Error::custom("oops")) } } deserializer.deserialize_tuple(2, Visitor) } } deserializes_with_error::("\n [1, 2]", make_error("oops", 2, 2)); #[derive(Deserialize, Debug, PartialEq)] struct B(i32, f64); deserializes_with_error::( "\n [1]", make_error( "invalid length 1, expected tuple struct B with 2 elements", 2, 2, ), ); } #[test] fn deserializes_map() { let mut m = HashMap::new(); m.insert("a".to_owned(), 1); m.insert("b".to_owned(), 2); m.insert("c".to_owned(), 3); deserializes_to("{ a: 1, 'b': 2, \"c\": 3 }", m); } #[test] fn deserializes_map_size_hint() { #[derive(Debug, PartialEq)] struct Size(usize); impl<'de> de::Deserialize<'de> for Size { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = Size; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("...") } fn visit_map(self, map: A) -> Result where A: de::MapAccess<'de>, { Ok(Size(map.size_hint().unwrap())) } } deserializer.deserialize_map(Visitor) } } deserializes_to("{}", Size(0)); deserializes_to("{ a: 1, 'b': 2, \"c\": 3 }", Size(3)); deserializes_to("{ a: 1, 'b': 2, \"c\": [1, 2] }", Size(3)); } #[test] fn deserializes_map_error() { #[derive(Debug, PartialEq)] struct A {} impl<'de> de::Deserialize<'de> for A { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = A; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("...") } fn visit_map(self, _a: A) -> Result where A: de::MapAccess<'de>, { Err(de::Error::custom("oops")) } } deserializer.deserialize_map(Visitor) } } deserializes_with_error::("\n { 'a': true }", make_error("oops", 2, 2)); } #[test] fn deserializes_struct() { #[derive(Deserialize, PartialEq, Debug)] struct S { a: i32, b: i32, c: i32, } deserializes_to("{ a: 1, 'b': 2, \"c\": 3 }", S { a: 1, b: 2, c: 3 }); } #[test] fn deserializes_struct_error() { #[derive(Deserialize, PartialEq, Debug)] struct S { a: i32, b: i32, c: i32, } deserializes_with_error::("\n { a: 1, 'b': 2 }", make_error("missing field `c`", 2, 2)); } #[test] fn deserializes_enum() { #[derive(Deserialize, PartialEq, Debug)] enum E { A, B(i32), C(i32, i32), D { a: i32, b: i32 }, E {}, F(), } deserializes_to("'A'", E::A); deserializes_to("{ B: 2 }", E::B(2)); deserializes_to("{ C: [3, 5] }", E::C(3, 5)); deserializes_to("{ D: { a: 7, b: 11 } }", E::D { a: 7, b: 11 }); deserializes_to("{ E: {} }", E::E {}); deserializes_to("{ F: [] }", E::F()); } #[test] fn deserializes_enum_error() { #[derive(Deserialize, PartialEq, Debug)] enum E { A {}, B(), } #[derive(Deserialize, PartialEq, Debug)] struct S { e: E, } deserializes_with_error::("{ e: 'A' }", make_error("expected an object", 1, 6)); deserializes_with_error::("{ e: 'B' }", make_error("expected an array", 1, 6)); deserializes_with_error::( "\n 'C'", make_error("unknown variant `C`, expected `A` or `B`", 2, 2), ); } #[test] fn deserializes_ignored() { #[derive(Deserialize, PartialEq, Debug)] struct S { a: i32, b: i32, } deserializes_to("{ a: 1, ignored: 42, b: 2 }", S { a: 1, b: 2 }); } #[test] fn deserializes_json_values() { // As int if json uses int type. deserializes_to("0x2a", serde_json::json!(42)); deserializes_to("0x2A", serde_json::json!(42)); deserializes_to("0X2A", serde_json::json!(42)); deserializes_to("42", serde_json::json!(42)); // As float if json calls for explicit float type. deserializes_to("42.", serde_json::json!(42.)); deserializes_to("42e0", serde_json::json!(42.)); deserializes_to("4e2", serde_json::json!(400.)); deserializes_to("4e2", serde_json::json!(4e2)); } #[test] fn deserializes_parse_error() { let parse_err_str = r#" --> 1:2 | 1 | { | ^--- | = expected identifier or string"#; #[derive(Deserialize, PartialEq, Debug)] struct A; deserializes_with_error::("{", make_error(parse_err_str, 1, 2)); deserializes_with_error::( "\n 42", make_error("invalid type: integer `42`, expected a boolean", 2, 2), ); } json5-0.4.1/tests/examples.rs000064400000000000000000000077520000000000000142020ustar 00000000000000use serde_derive::Deserialize; use serde_json::{json, Value}; use std::collections::HashMap; use std::fs::File; use std::io::prelude::*; mod common; use crate::common::{deserializes_to, serializes_to, Val}; #[test] fn serializes_example_infinite() { let mut map = HashMap::new(); map.insert("inf".to_string(), Val::Number(f64::INFINITY)); serializes_to(Val::Object(map), "{\"inf\":Infinity}"); serializes_to(json!({ "inf": f64::INFINITY }), "{\"inf\":null}"); let mut map2 = HashMap::new(); map2.insert("neg_inf".to_string(), Val::Number(f64::NEG_INFINITY)); serializes_to(Val::Object(map2), "{\"neg_inf\":-Infinity}"); serializes_to( json!({ "neg_inf": f64::NEG_INFINITY }), "{\"neg_inf\":null}", ); } #[test] fn deserializes_example_infinite() { let mut contents = String::new(); File::open("tests/assets/infinite.json5") .unwrap() .read_to_string(&mut contents) .unwrap(); #[derive(Deserialize, PartialEq, Debug)] struct InfiniteF64 { inf: f64, neg_inf: f64, } let expected_f64 = InfiniteF64 { inf: f64::INFINITY, neg_inf: f64::NEG_INFINITY, }; deserializes_to(&contents, expected_f64); #[derive(Deserialize, PartialEq, Debug)] struct InfiniteF32 { inf: f32, neg_inf: f32, } let expected_f32 = InfiniteF32 { inf: f32::INFINITY, neg_inf: f32::NEG_INFINITY, }; deserializes_to(&contents, expected_f32); let mut map = HashMap::new(); map.insert("inf".to_owned(), Val::Number(f64::INFINITY)); map.insert("neg_inf".to_owned(), Val::Number(f64::NEG_INFINITY)); deserializes_to::(&contents, Val::Object(map)); deserializes_to::( &contents, json!({ "inf": null, "neg_inf": null }), ) } #[test] fn serializes_example_nan() { let mut map = HashMap::new(); map.insert("nan".to_string(), Val::Number(f64::NAN)); serializes_to(Val::Object(map), "{\"nan\":NaN}"); serializes_to(json!({ "nan": f64::NAN }), "{\"nan\":null}"); let mut map2 = HashMap::new(); map2.insert("neg_nan".to_string(), Val::Number(f64::NAN)); serializes_to(Val::Object(map2), "{\"neg_nan\":NaN}"); serializes_to(json!({ "neg_nan": f64::NAN }), "{\"neg_nan\":null}"); } #[test] fn deserializes_example_nan() { let mut contents = String::new(); File::open("tests/assets/nan.json5") .unwrap() .read_to_string(&mut contents) .unwrap(); #[derive(Deserialize, PartialEq, Debug)] struct NanF64 { nan: f64, neg_nan: f64, } match json5::from_str::(&contents) { Ok(value) => assert!(value.nan.is_nan() && value.neg_nan.is_nan()), Err(err) => panic!(format!("{}", err)), } #[derive(Deserialize, PartialEq, Debug)] struct NanF32 { nan: f32, neg_nan: f32, } match json5::from_str::(&contents) { Ok(value) => assert!(value.nan.is_nan() && value.neg_nan.is_nan()), Err(err) => panic!(format!("{}", err)), } let mut map = HashMap::new(); map.insert("nan".to_string(), Val::Number(f64::NAN)); map.insert("neg_nan".to_string(), Val::Number(f64::NAN)); match json5::from_str::(&contents) { Ok(value) => match value { Val::Object(v) => { let nan = match v.get(&"nan".to_string()).unwrap() { Val::Number(n) => n, _ => panic!("not NaN"), }; let neg_nan = match v.get(&"neg_nan".to_string()).unwrap() { Val::Number(n) => n, _ => panic!("not NaN"), }; assert!(nan.is_nan() && neg_nan.is_nan()) } _ => panic!("not NaN"), }, Err(err) => panic!(format!("{}", err)), } deserializes_to::( &contents, json!({ "nan": null, "neg_nan": null }), ) } json5-0.4.1/tests/json5_dot_org_example.rs000064400000000000000000000023470000000000000166450ustar 00000000000000use serde_derive::Deserialize; use std::fs::File; use std::io::prelude::*; mod common; use crate::common::deserializes_to; #[derive(Deserialize, PartialEq, Debug)] #[serde(rename_all = "camelCase")] struct Example { unquoted: String, single_quotes: String, line_breaks: String, hexadecimal: u32, leading_decimal_point: f64, and_trailing: f64, positive_sign: i32, trailing_comma: String, and_in: Vec, backwards_compatible: String, } #[test] fn serializes_example_from_json5_dot_org() { let mut contents = String::new(); File::open("tests/assets/json5_dot_org_example.json5") .unwrap() .read_to_string(&mut contents) .unwrap(); let expected = Example { unquoted: "and you can quote me on that".to_owned(), single_quotes: "I can use \"double quotes\" here".to_owned(), line_breaks: "Look, Mom! No \\n's!".to_owned(), hexadecimal: 0xdecaf, leading_decimal_point: 0.8675309, and_trailing: 8675309.0, positive_sign: 1, trailing_comma: "in objects".to_owned(), and_in: vec!["arrays".to_owned()], backwards_compatible: "with JSON".to_owned(), }; deserializes_to(&contents, expected) } json5-0.4.1/tests/ser.rs000064400000000000000000000111040000000000000131370ustar 00000000000000use serde_derive::Serialize; use std::collections::HashMap; mod common; use crate::common::serializes_to; #[test] fn serializes_bool() { serializes_to(true, "true"); serializes_to(false, "false"); } #[test] fn serializes_i8() { let x: i8 = 42; serializes_to(x, "42"); } #[test] fn serializes_u8() { let x: u8 = 42; serializes_to(x, "42"); } #[test] fn serializes_i16() { let x: i16 = 42; serializes_to(x, "42"); } #[test] fn serializes_u16() { let x: u16 = 42; serializes_to(x, "42"); } #[test] fn serializes_i32() { let x: i32 = 42; serializes_to(x, "42"); } #[test] fn serializes_u32() { let x: u32 = 42; serializes_to(x, "42"); } #[test] fn serializes_i64() { let x: i64 = 42; serializes_to(x, "42"); } #[test] fn serializes_u64() { let x: u64 = 42; serializes_to(x, "42"); } #[test] fn serializes_f32() { let x: f32 = 42.42; serializes_to(x, "42.42"); serializes_to(std::f32::INFINITY, "Infinity"); serializes_to(std::f32::NEG_INFINITY, "-Infinity"); serializes_to(std::f32::NAN, "NaN"); } #[test] fn serializes_f64() { let x: f64 = 42.42; serializes_to(x, "42.42"); serializes_to(std::f64::INFINITY, "Infinity"); serializes_to(std::f64::NEG_INFINITY, "-Infinity"); serializes_to(std::f64::NAN, "NaN"); } #[test] fn serializes_char() { serializes_to('x', "\"x\""); serializes_to('자', "\"자\""); serializes_to('"', r#""\"""#); serializes_to('\r', r#""\r""#); serializes_to('\n', r#""\n""#); serializes_to('\t', r#""\t""#); serializes_to('\\', r#""\\""#); serializes_to('/', r#""\/""#); serializes_to('\u{0008}', r#""\b""#); serializes_to('\u{000c}', r#""\f""#); } #[test] fn serializes_str() { serializes_to("Hello!", "\"Hello!\""); serializes_to("안녕하세요", "\"안녕하세요\""); serializes_to("\"quotes!\"", "\"\\\"quotes!\\\"\""); serializes_to("new\nlines", "\"new\\nlines\""); serializes_to("\\", "\"\\\\\""); } #[test] fn serializes_string() { serializes_to("Hello!".to_owned(), "\"Hello!\""); serializes_to("안녕하세요".to_owned(), "\"안녕하세요\""); serializes_to("\"quotes!\"".to_owned(), "\"\\\"quotes!\\\"\""); serializes_to("new\nlines".to_owned(), "\"new\\nlines\""); serializes_to("\\".to_owned(), "\"\\\\\""); } #[test] #[ignore] // TODO currently unsupported fn serializes_bytes() {} #[test] #[ignore] // TODO currently unsupported fn serializes_byte_buf() {} #[test] fn serializes_option() { serializes_to::>(None, "null"); serializes_to(Some(42), "42"); serializes_to(Some(Some(42)), "42"); } #[test] fn serializes_unit() { serializes_to((), "null"); } #[test] fn serializes_unit_struct() { #[derive(Serialize, PartialEq, Debug)] struct A; serializes_to(A, "null"); } #[test] fn serializes_newtype_struct() { #[derive(Serialize, PartialEq, Debug)] struct A(i32); #[derive(Serialize, PartialEq, Debug)] struct B(f64); serializes_to(A(42), "42"); serializes_to(B(42.), "42"); } #[test] fn serializes_seq() { #[derive(Serialize, PartialEq, Debug)] #[serde(untagged)] enum Val { Number(f64), Bool(bool), String(String), } serializes_to(vec![1, 2, 3], "[1,2,3]"); serializes_to( vec![ Val::Number(42.), Val::Bool(true), Val::String("hello".to_owned()), ], "[42,true,\"hello\"]", ) } #[test] fn serializes_tuple() { serializes_to((1, 2, 3), "[1,2,3]"); } #[test] fn serializes_tuple_struct() { #[derive(Serialize, PartialEq, Debug)] struct A(i32, f64); #[derive(Serialize, PartialEq, Debug)] struct B(f64, i32); serializes_to(A(1, 2.), "[1,2]"); serializes_to(B(1., 2), "[1,2]"); } #[test] fn serializes_map() { let mut inner = HashMap::new(); inner.insert("b".to_owned(), true); let mut outer = HashMap::new(); outer.insert("a".to_owned(), inner); serializes_to(outer, "{\"a\":{\"b\":true}}"); } #[test] fn serializes_struct() { #[derive(Serialize, PartialEq, Debug)] struct S { a: i32, b: i32, c: i32, } serializes_to(S { a: 1, b: 2, c: 3 }, "{\"a\":1,\"b\":2,\"c\":3}"); } #[test] fn serializes_enum() { #[derive(Serialize, PartialEq, Debug)] enum E { A, B(i32), C(i32, i32), D { a: i32, b: i32 }, } serializes_to(E::A, "\"A\""); serializes_to(E::B(2), "{\"B\":2}"); serializes_to(E::C(3, 5), "{\"C\":[3,5]}"); serializes_to(E::D { a: 7, b: 11 }, "{\"D\":{\"a\":7,\"b\":11}}"); }