diff --git a/Cargo.toml b/Cargo.toml index 36682c0cc..55de4e105 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,17 +16,17 @@ travis-ci = { repository = "serde-rs/json" } appveyor = { repository = "serde-rs/json" } [dependencies] -serde = "1.0.60" +serde = { version = "1.0.85", default-features = false } indexmap = { version = "1.0", optional = true } -itoa = "0.4.3" +itoa = { version = "0.4.3", optional = true, default-features = false } ryu = "0.2" [dev-dependencies] -automod = "0.1" -compiletest_rs = { version = "0.3", features = ["stable"] } -serde_bytes = "0.10" -serde_derive = "1.0" -serde_stacker = "0.1" +# automod = "0.1" +# compiletest_rs = { version = "0.3", features = ["stable"] } +# serde_bytes = "0.10" +# serde_derive = "1.0" +# serde_stacker = "0.1" [package.metadata.docs.rs] features = ["raw_value", "unbounded_depth"] @@ -38,7 +38,7 @@ features = ["raw_value"] ### FEATURES ################################################################# [features] -default = [] +default = ["std"] # Use a different representation for the map type of serde_json::Value. # This allows data to be read into a Value and written back to a JSON string @@ -62,3 +62,8 @@ raw_value = [] # overflow the stack after deserialization has completed, including, but not # limited to, Display and Debug and Drop impls. unbounded_depth = [] + +std = ["serde/default", "itoa/default", "indexmap"] + +# No-Std feature +no_std = ["serde/alloc", "itoa"] \ No newline at end of file diff --git a/src/de.rs b/src/de.rs index f8d4ace53..51fd94404 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1,9 +1,26 @@ //! Deserialize JSON data to a Rust data structure. +#[cfg(not(feature = "std"))] +use alloc::str::FromStr; +#[cfg(not(feature = "std"))] +use alloc::string::String; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use core::marker::PhantomData; +#[cfg(not(feature = "std"))] +use core::result; +#[cfg(not(feature = "std"))] +use core::{i32, u64}; +#[cfg(feature = "std")] use std::io; +#[cfg(feature = "std")] use std::marker::PhantomData; +#[cfg(feature = "std")] use std::result; +#[cfg(feature = "std")] use std::str::FromStr; +#[cfg(feature = "std")] use std::{i32, u64}; use serde::de::{self, Expected, Unexpected}; @@ -12,7 +29,9 @@ use super::error::{Error, ErrorCode, Result}; use read::{self, Reference}; -pub use read::{IoRead, Read, SliceRead, StrRead}; +#[cfg(feature = "std")] +pub use read::IoRead; +pub use read::{Read, SliceRead, StrRead}; use number::Number; #[cfg(feature = "arbitrary_precision")] @@ -63,6 +82,7 @@ where } } +#[cfg(feature = "std")] impl Deserializer> where R: io::Read, @@ -2242,6 +2262,7 @@ where /// is wrong with the data, for example required struct fields are missing from /// the JSON map or some number is too big to fit in the expected primitive /// type. +#[cfg(feature = "std")] pub fn from_reader(rdr: R) -> Result where R: io::Read, diff --git a/src/error.rs b/src/error.rs index 42f09373a..1733d3913 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,11 +1,27 @@ //! When serializing or deserializing JSON goes wrong. +#[cfg(not(feature = "std"))] +use alloc::str::FromStr; +#[cfg(not(feature = "std"))] +use core::fmt::{self, Debug, Display}; +#[cfg(not(feature = "std"))] +use core::result; +#[cfg(feature = "std")] use std::error; +#[cfg(feature = "std")] use std::fmt::{self, Debug, Display}; +#[cfg(feature = "std")] use std::io; +#[cfg(feature = "std")] use std::result; +#[cfg(feature = "std")] use std::str::FromStr; +#[cfg(not(feature = "std"))] +use alloc::prelude::*; +#[cfg(not(feature = "std"))] +use alloc::string::String; + use serde::de; use serde::ser; @@ -130,6 +146,7 @@ pub enum Category { Eof, } +#[cfg(feature = "std")] #[cfg_attr(feature = "cargo-clippy", allow(fallible_impl_from))] impl From for io::Error { /// Convert a `serde_json::Error` into an `io::Error`. @@ -185,8 +202,13 @@ pub enum ErrorCode { Message(Box), /// Some IO error occurred while serializing or deserializing. + #[cfg(feature = "std")] Io(io::Error), + /// Some IO error occurred while serializing or deserializing. + #[cfg(not(feature = "std"))] + Io(fmt::Error), + /// EOF while parsing a list. EofWhileParsingList, @@ -271,6 +293,7 @@ impl Error { // Not public API. Should be pub(crate). // // Update `eager_json` crate when this function changes. + #[cfg(feature = "std")] #[doc(hidden)] #[cold] pub fn io(error: io::Error) -> Self { @@ -283,6 +306,19 @@ impl Error { } } + #[cfg(not(feature = "std"))] + #[doc(hidden)] + #[cold] + pub fn io(error: fmt::Error) -> Self { + Error { + err: Box::new(ErrorImpl { + code: ErrorCode::Io(error), + line: 0, + column: 0, + }), + } + } + // Not public API. Should be pub(crate). #[doc(hidden)] #[cold] @@ -333,6 +369,7 @@ impl Display for ErrorCode { } } +#[cfg(feature = "std")] impl error::Error for Error { fn description(&self) -> &str { match self.err.code { diff --git a/src/lib.rs b/src/lib.rs index ed2c15d63..2cc47bb0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -317,32 +317,46 @@ redundant_field_names, ))] #![deny(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "std"), feature(alloc))] #[macro_use] extern crate serde; +#[cfg(not(feature = "std"))] +extern crate alloc; #[cfg(feature = "preserve_order")] extern crate indexmap; extern crate itoa; extern crate ryu; +#[cfg(feature = "std")] #[doc(inline)] -pub use self::de::{from_reader, from_slice, from_str, Deserializer, StreamDeserializer}; +pub use self::de::from_reader; +#[doc(inline)] +pub use self::de::{from_slice, from_str, Deserializer, StreamDeserializer}; +#[cfg(feature = "std")] #[doc(inline)] pub use self::error::{Error, Result}; #[doc(inline)] -pub use self::ser::{ - to_string, to_string_pretty, to_vec, to_vec_pretty, to_writer, to_writer_pretty, Serializer, -}; +pub use self::ser::{to_string, to_vec, to_writer, Serializer}; +#[cfg(feature = "std")] +#[doc(inline)] +pub use self::ser::{to_string_pretty, to_vec_pretty, to_writer_pretty}; #[doc(inline)] pub use self::value::{from_value, to_value, Map, Number, Value}; +#[cfg(not(feature = "std"))] +use core::result; +#[cfg(feature = "std")] +use std::result; + // We only use our own error type; no need for From conversions provided by the // standard library's try! macro. This reduces lines of LLVM IR by 4%. macro_rules! try { ($e:expr) => { match $e { - ::std::result::Result::Ok(val) => val, - ::std::result::Result::Err(err) => return ::std::result::Result::Err(err), + ::result::Result::Ok(val) => val, + ::result::Result::Err(err) => return ::result::Result::Err(err), } }; } @@ -356,9 +370,11 @@ pub mod map; pub mod ser; pub mod value; +#[cfg(feature = "std")] mod iter; mod number; mod read; +#[cfg(feature = "std")] #[cfg(feature = "raw_value")] mod raw; diff --git a/src/map.rs b/src/map.rs index 642463032..857f3a4b5 100644 --- a/src/map.rs +++ b/src/map.rs @@ -6,14 +6,36 @@ //! [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html //! [`IndexMap`]: https://docs.rs/indexmap/*/indexmap/map/struct.IndexMap.html +#[cfg(not(feature = "std"))] +use core::borrow::Borrow; +#[cfg(not(feature = "std"))] +use core::fmt::{self, Debug}; +#[cfg(not(feature = "std"))] +use core::hash::Hash; +#[cfg(not(feature = "std"))] +use core::iter::FromIterator; +#[cfg(not(feature = "std"))] +use core::ops; use serde::{de, ser}; +#[cfg(feature = "std")] use std::borrow::Borrow; +#[cfg(feature = "std")] use std::fmt::{self, Debug}; +#[cfg(feature = "std")] use std::hash::Hash; +#[cfg(feature = "std")] use std::iter::FromIterator; +#[cfg(feature = "std")] use std::ops; use value::Value; +#[cfg(not(feature = "std"))] +use alloc::string::String; + +#[cfg(not(feature = "std"))] +#[cfg(not(feature = "preserve_order"))] +use alloc::collections::{btree_map, BTreeMap}; +#[cfg(feature = "std")] #[cfg(not(feature = "preserve_order"))] use std::collections::{btree_map, BTreeMap}; @@ -135,8 +157,12 @@ impl Map { where S: Into, { + #[cfg(not(feature = "std"))] + #[cfg(not(feature = "preserve_order"))] + use alloc::collections::btree_map::Entry as EntryImpl; #[cfg(feature = "preserve_order")] use indexmap::map::Entry as EntryImpl; + #[cfg(feature = "std")] #[cfg(not(feature = "preserve_order"))] use std::collections::btree_map::Entry as EntryImpl; diff --git a/src/number.rs b/src/number.rs index 70a8f6d65..cac0297b6 100644 --- a/src/number.rs +++ b/src/number.rs @@ -1,6 +1,9 @@ +#[cfg(not(feature = "std"))] +use core::fmt::{self, Debug, Display}; use error::Error; use serde::de::{self, Unexpected, Visitor}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; +#[cfg(feature = "std")] use std::fmt::{self, Debug, Display}; #[cfg(feature = "arbitrary_precision")] diff --git a/src/read.rs b/src/read.rs index 71b050413..310d9c580 100644 --- a/src/read.rs +++ b/src/read.rs @@ -1,9 +1,16 @@ +#[cfg(not(feature = "std"))] +use core::ops::Deref; +#[cfg(not(feature = "std"))] +use core::{char, cmp, str}; +#[cfg(feature = "std")] use std::ops::Deref; +#[cfg(feature = "std")] use std::{char, cmp, io, str}; #[cfg(feature = "raw_value")] use serde::de::Visitor; +#[cfg(feature = "std")] use iter::LineColIterator; use error::{Error, ErrorCode, Result}; @@ -11,6 +18,9 @@ use error::{Error, ErrorCode, Result}; #[cfg(feature = "raw_value")] use raw::{BorrowedRawDeserializer, OwnedRawDeserializer}; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; + /// Trait used by the deserializer for iterating over input. This is manually /// "specialized" for iterating over &[u8]. Once feature(specialization) is /// stable we can use actual specialization. @@ -118,6 +128,7 @@ impl<'b, 'c, T: ?Sized + 'static> Deref for Reference<'b, 'c, T> { } /// JSON input source that reads from a std::io input stream. +#[cfg(feature = "std")] pub struct IoRead where R: io::Read, @@ -157,6 +168,7 @@ mod private { ////////////////////////////////////////////////////////////////////////////// +#[cfg(feature = "std")] impl IoRead where R: io::Read, @@ -181,8 +193,10 @@ where } } +#[cfg(feature = "std")] impl private::Sealed for IoRead where R: io::Read {} +#[cfg(feature = "std")] impl IoRead where R: io::Read, @@ -221,6 +235,7 @@ where } } +#[cfg(feature = "std")] impl<'de, R> Read<'de> for IoRead where R: io::Read, diff --git a/src/ser.rs b/src/ser.rs index 463c15a16..d6075f6d3 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -1,8 +1,20 @@ //! Serialize a Rust data structure into JSON data. +#[cfg(not(feature = "std"))] +use alloc::prelude::{String, ToString, Vec}; +#[cfg(not(feature = "std"))] +use core::fmt; +#[cfg(not(feature = "std"))] +use core::num::FpCategory; +#[cfg(not(feature = "std"))] +use core::str; +#[cfg(feature = "std")] use std::fmt; +#[cfg(feature = "std")] use std::io; +#[cfg(feature = "std")] use std::num::FpCategory; +#[cfg(feature = "std")] use std::str; use super::error::{Error, ErrorCode, Result}; @@ -11,6 +23,16 @@ use serde::ser::{self, Impossible, Serialize}; use itoa; use ryu; +#[cfg(not(feature = "std"))] +use core::fmt::Write as WriteTrait; +#[cfg(feature = "std")] +use std::io::Write as WriteTrait; + +#[cfg(feature = "std")] +type IoResult = io::Result<()>; +#[cfg(not(feature = "std"))] +type IoResult = fmt::Result; + /// A structure for serializing Rust values into JSON. pub struct Serializer { writer: W, @@ -19,7 +41,7 @@ pub struct Serializer { impl Serializer where - W: io::Write, + W: WriteTrait, { /// Creates a new JSON serializer. #[inline] @@ -28,6 +50,7 @@ where } } +#[cfg(feature = "std")] impl<'a, W> Serializer> where W: io::Write, @@ -41,7 +64,7 @@ where impl Serializer where - W: io::Write, + W: WriteTrait, F: Formatter, { /// Creates a new JSON visitor whose output will be written to the writer @@ -63,7 +86,7 @@ where impl<'a, W, F> ser::Serializer for &'a mut Serializer where - W: io::Write, + W: WriteTrait, F: Formatter, { type Ok = (); @@ -460,17 +483,23 @@ where where T: fmt::Display, { + #[cfg(not(feature = "std"))] + use core::fmt::Write; + #[cfg(feature = "std")] use std::fmt::Write; struct Adapter<'ser, W: 'ser, F: 'ser> { writer: &'ser mut W, formatter: &'ser mut F, + #[cfg(feature = "std")] error: Option, + #[cfg(not(feature = "std"))] + error: Option, } - impl<'ser, W, F> Write for Adapter<'ser, W, F> + impl<'ser, W, F> fmt::Write for Adapter<'ser, W, F> where - W: io::Write, + W: WriteTrait, F: Formatter, { fn write_str(&mut self, s: &str) -> fmt::Result { @@ -534,7 +563,7 @@ pub enum Compound<'a, W: 'a, F: 'a> { impl<'a, W, F> ser::SerializeSeq for Compound<'a, W, F> where - W: io::Write, + W: WriteTrait, F: Formatter, { type Ok = (); @@ -589,7 +618,7 @@ where impl<'a, W, F> ser::SerializeTuple for Compound<'a, W, F> where - W: io::Write, + W: WriteTrait, F: Formatter, { type Ok = (); @@ -611,7 +640,7 @@ where impl<'a, W, F> ser::SerializeTupleStruct for Compound<'a, W, F> where - W: io::Write, + W: WriteTrait, F: Formatter, { type Ok = (); @@ -633,7 +662,7 @@ where impl<'a, W, F> ser::SerializeTupleVariant for Compound<'a, W, F> where - W: io::Write, + W: WriteTrait, F: Formatter, { type Ok = (); @@ -672,7 +701,7 @@ where impl<'a, W, F> ser::SerializeMap for Compound<'a, W, F> where - W: io::Write, + W: WriteTrait, F: Formatter, { type Ok = (); @@ -754,7 +783,7 @@ where impl<'a, W, F> ser::SerializeStruct for Compound<'a, W, F> where - W: io::Write, + W: WriteTrait, F: Formatter, { type Ok = (); @@ -805,7 +834,7 @@ where impl<'a, W, F> ser::SerializeStructVariant for Compound<'a, W, F> where - W: io::Write, + W: WriteTrait, F: Formatter, { type Ok = (); @@ -852,11 +881,13 @@ struct MapKeySerializer<'a, W: 'a, F: 'a> { ser: &'a mut Serializer, } +#[cfg(feature = "std")] #[cfg(feature = "arbitrary_precision")] fn invalid_number() -> Error { Error::syntax(ErrorCode::InvalidNumber, 0, 0) } +#[cfg(feature = "std")] #[cfg(feature = "raw_value")] fn invalid_raw_value() -> Error { Error::syntax(ErrorCode::ExpectedSomeValue, 0, 0) @@ -868,7 +899,7 @@ fn key_must_be_a_string() -> Error { impl<'a, W, F> ser::Serializer for MapKeySerializer<'a, W, F> where - W: io::Write, + W: WriteTrait, F: Formatter, { type Ok = (); @@ -1196,9 +1227,11 @@ where } } +#[cfg(feature = "std")] #[cfg(feature = "arbitrary_precision")] struct NumberStrEmitter<'a, W: 'a + io::Write, F: 'a + Formatter>(&'a mut Serializer); +#[cfg(feature = "std")] #[cfg(feature = "arbitrary_precision")] impl<'a, W: io::Write, F: Formatter> ser::Serializer for NumberStrEmitter<'a, W, F> { type Ok = (); @@ -1381,9 +1414,11 @@ impl<'a, W: io::Write, F: Formatter> ser::Serializer for NumberStrEmitter<'a, W, } } +#[cfg(feature = "std")] #[cfg(feature = "raw_value")] struct RawValueStrEmitter<'a, W: 'a + io::Write, F: 'a + Formatter>(&'a mut Serializer); +#[cfg(feature = "std")] #[cfg(feature = "raw_value")] impl<'a, W: io::Write, F: Formatter> ser::Serializer for RawValueStrEmitter<'a, W, F> { type Ok = (); @@ -1608,6 +1643,7 @@ impl CharEscape { /// This trait abstracts away serializing the JSON control characters, which allows the user to /// optionally pretty print the JSON output. +#[cfg(feature = "std")] pub trait Formatter { /// Writes a `null` value to the specified writer. #[inline] @@ -1920,6 +1956,319 @@ pub trait Formatter { writer.write_all(fragment.as_bytes()) } } +/// +/// This trait abstracts away serializing the JSON control characters, which allows the user to +/// optionally pretty print the JSON output. +#[cfg(not(feature = "std"))] +pub trait Formatter { + /// Writes a `null` value to the specified writer. + #[inline] + fn write_null(&mut self, writer: &mut W) -> fmt::Result + where + W: fmt::Write, + { + writer.write_str("null") + } + + /// Writes a `true` or `false` value to the specified writer. + #[inline] + fn write_bool(&mut self, writer: &mut W, value: bool) -> fmt::Result + where + W: fmt::Write, + { + let s = if value { "true" } else { "false" }; + writer.write_str(s) + } + + /// Writes an integer value like `-123` to the specified writer. + #[inline] + fn write_i8(&mut self, writer: &mut W, value: i8) -> fmt::Result + where + W: fmt::Write, + { + itoa::fmt(writer, value).map(drop) + } + + /// Writes an integer value like `-123` to the specified writer. + #[inline] + fn write_i16(&mut self, writer: &mut W, value: i16) -> fmt::Result + where + W: fmt::Write, + { + itoa::fmt(writer, value).map(drop) + } + + /// Writes an integer value like `-123` to the specified writer. + #[inline] + fn write_i32(&mut self, writer: &mut W, value: i32) -> fmt::Result + where + W: fmt::Write, + { + itoa::fmt(writer, value).map(drop) + } + + /// Writes an integer value like `-123` to the specified writer. + #[inline] + fn write_i64(&mut self, writer: &mut W, value: i64) -> fmt::Result + where + W: fmt::Write, + { + itoa::fmt(writer, value).map(drop) + } + + /// Writes an integer value like `123` to the specified writer. + #[inline] + fn write_u8(&mut self, writer: &mut W, value: u8) -> fmt::Result + where + W: fmt::Write, + { + itoa::fmt(writer, value).map(drop) + } + + /// Writes an integer value like `123` to the specified writer. + #[inline] + fn write_u16(&mut self, writer: &mut W, value: u16) -> fmt::Result + where + W: fmt::Write, + { + itoa::fmt(writer, value).map(drop) + } + + /// Writes an integer value like `123` to the specified writer. + #[inline] + fn write_u32(&mut self, writer: &mut W, value: u32) -> fmt::Result + where + W: fmt::Write, + { + itoa::fmt(writer, value).map(drop) + } + + /// Writes an integer value like `123` to the specified writer. + #[inline] + fn write_u64(&mut self, writer: &mut W, value: u64) -> fmt::Result + where + W: fmt::Write, + { + itoa::fmt(writer, value).map(drop) + } + + /// Writes a floating point value like `-31.26e+12` to the specified writer. + #[inline] + fn write_f32(&mut self, writer: &mut W, value: f32) -> fmt::Result + where + W: fmt::Write, + { + let mut buffer = ryu::Buffer::new(); + let s = buffer.format(value); + writer.write_str(s) + } + + /// Writes a floating point value like `-31.26e+12` to the specified writer. + #[inline] + fn write_f64(&mut self, writer: &mut W, value: f64) -> fmt::Result + where + W: fmt::Write, + { + let mut buffer = ryu::Buffer::new(); + let s = buffer.format(value); + writer.write_str(s) + } + + /// Writes a number that has already been rendered to a string. + #[inline] + fn write_number_str(&mut self, writer: &mut W, value: &str) -> fmt::Result + where + W: fmt::Write, + { + writer.write_str(value) + } + + /// Called before each series of `write_string_fragment` and + /// `write_char_escape`. Writes a `"` to the specified writer. + #[inline] + fn begin_string(&mut self, writer: &mut W) -> fmt::Result + where + W: fmt::Write, + { + writer.write_str("\"") + } + + /// Called after each series of `write_string_fragment` and + /// `write_char_escape`. Writes a `"` to the specified writer. + #[inline] + fn end_string(&mut self, writer: &mut W) -> fmt::Result + where + W: fmt::Write, + { + writer.write_str("\"") + } + + /// Writes a string fragment that doesn't need any escaping to the + /// specified writer. + #[inline] + fn write_string_fragment(&mut self, writer: &mut W, fragment: &str) -> fmt::Result + where + W: fmt::Write, + { + writer.write_str(fragment) + } + + /// Writes a character escape code to the specified writer. + #[inline] + fn write_char_escape( + &mut self, + writer: &mut W, + char_escape: CharEscape, + ) -> fmt::Result + where + W: fmt::Write, + { + use self::CharEscape::*; + + let s = match char_escape { + Quote => "\\\"", + ReverseSolidus => "\\\\", + Solidus => "\\/", + Backspace => "\\b", + FormFeed => "\\f", + LineFeed => "\\n", + CarriageReturn => "\\r", + Tab => "\\t", + AsciiControl(byte) => { + static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef"; + let bytes = &[ + b'\\', + b'u', + b'0', + b'0', + HEX_DIGITS[(byte >> 4) as usize], + HEX_DIGITS[(byte & 0xF) as usize], + ]; + let bytes = str::from_utf8(bytes).or(Err(fmt::Error))?; + return writer.write_str(bytes); + } + }; + + writer.write_str(s) + } + + /// Called before every array. Writes a `[` to the specified + /// writer. + #[inline] + fn begin_array(&mut self, writer: &mut W) -> fmt::Result + where + W: fmt::Write, + { + writer.write_str("[") + } + + /// Called after every array. Writes a `]` to the specified + /// writer. + #[inline] + fn end_array(&mut self, writer: &mut W) -> fmt::Result + where + W: fmt::Write, + { + writer.write_str("]") + } + + /// Called before every array value. Writes a `,` if needed to + /// the specified writer. + #[inline] + fn begin_array_value(&mut self, writer: &mut W, first: bool) -> fmt::Result + where + W: fmt::Write, + { + if first { + Ok(()) + } else { + writer.write_str(",") + } + } + + /// Called after every array value. + #[inline] + fn end_array_value(&mut self, _writer: &mut W) -> fmt::Result + where + W: fmt::Write, + { + Ok(()) + } + + /// Called before every object. Writes a `{` to the specified + /// writer. + #[inline] + fn begin_object(&mut self, writer: &mut W) -> fmt::Result + where + W: fmt::Write, + { + writer.write_str("{") + } + + /// Called after every object. Writes a `}` to the specified + /// writer. + #[inline] + fn end_object(&mut self, writer: &mut W) -> fmt::Result + where + W: fmt::Write, + { + writer.write_str("}") + } + + /// Called before every object key. + #[inline] + fn begin_object_key(&mut self, writer: &mut W, first: bool) -> fmt::Result + where + W: fmt::Write, + { + if first { + Ok(()) + } else { + writer.write_str(",") + } + } + + /// Called after every object key. A `:` should be written to the + /// specified writer by either this method or + /// `begin_object_value`. + #[inline] + fn end_object_key(&mut self, _writer: &mut W) -> fmt::Result + where + W: fmt::Write, + { + Ok(()) + } + + /// Called before every object value. A `:` should be written to + /// the specified writer by either this method or + /// `end_object_key`. + #[inline] + fn begin_object_value(&mut self, writer: &mut W) -> fmt::Result + where + W: fmt::Write, + { + writer.write_str(":") + } + + /// Called after every object value. + #[inline] + fn end_object_value(&mut self, _writer: &mut W) -> fmt::Result + where + W: fmt::Write, + { + Ok(()) + } + + /// Writes a raw JSON fragment that doesn't need any escaping to the + /// specified writer. + #[inline] + fn write_raw_fragment(&mut self, writer: &mut W, fragment: &str) -> fmt::Result + where + W: fmt::Write, + { + writer.write_str(fragment) + } +} /// This structure compacts a JSON value with no extra whitespace. #[derive(Clone, Debug)] @@ -1928,6 +2277,7 @@ pub struct CompactFormatter; impl Formatter for CompactFormatter {} /// This structure pretty prints a JSON value to make it human readable. +#[cfg(feature = "std")] #[derive(Clone, Debug)] pub struct PrettyFormatter<'a> { current_indent: usize, @@ -1935,6 +2285,7 @@ pub struct PrettyFormatter<'a> { indent: &'a [u8], } +#[cfg(feature = "std")] impl<'a> PrettyFormatter<'a> { /// Construct a pretty printer formatter that defaults to using two spaces for indentation. pub fn new() -> Self { @@ -1951,12 +2302,14 @@ impl<'a> PrettyFormatter<'a> { } } +#[cfg(feature = "std")] impl<'a> Default for PrettyFormatter<'a> { fn default() -> Self { PrettyFormatter::new() } } +#[cfg(feature = "std")] impl<'a> Formatter for PrettyFormatter<'a> { #[inline] fn begin_array(&mut self, writer: &mut W) -> io::Result<()> @@ -2066,9 +2419,9 @@ fn format_escaped_str( writer: &mut W, formatter: &mut F, value: &str, -) -> io::Result<()> +) -> IoResult where - W: io::Write, + W: WriteTrait, F: Formatter, { try!(formatter.begin_string(writer)); @@ -2081,9 +2434,9 @@ fn format_escaped_str_contents( writer: &mut W, formatter: &mut F, value: &str, -) -> io::Result<()> +) -> IoResult where - W: io::Write, + W: WriteTrait, F: Formatter, { let bytes = value.as_bytes(); @@ -2154,7 +2507,7 @@ static ESCAPE: [u8; 256] = [ #[inline] pub fn to_writer(writer: W, value: &T) -> Result<()> where - W: io::Write, + W: WriteTrait, T: Serialize, { let mut ser = Serializer::new(writer); @@ -2169,6 +2522,7 @@ where /// /// Serialization can fail if `T`'s implementation of `Serialize` decides to /// fail, or if `T` contains a map with non-string keys. +#[cfg(feature = "std")] #[inline] pub fn to_writer_pretty(writer: W, value: &T) -> Result<()> where @@ -2180,12 +2534,31 @@ where Ok(()) } +/// Serialize the given data structure as pretty-printed JSON into the IO +/// stream. +/// +/// # Errors +/// +/// Serialization can fail if `T`'s implementation of `Serialize` decides to +/// fail, or if `T` contains a map with non-string keys. +// Currently no_std does not support to_writer_pretty. This is a workaround for types that +// implement alternate `{:#?}`. +#[cfg(not(feature = "std"))] +pub fn to_writer_pretty(writer: W, value: &T) -> Result<()> +where + W: WriteTrait, + T: Serialize, +{ + to_writer(writer, value) +} + /// Serialize the given data structure as a JSON byte vector. /// /// # Errors /// /// Serialization can fail if `T`'s implementation of `Serialize` decides to /// fail, or if `T` contains a map with non-string keys. +#[cfg(feature = "std")] #[inline] pub fn to_vec(value: &T) -> Result> where @@ -2196,12 +2569,30 @@ where Ok(writer) } +/// Serialize the given data structure as a JSON byte vector. +/// +/// # Errors +/// +/// Serialization can fail if `T`'s implementation of `Serialize` decides to +/// fail, or if `T` contains a map with non-string keys. +#[cfg(not(feature = "std"))] +#[inline] +pub fn to_vec(value: &T) -> Result> +where + T: Serialize, +{ + let mut writer = String::with_capacity(128); + try!(to_writer(&mut writer, value)); + Ok(writer.into()) +} + /// Serialize the given data structure as a pretty-printed JSON byte vector. /// /// # Errors /// /// Serialization can fail if `T`'s implementation of `Serialize` decides to /// fail, or if `T` contains a map with non-string keys. +#[cfg(feature = "std")] #[inline] pub fn to_vec_pretty(value: &T) -> Result> where @@ -2237,6 +2628,7 @@ where /// /// Serialization can fail if `T`'s implementation of `Serialize` decides to /// fail, or if `T` contains a map with non-string keys. +#[cfg(feature = "std")] #[inline] pub fn to_string_pretty(value: &T) -> Result where @@ -2250,6 +2642,7 @@ where Ok(string) } +#[cfg(feature = "std")] fn indent(wr: &mut W, n: usize, s: &[u8]) -> io::Result<()> where W: io::Write, diff --git a/src/value/de.rs b/src/value/de.rs index a1f40cf92..28b1ff2c2 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -1,7 +1,22 @@ +#[cfg(not(feature = "std"))] +use alloc::borrow::Cow; +#[cfg(not(feature = "std"))] +use alloc::vec; +#[cfg(not(feature = "std"))] +use core::fmt; +#[cfg(not(feature = "std"))] +use core::slice; +#[cfg(not(feature = "std"))] +use core::str; +#[cfg(feature = "std")] use std::borrow::Cow; +#[cfg(feature = "std")] use std::fmt; +#[cfg(feature = "std")] use std::slice; +#[cfg(feature = "std")] use std::str; +#[cfg(feature = "std")] use std::vec; use serde; @@ -20,6 +35,9 @@ use serde::de; #[cfg(feature = "arbitrary_precision")] use number::NumberFromString; +#[cfg(not(feature = "std"))] +use alloc::prelude::{String, ToOwned, Vec}; + impl<'de> Deserialize<'de> for Value { #[inline] fn deserialize(deserializer: D) -> Result diff --git a/src/value/from.rs b/src/value/from.rs index d647deae8..8b7d18132 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -1,9 +1,19 @@ +#[cfg(not(feature = "std"))] +use core::iter::FromIterator; +#[cfg(feature = "std")] use std::borrow::Cow; +#[cfg(feature = "std")] +use std::iter::FromIterator; use super::Value; use map::Map; use number::Number; +#[cfg(not(feature = "std"))] +use alloc::borrow::Cow; +#[cfg(not(feature = "std"))] +use alloc::prelude::{String, ToString, Vec}; + macro_rules! from_integer { ($($ty:ident)*) => { $( @@ -182,7 +192,7 @@ impl<'a, T: Clone + Into> From<&'a [T]> for Value { } } -impl> ::std::iter::FromIterator for Value { +impl> FromIterator for Value { /// Convert an iteratable type to a `Value` /// /// # Examples diff --git a/src/value/index.rs b/src/value/index.rs index 47990a547..6ec55f85e 100644 --- a/src/value/index.rs +++ b/src/value/index.rs @@ -1,9 +1,18 @@ +#[cfg(not(feature = "std"))] +use core::fmt; +#[cfg(not(feature = "std"))] +use core::ops; +#[cfg(feature = "std")] use std::fmt; +#[cfg(feature = "std")] use std::ops; use super::Value; use map::Map; +#[cfg(not(feature = "std"))] +use alloc::prelude::{String, ToOwned}; + /// A type that can be used to index into a `serde_json::Value`. /// /// The [`get`] and [`get_mut`] methods of `Value` accept any type that @@ -132,6 +141,9 @@ where // Prevent users from implementing the Index trait. mod private { + #[cfg(not(feature = "std"))] + use alloc::prelude::String; + pub trait Sealed {} impl Sealed for usize {} impl Sealed for str {} diff --git a/src/value/mod.rs b/src/value/mod.rs index a97144f1e..f83db8e4d 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -92,9 +92,19 @@ //! [from_slice]: https://docs.serde.rs/serde_json/de/fn.from_slice.html //! [from_reader]: https://docs.serde.rs/serde_json/de/fn.from_reader.html +#[cfg(not(feature = "std"))] +use core::fmt::{self, Debug}; +#[cfg(not(feature = "std"))] +use core::mem; +#[cfg(not(feature = "std"))] +use core::str; +#[cfg(feature = "std")] use std::fmt::{self, Debug}; +#[cfg(feature = "std")] use std::io; +#[cfg(feature = "std")] use std::mem; +#[cfg(feature = "std")] use std::str; use serde::de::DeserializeOwned; @@ -111,6 +121,9 @@ pub use self::index::Index; use self::ser::Serializer; +#[cfg(not(feature = "std"))] +use alloc::prelude::{String, Vec}; + /// Represents any valid JSON value. /// /// See the `serde_json::value` module documentation for usage examples. @@ -194,6 +207,7 @@ struct WriterFormatter<'a, 'b: 'a> { inner: &'a mut fmt::Formatter<'b>, } +#[cfg(feature = "std")] impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> { fn write(&mut self, buf: &[u8]) -> io::Result { fn io_error(_: E) -> io::Error { @@ -211,6 +225,14 @@ impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> { } } +#[cfg(not(feature = "std"))] +impl<'a, 'b> fmt::Write for WriterFormatter<'a, 'b> { + fn write_str(&mut self, s: &str) -> fmt::Result { + try!(self.inner.write_str(s)); + Ok(()) + } +} + impl fmt::Display for Value { /// Display a JSON value as a string. /// @@ -930,28 +952,6 @@ mod ser; /// "location": "Menlo Park, CA", /// }); /// -/// let v = serde_json::to_value(u).unwrap(); -/// assert_eq!(v, expected); -/// -/// Ok(()) -/// } -/// # -/// # fn main() { -/// # compare_json_values().unwrap(); -/// # } -/// ``` -/// -/// # Errors -/// -/// This conversion can fail if `T`'s implementation of `Serialize` decides to -/// fail, or if `T` contains a map with non-string keys. -/// -/// ```edition2018 -/// use std::collections::BTreeMap; -/// -/// fn main() { -/// // The keys in this map are vectors, not strings. -/// let mut map = BTreeMap::new(); /// map.insert(vec![32, 64], "x86"); /// /// println!("{}", serde_json::to_value(map).unwrap_err()); diff --git a/src/value/partial_eq.rs b/src/value/partial_eq.rs index cfcf95706..f4ac2687b 100644 --- a/src/value/partial_eq.rs +++ b/src/value/partial_eq.rs @@ -1,5 +1,8 @@ use super::Value; +#[cfg(not(feature = "std"))] +use alloc::prelude::String; + fn eq_i64(value: &Value, other: i64) -> bool { value.as_i64().map_or(false, |i| i == other) } diff --git a/src/value/ser.rs b/src/value/ser.rs index b0e09beb5..9d57eb511 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -6,6 +6,9 @@ use map::Map; use number::Number; use value::{to_value, Value}; +#[cfg(not(feature = "std"))] +use alloc::prelude::{String, ToOwned, ToString, Vec}; + impl Serialize for Value { #[inline] fn serialize(&self, serializer: S) -> Result