From 85f2728d9e2c6040ac3a800ee9d30d6c331f3190 Mon Sep 17 00:00:00 2001 From: Jeff Vander Stoep Date: Mon, 5 Feb 2024 08:46:02 +0100 Subject: Upgrade serde_json to 1.0.113 This project was upgraded with external_updater. Usage: tools/external_updater/updater.sh update external/rust/crates/serde_json For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md Test: TreeHugger Change-Id: I4091b2292a767bd0120c88d4f585c7f2c55fd189 --- .cargo_vcs_info.json | 2 +- .github/workflows/ci.yml | 57 +++++---- Android.bp | 4 +- Cargo.toml | 31 ++--- Cargo.toml.orig | 35 +++--- METADATA | 25 ++-- README.md | 2 +- build.rs | 39 +----- src/de.rs | 287 ++++++++++++++++++++++++++---------------- src/error.rs | 126 ++++++++++++++++--- src/io/core.rs | 2 +- src/lexical/algorithm.rs | 5 +- src/lexical/digit.rs | 5 +- src/lexical/errors.rs | 3 +- src/lexical/math.rs | 4 +- src/lexical/num.rs | 2 +- src/lib.rs | 22 ++-- src/macros.rs | 3 +- src/map.rs | 121 ++++++++++++------ src/number.rs | 68 +++++++++- src/raw.rs | 268 +++++++++++++++++++++++++++++++++++++-- src/read.rs | 2 +- src/ser.rs | 122 ++++++++++++++---- src/value/de.rs | 90 +++++++++---- src/value/from.rs | 37 +++--- src/value/index.rs | 2 +- src/value/mod.rs | 30 ++++- src/value/partial_eq.rs | 14 ++- src/value/ser.rs | 39 +++--- tests/lexical.rs | 5 - tests/lexical/parse.rs | 2 +- tests/map.rs | 1 - tests/regression/issue1004.rs | 12 ++ tests/regression/issue845.rs | 1 - tests/test.rs | 224 +++++++++++++++++++++++---------- tests/ui/parse_key.stderr | 2 +- 36 files changed, 1216 insertions(+), 478 deletions(-) create mode 100644 tests/regression/issue1004.rs diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index d3c2d3e..fa385b5 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,6 @@ { "git": { - "sha1": "a15bd0968639884ec7b73107360d58fd655e2071" + "sha1": "09d865b34b9701be52764dc9bf571b1a16e9d3dc" }, "path_in_vcs": "" } \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bfaffaf..d997982 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,7 @@ name: CI on: push: pull_request: + workflow_dispatch: schedule: [cron: "40 1 * * *"] permissions: @@ -21,7 +22,7 @@ jobs: os: [ubuntu, windows] timeout-minutes: 45 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - run: cargo test - run: cargo test --features preserve_order --tests -- --skip ui --exact @@ -37,7 +38,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [beta, 1.56.1, 1.53.0, 1.46.0, 1.40.0, 1.38.0, 1.36.0] + rust: [beta, 1.64.0, 1.56.1] os: [ubuntu] include: - rust: stable @@ -47,27 +48,37 @@ jobs: os: windows timeout-minutes: 45 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} targets: ${{matrix.target}} - - run: cargo check - - run: cargo check --features float_roundtrip - - run: cargo check --features arbitrary_precision - - run: cargo check --features raw_value - - run: cargo check --features unbounded_depth + - run: cargo check --manifest-path tests/crate/Cargo.toml + - run: cargo check --manifest-path tests/crate/Cargo.toml --features float_roundtrip + - run: cargo check --manifest-path tests/crate/Cargo.toml --features arbitrary_precision + - run: cargo check --manifest-path tests/crate/Cargo.toml --features raw_value + - run: cargo check --manifest-path tests/crate/Cargo.toml --features unbounded_depth - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,arbitrary_precision - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,raw_value - - run: cargo check --features preserve_order - if: matrix.rust != '1.53.0' && matrix.rust != '1.46.0' && matrix.rust != '1.45.0' && matrix.rust != '1.40.0' && matrix.rust != '1.38.0' && matrix.rust != '1.36.0' - - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,preserve_order - if: matrix.rust != '1.53.0' && matrix.rust != '1.46.0' && matrix.rust != '1.45.0' && matrix.rust != '1.40.0' && matrix.rust != '1.38.0' && matrix.rust != '1.36.0' + - run: cargo check --manifest-path tests/crate/Cargo.toml --features serde_json/preserve_order + if: matrix.rust != '1.56.1' + - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,serde_json/preserve_order + if: matrix.rust != '1.56.1' - name: Build without std run: cargo check --manifest-path tests/crate/Cargo.toml --target ${{matrix.target}} --no-default-features --features alloc if: matrix.target + minimal: + name: Minimal versions + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - run: cargo generate-lockfile -Z minimal-versions + - run: cargo check --locked + miri: name: Miri runs-on: ubuntu-latest @@ -75,8 +86,9 @@ jobs: MIRIFLAGS: -Zmiri-strict-provenance timeout-minutes: 45 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@miri + - run: cargo miri setup - run: cargo miri test - run: cargo miri test --features preserve_order,float_roundtrip,arbitrary_precision,raw_value @@ -86,28 +98,29 @@ jobs: if: github.event_name != 'pull_request' timeout-minutes: 45 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@clippy - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic - run: cargo clippy --all-features --tests -- -Dclippy::all -Dclippy::pedantic - docs: + doc: name: Documentation runs-on: ubuntu-latest timeout-minutes: 45 + env: + RUSTDOCFLAGS: -Dwarnings steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - - run: cargo doc --features raw_value,unbounded_depth - env: - RUSTDOCFLAGS: --cfg docsrs + - uses: dtolnay/install@cargo-docs-rs + - run: cargo docs-rs fuzz: name: Fuzz runs-on: ubuntu-latest timeout-minutes: 45 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - uses: dtolnay/install@cargo-fuzz - run: cargo fuzz check @@ -118,7 +131,7 @@ jobs: if: github.event_name != 'pull_request' timeout-minutes: 45 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/install@cargo-outdated - - run: cargo outdated --workspace --exit-code 1 + - run: cargo outdated --exit-code 1 - run: cargo outdated --manifest-path fuzz/Cargo.toml --exit-code 1 diff --git a/Android.bp b/Android.bp index d448340..678bf00 100644 --- a/Android.bp +++ b/Android.bp @@ -43,9 +43,9 @@ rust_library { host_supported: true, crate_name: "serde_json", cargo_env_compat: true, - cargo_pkg_version: "1.0.94", + cargo_pkg_version: "1.0.113", srcs: ["src/lib.rs"], - edition: "2018", + edition: "2021", features: [ "default", "std", diff --git a/Cargo.toml b/Cargo.toml index fc2253d..447641d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,10 +10,10 @@ # See Cargo.toml.orig for the original contents. [package] -edition = "2018" -rust-version = "1.36" +edition = "2021" +rust-version = "1.56" name = "serde_json" -version = "1.0.94" +version = "1.0.113" authors = [ "Erick Tryzelaar ", "David Tolnay ", @@ -36,12 +36,14 @@ repository = "https://github.com/serde-rs/json" [package.metadata.docs.rs] features = [ + "preserve_order", "raw_value", "unbounded_depth", ] rustdoc-args = [ "--cfg", "docsrs", + "--generate-link-to-definition", ] targets = ["x86_64-unknown-linux-gnu"] @@ -52,8 +54,7 @@ features = ["raw_value"] doc-scrape-examples = false [dependencies.indexmap] -version = "1.5.2" -features = ["std"] +version = "2.2.1" optional = true [dependencies.itoa] @@ -63,36 +64,36 @@ version = "1.0" version = "1.0" [dependencies.serde] -version = "1.0.100" +version = "1.0.194" default-features = false [dev-dependencies.automod] -version = "1.0" +version = "1.0.11" [dev-dependencies.indoc] -version = "2.0" +version = "2.0.2" [dev-dependencies.ref-cast] -version = "1.0" +version = "1.0.18" [dev-dependencies.rustversion] -version = "1.0" +version = "1.0.13" [dev-dependencies.serde] -version = "1.0.100" +version = "1.0.194" features = ["derive"] [dev-dependencies.serde_bytes] -version = "0.11" +version = "0.11.10" [dev-dependencies.serde_derive] -version = "1.0" +version = "1.0.166" [dev-dependencies.serde_stacker] -version = "0.1" +version = "0.1.8" [dev-dependencies.trybuild] -version = "1.0.49" +version = "1.0.81" features = ["diff"] [features] diff --git a/Cargo.toml.orig b/Cargo.toml.orig index ee54284..cedde61 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,43 +1,40 @@ [package] name = "serde_json" -version = "1.0.94" # remember to update html_root_url +version = "1.0.113" authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" documentation = "https://docs.rs/serde_json" -edition = "2018" +edition = "2021" keywords = ["json", "serde", "serialization"] license = "MIT OR Apache-2.0" repository = "https://github.com/serde-rs/json" -rust-version = "1.36" +rust-version = "1.56" [dependencies] -serde = { version = "1.0.100", default-features = false } -indexmap = { version = "1.5.2", features = ["std"], optional = true } +indexmap = { version = "2.2.1", optional = true } itoa = "1.0" ryu = "1.0" +serde = { version = "1.0.194", default-features = false } [dev-dependencies] -automod = "1.0" -indoc = "2.0" -ref-cast = "1.0" -rustversion = "1.0" -serde = { version = "1.0.100", features = ["derive"] } -serde_bytes = "0.11" -serde_derive = "1.0" -serde_stacker = "0.1" -trybuild = { version = "1.0.49", features = ["diff"] } - -[workspace] -members = ["tests/crate"] +automod = "1.0.11" +indoc = "2.0.2" +ref-cast = "1.0.18" +rustversion = "1.0.13" +serde = { version = "1.0.194", features = ["derive"] } +serde_bytes = "0.11.10" +serde_derive = "1.0.166" +serde_stacker = "0.1.8" +trybuild = { version = "1.0.81", features = ["diff"] } [lib] doc-scrape-examples = false [package.metadata.docs.rs] -features = ["raw_value", "unbounded_depth"] +features = ["preserve_order", "raw_value", "unbounded_depth"] targets = ["x86_64-unknown-linux-gnu"] -rustdoc-args = ["--cfg", "docsrs"] +rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] [package.metadata.playground] features = ["raw_value"] diff --git a/METADATA b/METADATA index 9fece5b..a4bf6a6 100644 --- a/METADATA +++ b/METADATA @@ -1,23 +1,20 @@ # This project was upgraded with external_updater. -# Usage: tools/external_updater/updater.sh update rust/crates/serde_json -# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md +# Usage: tools/external_updater/updater.sh update external/rust/crates/serde_json +# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md name: "serde_json" description: "A JSON serialization file format" third_party { - url { - type: HOMEPAGE - value: "https://crates.io/crates/serde_json" - } - url { - type: ARCHIVE - value: "https://static.crates.io/crates/serde_json/serde_json-1.0.94.crate" - } - version: "1.0.94" license_type: NOTICE last_upgrade_date { - year: 2023 - month: 3 - day: 20 + year: 2024 + month: 2 + day: 5 + } + homepage: "https://crates.io/crates/serde_json" + identifier { + type: "Archive" + value: "https://static.crates.io/crates/serde_json/serde_json-1.0.113.crate" + version: "1.0.113" } } diff --git a/README.md b/README.md index d704979..a3ba288 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ enum Value { A string of JSON data can be parsed into a `serde_json::Value` by the [`serde_json::from_str`][from_str] function. There is also -[`from_slice`][from_slice] for parsing from a byte slice &[u8] and +[`from_slice`][from_slice] for parsing from a byte slice &\[u8\] and [`from_reader`][from_reader] for parsing from any `io::Read` like a File or a TCP stream. diff --git a/build.rs b/build.rs index 0e12602..dd09e62 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,4 @@ use std::env; -use std::process::Command; -use std::str::{self, FromStr}; fn main() { println!("cargo:rerun-if-changed=build.rs"); @@ -9,46 +7,11 @@ fn main() { // src/lexical/math.rs for where this has an effect. let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); match target_arch.as_str() { - "aarch64" | "mips64" | "powerpc64" | "x86_64" => { + "aarch64" | "mips64" | "powerpc64" | "x86_64" | "loongarch64" => { println!("cargo:rustc-cfg=limb_width_64"); } _ => { println!("cargo:rustc-cfg=limb_width_32"); } } - - let minor = match rustc_minor_version() { - Some(minor) => minor, - None => return, - }; - - // BTreeMap::get_key_value - // https://blog.rust-lang.org/2019/12/19/Rust-1.40.0.html#additions-to-the-standard-library - if minor < 40 { - println!("cargo:rustc-cfg=no_btreemap_get_key_value"); - } - - // BTreeMap::remove_entry - // https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#library-changes - if minor < 45 { - println!("cargo:rustc-cfg=no_btreemap_remove_entry"); - } - - // BTreeMap::retain - // https://blog.rust-lang.org/2021/06/17/Rust-1.53.0.html#stabilized-apis - if minor < 53 { - println!("cargo:rustc-cfg=no_btreemap_retain"); - } -} - -fn rustc_minor_version() -> Option { - let rustc = env::var_os("RUSTC")?; - let output = Command::new(rustc).arg("--version").output().ok()?; - let version = str::from_utf8(&output.stdout).ok()?; - let mut pieces = version.split('.'); - if pieces.next() != Some("rustc 1") { - return None; - } - let next = pieces.next()?; - u32::from_str(next).ok() } diff --git a/src/de.rs b/src/de.rs index 88d0f26..7154f84 100644 --- a/src/de.rs +++ b/src/de.rs @@ -22,6 +22,7 @@ use crate::number::NumberDeserializer; pub use crate::read::{Read, SliceRead, StrRead}; #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub use crate::read::IoRead; ////////////////////////////////////////////////////////////////////////////// @@ -209,7 +210,7 @@ impl<'de, R: Read<'de>> Deserializer { self.disable_recursion_limit = true; } - fn peek(&mut self) -> Result> { + pub(crate) fn peek(&mut self) -> Result> { self.read.peek() } @@ -248,7 +249,7 @@ impl<'de, R: Read<'de>> Deserializer { fn parse_whitespace(&mut self) -> Result> { loop { match tri!(self.peek()) { - Some(b' ') | Some(b'\n') | Some(b'\t') | Some(b'\r') => { + Some(b' ' | b'\n' | b'\t' | b'\r') => { self.eat_char(); } other => { @@ -309,9 +310,9 @@ impl<'de, R: Read<'de>> Deserializer { self.fix_position(err) } - fn deserialize_number(&mut self, visitor: V) -> Result + pub(crate) fn deserialize_number<'any, V>(&mut self, visitor: V) -> Result where - V: de::Visitor<'de>, + V: de::Visitor<'any>, { let peek = match tri!(self.parse_whitespace()) { Some(b) => b, @@ -335,6 +336,79 @@ impl<'de, R: Read<'de>> Deserializer { } } + #[cfg(feature = "float_roundtrip")] + pub(crate) fn do_deserialize_f32<'any, V>(&mut self, visitor: V) -> Result + where + V: de::Visitor<'any>, + { + self.single_precision = true; + let val = self.deserialize_number(visitor); + self.single_precision = false; + val + } + + pub(crate) fn do_deserialize_i128<'any, V>(&mut self, visitor: V) -> Result + where + V: de::Visitor<'any>, + { + let mut buf = String::new(); + + match tri!(self.parse_whitespace()) { + Some(b'-') => { + self.eat_char(); + buf.push('-'); + } + Some(_) => {} + None => { + return Err(self.peek_error(ErrorCode::EofWhileParsingValue)); + } + }; + + tri!(self.scan_integer128(&mut buf)); + + let value = match buf.parse() { + Ok(int) => visitor.visit_i128(int), + Err(_) => { + return Err(self.error(ErrorCode::NumberOutOfRange)); + } + }; + + match value { + Ok(value) => Ok(value), + Err(err) => Err(self.fix_position(err)), + } + } + + pub(crate) fn do_deserialize_u128<'any, V>(&mut self, visitor: V) -> Result + where + V: de::Visitor<'any>, + { + match tri!(self.parse_whitespace()) { + Some(b'-') => { + return Err(self.peek_error(ErrorCode::NumberOutOfRange)); + } + Some(_) => {} + None => { + return Err(self.peek_error(ErrorCode::EofWhileParsingValue)); + } + } + + let mut buf = String::new(); + tri!(self.scan_integer128(&mut buf)); + + let value = match buf.parse() { + Ok(int) => visitor.visit_u128(int), + Err(_) => { + return Err(self.error(ErrorCode::NumberOutOfRange)); + } + }; + + match value { + Ok(value) => Ok(value), + Err(err) => Err(self.fix_position(err)), + } + } + fn scan_integer128(&mut self, buf: &mut String) -> Result<()> { match tri!(self.next_char_or_null()) { b'0' => { @@ -860,7 +934,7 @@ impl<'de, R: Read<'de>> Deserializer { if !positive { buf.push('-'); } - self.scan_integer(&mut buf)?; + tri!(self.scan_integer(&mut buf)); if positive { if let Ok(unsigned) = buf.parse() { return Ok(ParserNumber::U64(unsigned)); @@ -913,7 +987,7 @@ impl<'de, R: Read<'de>> Deserializer { fn scan_number(&mut self, buf: &mut String) -> Result<()> { match tri!(self.peek_or_null()) { b'.' => self.scan_decimal(buf), - e @ b'e' | e @ b'E' => self.scan_exponent(e as char, buf), + e @ (b'e' | b'E') => self.scan_exponent(e as char, buf), _ => Ok(()), } } @@ -938,7 +1012,7 @@ impl<'de, R: Read<'de>> Deserializer { } match tri!(self.peek_or_null()) { - e @ b'e' | e @ b'E' => self.scan_exponent(e as char, buf), + e @ (b'e' | b'E') => self.scan_exponent(e as char, buf), _ => Ok(()), } } @@ -1059,7 +1133,7 @@ impl<'de, R: Read<'de>> Deserializer { tri!(self.read.ignore_str()); None } - frame @ b'[' | frame @ b'{' => { + frame @ (b'[' | b'{') => { self.scratch.extend(enclosing.take()); self.eat_char(); Some(frame) @@ -1204,9 +1278,9 @@ impl<'de, R: Read<'de>> Deserializer { where V: de::Visitor<'de>, { - self.parse_whitespace()?; + tri!(self.parse_whitespace()); self.read.begin_raw_buffering(); - self.ignore_value()?; + tri!(self.ignore_value()); self.read.end_raw_buffering(visitor) } } @@ -1258,11 +1332,15 @@ static POW10: [f64; 309] = [ macro_rules! deserialize_number { ($method:ident) => { + deserialize_number!($method, deserialize_number); + }; + + ($method:ident, $using:ident) => { fn $method(self, visitor: V) -> Result where V: de::Visitor<'de>, { - self.deserialize_number(visitor) + self.$using(visitor) } }; } @@ -1424,77 +1502,9 @@ impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { deserialize_number!(deserialize_f64); #[cfg(feature = "float_roundtrip")] - fn deserialize_f32(self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - self.single_precision = true; - let val = self.deserialize_number(visitor); - self.single_precision = false; - val - } - - fn deserialize_i128(self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - let mut buf = String::new(); - - match tri!(self.parse_whitespace()) { - Some(b'-') => { - self.eat_char(); - buf.push('-'); - } - Some(_) => {} - None => { - return Err(self.peek_error(ErrorCode::EofWhileParsingValue)); - } - }; - - tri!(self.scan_integer128(&mut buf)); - - let value = match buf.parse() { - Ok(int) => visitor.visit_i128(int), - Err(_) => { - return Err(self.error(ErrorCode::NumberOutOfRange)); - } - }; - - match value { - Ok(value) => Ok(value), - Err(err) => Err(self.fix_position(err)), - } - } - - fn deserialize_u128(self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - match tri!(self.parse_whitespace()) { - Some(b'-') => { - return Err(self.peek_error(ErrorCode::NumberOutOfRange)); - } - Some(_) => {} - None => { - return Err(self.peek_error(ErrorCode::EofWhileParsingValue)); - } - } - - let mut buf = String::new(); - tri!(self.scan_integer128(&mut buf)); - - let value = match buf.parse() { - Ok(int) => visitor.visit_u128(int), - Err(_) => { - return Err(self.error(ErrorCode::NumberOutOfRange)); - } - }; - - match value { - Ok(value) => Ok(value), - Err(err) => Err(self.fix_position(err)), - } - } + deserialize_number!(deserialize_f32, do_deserialize_f32); + deserialize_number!(deserialize_i128, do_deserialize_i128); + deserialize_number!(deserialize_u128, do_deserialize_u128); fn deserialize_char(self, visitor: V) -> Result where @@ -2118,24 +2128,47 @@ struct MapKey<'a, R: 'a> { de: &'a mut Deserializer, } -macro_rules! deserialize_integer_key { - ($method:ident => $visit:ident) => { +macro_rules! deserialize_numeric_key { + ($method:ident) => { + fn $method(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + self.deserialize_number(visitor) + } + }; + + ($method:ident, $delegate:ident) => { fn $method(self, visitor: V) -> Result where V: de::Visitor<'de>, { self.de.eat_char(); - self.de.scratch.clear(); - let string = tri!(self.de.read.parse_str(&mut self.de.scratch)); - match (string.parse(), string) { - (Ok(integer), _) => visitor.$visit(integer), - (Err(_), Reference::Borrowed(s)) => visitor.visit_borrowed_str(s), - (Err(_), Reference::Copied(s)) => visitor.visit_str(s), + + match tri!(self.de.peek()) { + Some(b'0'..=b'9' | b'-') => {} + _ => return Err(self.de.error(ErrorCode::ExpectedNumericKey)), + } + + let value = tri!(self.de.$delegate(visitor)); + + match tri!(self.de.peek()) { + Some(b'"') => self.de.eat_char(), + _ => return Err(self.de.peek_error(ErrorCode::ExpectedDoubleQuote)), } + + Ok(value) } }; } +impl<'de, 'a, R> MapKey<'a, R> +where + R: Read<'de>, +{ + deserialize_numeric_key!(deserialize_number, deserialize_number); +} + impl<'de, 'a, R> de::Deserializer<'de> for MapKey<'a, R> where R: Read<'de>, @@ -2155,16 +2188,56 @@ where } } - deserialize_integer_key!(deserialize_i8 => visit_i8); - deserialize_integer_key!(deserialize_i16 => visit_i16); - deserialize_integer_key!(deserialize_i32 => visit_i32); - deserialize_integer_key!(deserialize_i64 => visit_i64); - deserialize_integer_key!(deserialize_i128 => visit_i128); - deserialize_integer_key!(deserialize_u8 => visit_u8); - deserialize_integer_key!(deserialize_u16 => visit_u16); - deserialize_integer_key!(deserialize_u32 => visit_u32); - deserialize_integer_key!(deserialize_u64 => visit_u64); - deserialize_integer_key!(deserialize_u128 => visit_u128); + deserialize_numeric_key!(deserialize_i8); + deserialize_numeric_key!(deserialize_i16); + deserialize_numeric_key!(deserialize_i32); + deserialize_numeric_key!(deserialize_i64); + deserialize_numeric_key!(deserialize_i128, deserialize_i128); + deserialize_numeric_key!(deserialize_u8); + deserialize_numeric_key!(deserialize_u16); + deserialize_numeric_key!(deserialize_u32); + deserialize_numeric_key!(deserialize_u64); + deserialize_numeric_key!(deserialize_u128, deserialize_u128); + #[cfg(not(feature = "float_roundtrip"))] + deserialize_numeric_key!(deserialize_f32); + #[cfg(feature = "float_roundtrip")] + deserialize_numeric_key!(deserialize_f32, deserialize_f32); + deserialize_numeric_key!(deserialize_f64); + + fn deserialize_bool(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + self.de.eat_char(); + + let peek = match tri!(self.de.next_char()) { + Some(b) => b, + None => { + return Err(self.de.peek_error(ErrorCode::EofWhileParsingValue)); + } + }; + + let value = match peek { + b't' => { + tri!(self.de.parse_ident(b"rue\"")); + visitor.visit_bool(true) + } + b'f' => { + tri!(self.de.parse_ident(b"alse\"")); + visitor.visit_bool(false) + } + _ => { + self.de.scratch.clear(); + let s = tri!(self.de.read.parse_str(&mut self.de.scratch)); + Err(de::Error::invalid_type(Unexpected::Str(&s), &visitor)) + } + }; + + match value { + Ok(value) => Ok(value), + Err(err) => Err(self.de.fix_position(err)), + } + } #[inline] fn deserialize_option(self, visitor: V) -> Result @@ -2221,8 +2294,8 @@ where } forward_to_deserialize_any! { - bool f32 f64 char str string unit unit_struct seq tuple tuple_struct map - struct identifier ignored_any + char str string unit unit_struct seq tuple tuple_struct map struct + identifier ignored_any } } @@ -2318,8 +2391,8 @@ where fn peek_end_of_value(&mut self) -> Result<()> { match tri!(self.de.peek()) { - Some(b' ') | Some(b'\n') | Some(b'\t') | Some(b'\r') | Some(b'"') | Some(b'[') - | Some(b']') | Some(b'{') | Some(b'}') | Some(b',') | Some(b':') | None => Ok(()), + Some(b' ' | b'\n' | b'\t' | b'\r' | b'"' | b'[' | b']' | b'{' | b'}' | b',' | b':') + | None => Ok(()), Some(_) => { let position = self.de.read.peek_position(); Err(Error::syntax( @@ -2369,7 +2442,7 @@ where if self_delineated_value { Ok(value) } else { - self.peek_end_of_value().map(|_| value) + self.peek_end_of_value().map(|()| value) } } Err(e) => { @@ -2408,9 +2481,9 @@ where Ok(value) } -/// Deserialize an instance of type `T` from an IO stream of JSON. +/// Deserialize an instance of type `T` from an I/O stream of JSON. /// -/// The content of the IO stream is deserialized directly from the stream +/// The content of the I/O stream is deserialized directly from the stream /// without being buffered in memory by serde_json. /// /// When reading from a source against which short reads are not efficient, such diff --git a/src/error.rs b/src/error.rs index 0898baf..fbf9eb1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,6 +9,8 @@ use core::str::FromStr; use serde::{de, ser}; #[cfg(feature = "std")] use std::error; +#[cfg(feature = "std")] +use std::io::ErrorKind; /// This type represents all possible errors that can occur when serializing or /// deserializing JSON data. @@ -36,15 +38,16 @@ impl Error { /// The first character in the input and any characters immediately /// following a newline character are in column 1. /// - /// Note that errors may occur in column 0, for example if a read from an IO - /// stream fails immediately following a previously read newline character. + /// Note that errors may occur in column 0, for example if a read from an + /// I/O stream fails immediately following a previously read newline + /// character. pub fn column(&self) -> usize { self.err.column } /// Categorizes the cause of this error. /// - /// - `Category::Io` - failure to read or write bytes on an IO stream + /// - `Category::Io` - failure to read or write bytes on an I/O stream /// - `Category::Syntax` - input that is not syntactically valid JSON /// - `Category::Data` - input data that is semantically incorrect /// - `Category::Eof` - unexpected end of the input data @@ -61,12 +64,15 @@ impl Error { | ErrorCode::ExpectedObjectCommaOrEnd | ErrorCode::ExpectedSomeIdent | ErrorCode::ExpectedSomeValue + | ErrorCode::ExpectedDoubleQuote | ErrorCode::InvalidEscape | ErrorCode::InvalidNumber | ErrorCode::NumberOutOfRange | ErrorCode::InvalidUnicodeCodePoint | ErrorCode::ControlCharacterWhileParsingString | ErrorCode::KeyMustBeAString + | ErrorCode::ExpectedNumericKey + | ErrorCode::FloatKeyMustBeFinite | ErrorCode::LoneLeadingSurrogateInHexEscape | ErrorCode::TrailingComma | ErrorCode::TrailingCharacters @@ -76,7 +82,7 @@ impl Error { } /// Returns true if this error was caused by a failure to read or write - /// bytes on an IO stream. + /// bytes on an I/O stream. pub fn is_io(&self) -> bool { self.classify() == Category::Io } @@ -104,12 +110,61 @@ impl Error { pub fn is_eof(&self) -> bool { self.classify() == Category::Eof } + + /// The kind reported by the underlying standard library I/O error, if this + /// error was caused by a failure to read or write bytes on an I/O stream. + /// + /// # Example + /// + /// ``` + /// use serde_json::Value; + /// use std::io::{self, ErrorKind, Read}; + /// use std::process; + /// + /// struct ReaderThatWillTimeOut<'a>(&'a [u8]); + /// + /// impl<'a> Read for ReaderThatWillTimeOut<'a> { + /// fn read(&mut self, buf: &mut [u8]) -> io::Result { + /// if self.0.is_empty() { + /// Err(io::Error::new(ErrorKind::TimedOut, "timed out")) + /// } else { + /// self.0.read(buf) + /// } + /// } + /// } + /// + /// fn main() { + /// let reader = ReaderThatWillTimeOut(br#" {"k": "#); + /// + /// let _: Value = match serde_json::from_reader(reader) { + /// Ok(value) => value, + /// Err(error) => { + /// if error.io_error_kind() == Some(ErrorKind::TimedOut) { + /// // Maybe this application needs to retry certain kinds of errors. + /// + /// # return; + /// } else { + /// eprintln!("error: {}", error); + /// process::exit(1); + /// } + /// } + /// }; + /// } + /// ``` + #[cfg(feature = "std")] + pub fn io_error_kind(&self) -> Option { + if let ErrorCode::Io(io_error) = &self.err.code { + Some(io_error.kind()) + } else { + None + } + } } /// Categorizes the cause of a `serde_json::Error`. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum Category { - /// The error was caused by a failure to read or write bytes on an IO + /// The error was caused by a failure to read or write bytes on an I/O /// stream. Io, @@ -134,8 +189,8 @@ pub enum Category { impl From for io::Error { /// Convert a `serde_json::Error` into an `io::Error`. /// - /// JSON syntax and data errors are turned into `InvalidData` IO errors. - /// EOF errors are turned into `UnexpectedEof` IO errors. + /// JSON syntax and data errors are turned into `InvalidData` I/O errors. + /// EOF errors are turned into `UnexpectedEof` I/O errors. /// /// ``` /// use std::io; @@ -165,8 +220,8 @@ impl From for io::Error { } else { match j.classify() { Category::Io => unreachable!(), - Category::Syntax | Category::Data => io::Error::new(io::ErrorKind::InvalidData, j), - Category::Eof => io::Error::new(io::ErrorKind::UnexpectedEof, j), + Category::Syntax | Category::Data => io::Error::new(ErrorKind::InvalidData, j), + Category::Eof => io::Error::new(ErrorKind::UnexpectedEof, j), } } } @@ -182,7 +237,7 @@ pub(crate) enum ErrorCode { /// Catchall for syntax error messages Message(Box), - /// Some IO error occurred while serializing or deserializing. + /// Some I/O error occurred while serializing or deserializing. Io(io::Error), /// EOF while parsing a list. @@ -212,6 +267,9 @@ pub(crate) enum ErrorCode { /// Expected this character to start a JSON value. ExpectedSomeValue, + /// Expected this character to be a `"`. + ExpectedDoubleQuote, + /// Invalid hex escape code. InvalidEscape, @@ -230,6 +288,12 @@ pub(crate) enum ErrorCode { /// Object key is not a string. KeyMustBeAString, + /// Contents of key were supposed to be a number. + ExpectedNumericKey, + + /// Object key is a non-finite float value. + FloatKeyMustBeFinite, + /// Lone leading surrogate in hex escape. LoneLeadingSurrogateInHexEscape, @@ -296,6 +360,7 @@ impl Display for ErrorCode { ErrorCode::ExpectedObjectCommaOrEnd => f.write_str("expected `,` or `}`"), ErrorCode::ExpectedSomeIdent => f.write_str("expected ident"), ErrorCode::ExpectedSomeValue => f.write_str("expected value"), + ErrorCode::ExpectedDoubleQuote => f.write_str("expected `\"`"), ErrorCode::InvalidEscape => f.write_str("invalid escape"), ErrorCode::InvalidNumber => f.write_str("invalid number"), ErrorCode::NumberOutOfRange => f.write_str("number out of range"), @@ -304,6 +369,12 @@ impl Display for ErrorCode { f.write_str("control character (\\u0000-\\u001F) found while parsing a string") } ErrorCode::KeyMustBeAString => f.write_str("key must be a string"), + ErrorCode::ExpectedNumericKey => { + f.write_str("invalid value: expected key to be a number in quotes") + } + ErrorCode::FloatKeyMustBeFinite => { + f.write_str("float key must be finite (got NaN or +/-inf)") + } ErrorCode::LoneLeadingSurrogateInHexEscape => { f.write_str("lone leading surrogate in hex escape") } @@ -367,11 +438,20 @@ impl de::Error for Error { #[cold] fn invalid_type(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self { - if let de::Unexpected::Unit = unexp { - Error::custom(format_args!("invalid type: null, expected {}", exp)) - } else { - Error::custom(format_args!("invalid type: {}, expected {}", unexp, exp)) - } + Error::custom(format_args!( + "invalid type: {}, expected {}", + JsonUnexpected(unexp), + exp, + )) + } + + #[cold] + fn invalid_value(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self { + Error::custom(format_args!( + "invalid value: {}, expected {}", + JsonUnexpected(unexp), + exp, + )) } } @@ -382,6 +462,22 @@ impl ser::Error for Error { } } +struct JsonUnexpected<'a>(de::Unexpected<'a>); + +impl<'a> Display for JsonUnexpected<'a> { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match self.0 { + de::Unexpected::Unit => formatter.write_str("null"), + de::Unexpected::Float(value) => write!( + formatter, + "floating point `{}`", + ryu::Buffer::new().format(value), + ), + unexp => Display::fmt(&unexp, formatter), + } + } +} + // Parse our own error message that looks like "{} at line {} column {}" to work // around erased-serde round-tripping the error through de::Error::custom. fn make_error(mut msg: String) -> Error { diff --git a/src/io/core.rs b/src/io/core.rs index 465ab8b..54c8ddf 100644 --- a/src/io/core.rs +++ b/src/io/core.rs @@ -9,7 +9,7 @@ pub enum ErrorKind { Other, } -// IO errors can never occur in no-std mode. All our no-std IO implementations +// I/O errors can never occur in no-std mode. All our no-std I/O implementations // are infallible. pub struct Error; diff --git a/src/lexical/algorithm.rs b/src/lexical/algorithm.rs index a2cbf18..eaa5e7e 100644 --- a/src/lexical/algorithm.rs +++ b/src/lexical/algorithm.rs @@ -51,7 +51,10 @@ where // Compute the product of the power, if it overflows, // prematurely return early, otherwise, if we didn't overshoot, // we can get an exact value. - let value = mantissa.checked_mul(power)?; + let value = match mantissa.checked_mul(power) { + None => return None, + Some(value) => value, + }; if value >> mantissa_size != 0 { None } else { diff --git a/src/lexical/digit.rs b/src/lexical/digit.rs index 882aa9e..3d150a1 100644 --- a/src/lexical/digit.rs +++ b/src/lexical/digit.rs @@ -11,5 +11,8 @@ pub(crate) fn to_digit(c: u8) -> Option { // Add digit to mantissa. #[inline] pub(crate) fn add_digit(value: u64, digit: u32) -> Option { - value.checked_mul(10)?.checked_add(digit as u64) + match value.checked_mul(10) { + None => None, + Some(n) => n.checked_add(digit as u64), + } } diff --git a/src/lexical/errors.rs b/src/lexical/errors.rs index cad4bd3..f4f41cd 100644 --- a/src/lexical/errors.rs +++ b/src/lexical/errors.rs @@ -5,8 +5,7 @@ //! This estimates the error in a floating-point representation. //! //! This implementation is loosely based off the Golang implementation, -//! found here: -//! https://golang.org/src/strconv/atof.go +//! found here: use super::float::*; use super::num::*; diff --git a/src/lexical/math.rs b/src/lexical/math.rs index 37cc1d2..d7122bf 100644 --- a/src/lexical/math.rs +++ b/src/lexical/math.rs @@ -336,7 +336,7 @@ mod small { pub fn imul(x: &mut Vec, y: Limb) { // Multiply iteratively over all elements, adding the carry each time. let mut carry: Limb = 0; - for xi in x.iter_mut() { + for xi in &mut *x { carry = scalar::imul(xi, y, carry); } @@ -482,7 +482,7 @@ mod small { let rshift = bits - n; let lshift = n; let mut prev: Limb = 0; - for xi in x.iter_mut() { + for xi in &mut *x { let tmp = *xi; *xi <<= lshift; *xi |= prev >> rshift; diff --git a/src/lexical/num.rs b/src/lexical/num.rs index e47e003..af10afd 100644 --- a/src/lexical/num.rs +++ b/src/lexical/num.rs @@ -223,7 +223,7 @@ pub trait Float: Number { const NEGATIVE_INFINITY_BITS: Self::Unsigned; /// Size of the significand (mantissa) without hidden bit. const MANTISSA_SIZE: i32; - /// Bias of the exponet + /// Bias of the exponent const EXPONENT_BIAS: i32; /// Exponent portion of a denormal float. const DENORMAL_EXPONENT: i32; diff --git a/src/lib.rs b/src/lib.rs index bd49763..aa3fcfc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,10 +55,9 @@ //! ``` //! //! A string of JSON data can be parsed into a `serde_json::Value` by the -//! [`serde_json::from_str`][from_str] function. There is also -//! [`from_slice`][from_slice] for parsing from a byte slice &[u8] and -//! [`from_reader`][from_reader] for parsing from any `io::Read` like a File or -//! a TCP stream. +//! [`serde_json::from_str`][from_str] function. There is also [`from_slice`] +//! for parsing from a byte slice &\[u8\] and [`from_reader`] for parsing from +//! any `io::Read` like a File or a TCP stream. //! //! ``` //! use serde_json::{Result, Value}; @@ -300,7 +299,7 @@ //! [macro]: crate::json //! [`serde-json-core`]: https://github.com/rust-embedded-community/serde-json-core -#![doc(html_root_url = "https://docs.rs/serde_json/1.0.94")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.113")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, @@ -315,18 +314,13 @@ clippy::match_single_binding, clippy::needless_doctest_main, clippy::needless_late_init, - // clippy bug: https://github.com/rust-lang/rust-clippy/issues/8366 - clippy::ptr_arg, clippy::return_self_not_must_use, clippy::transmute_ptr_to_ptr, - clippy::unnecessary_wraps, - // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704 - clippy::unnested_or_patterns, + clippy::unconditional_recursion, // https://github.com/rust-lang/rust-clippy/issues/12133 + clippy::unnecessary_wraps )] // Ignored clippy_pedantic lints #![allow( - // buggy - clippy::iter_not_returning_iterator, // https://github.com/rust-lang/rust-clippy/issues/8285 // Deserializer::from_str, into_iter clippy::should_implement_trait, // integer and float ser/de requires these sorts of casts @@ -362,6 +356,8 @@ clippy::missing_errors_doc, clippy::must_use_candidate, )] +// Restrictions +#![deny(clippy::question_mark_used)] #![allow(non_upper_case_globals)] #![deny(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] @@ -370,6 +366,7 @@ extern crate alloc; #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[doc(inline)] pub use crate::de::from_reader; #[doc(inline)] @@ -379,6 +376,7 @@ pub use crate::error::{Error, Result}; #[doc(inline)] pub use crate::ser::{to_string, to_string_pretty, to_vec, to_vec_pretty}; #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[doc(inline)] pub use crate::ser::{to_writer, to_writer_pretty, Serializer}; #[doc(inline)] diff --git a/src/macros.rs b/src/macros.rs index 5287998..e8c6cd2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -10,7 +10,8 @@ /// "features": [ /// "serde", /// "json" -/// ] +/// ], +/// "homepage": null /// } /// }); /// ``` diff --git a/src/map.rs b/src/map.rs index 3e8a381..1d74e1b 100644 --- a/src/map.rs +++ b/src/map.rs @@ -11,7 +11,7 @@ use alloc::string::String; use core::borrow::Borrow; use core::fmt::{self, Debug}; use core::hash::Hash; -use core::iter::{FromIterator, FusedIterator}; +use core::iter::FusedIterator; #[cfg(feature = "preserve_order")] use core::mem; use core::ops; @@ -106,7 +106,6 @@ impl Map { /// The key may be any borrowed form of the map's key type, but the ordering /// on the borrowed form *must* match the ordering on the key type. #[inline] - #[cfg(any(feature = "preserve_order", not(no_btreemap_get_key_value)))] pub fn get_key_value(&self, key: &Q) -> Option<(&String, &Value)> where String: Borrow, @@ -131,6 +130,12 @@ impl Map { /// /// The key may be any borrowed form of the map's key type, but the ordering /// on the borrowed form *must* match the ordering on the key type. + /// + /// If serde_json's "preserve_order" is enabled, `.remove(key)` is + /// equivalent to [`.swap_remove(key)`][Self::swap_remove], replacing this + /// entry's position with the last element. If you need to preserve the + /// relative order of the keys in the map, use + /// [`.shift_remove(key)`][Self::shift_remove] instead. #[inline] pub fn remove(&mut self, key: &Q) -> Option where @@ -138,7 +143,7 @@ impl Map { Q: ?Sized + Ord + Eq + Hash, { #[cfg(feature = "preserve_order")] - return self.map.swap_remove(key); + return self.swap_remove(key); #[cfg(not(feature = "preserve_order"))] return self.map.remove(key); } @@ -148,49 +153,86 @@ impl Map { /// /// The key may be any borrowed form of the map's key type, but the ordering /// on the borrowed form *must* match the ordering on the key type. + /// + /// If serde_json's "preserve_order" is enabled, `.remove_entry(key)` is + /// equivalent to [`.swap_remove_entry(key)`][Self::swap_remove_entry], + /// replacing this entry's position with the last element. If you need to + /// preserve the relative order of the keys in the map, use + /// [`.shift_remove_entry(key)`][Self::shift_remove_entry] instead. + #[inline] pub fn remove_entry(&mut self, key: &Q) -> Option<(String, Value)> where String: Borrow, Q: ?Sized + Ord + Eq + Hash, { - #[cfg(any(feature = "preserve_order", not(no_btreemap_remove_entry)))] + #[cfg(feature = "preserve_order")] + return self.swap_remove_entry(key); + #[cfg(not(feature = "preserve_order"))] return self.map.remove_entry(key); - #[cfg(all( - not(feature = "preserve_order"), - no_btreemap_remove_entry, - not(no_btreemap_get_key_value), - ))] - { - let (key, _value) = self.map.get_key_value(key)?; - let key = key.clone(); - let value = self.map.remove::(&key)?; - Some((key, value)) - } - #[cfg(all( - not(feature = "preserve_order"), - no_btreemap_remove_entry, - no_btreemap_get_key_value, - ))] - { - use core::ops::{Bound, RangeBounds}; - - struct Key<'a, Q: ?Sized>(&'a Q); - - impl<'a, Q: ?Sized> RangeBounds for Key<'a, Q> { - fn start_bound(&self) -> Bound<&Q> { - Bound::Included(self.0) - } - fn end_bound(&self) -> Bound<&Q> { - Bound::Included(self.0) - } - } + } - let mut range = self.map.range(Key(key)); - let (key, _value) = range.next()?; - let key = key.clone(); - let value = self.map.remove::(&key)?; - Some((key, value)) - } + /// Removes and returns the value corresponding to the key from the map. + /// + /// Like [`Vec::swap_remove`], the entry is removed by swapping it with the + /// last element of the map and popping it off. This perturbs the position + /// of what used to be the last element! + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn swap_remove(&mut self, key: &Q) -> Option + where + String: Borrow, + Q: ?Sized + Ord + Eq + Hash, + { + self.map.swap_remove(key) + } + + /// Remove and return the key-value pair. + /// + /// Like [`Vec::swap_remove`], the entry is removed by swapping it with the + /// last element of the map and popping it off. This perturbs the position + /// of what used to be the last element! + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn swap_remove_entry(&mut self, key: &Q) -> Option<(String, Value)> + where + String: Borrow, + Q: ?Sized + Ord + Eq + Hash, + { + self.map.swap_remove_entry(key) + } + + /// Removes and returns the value corresponding to the key from the map. + /// + /// Like [`Vec::remove`], the entry is removed by shifting all of the + /// elements that follow it, preserving their relative order. This perturbs + /// the index of all of those elements! + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn shift_remove(&mut self, key: &Q) -> Option + where + String: Borrow, + Q: ?Sized + Ord + Eq + Hash, + { + self.map.shift_remove(key) + } + + /// Remove and return the key-value pair. + /// + /// Like [`Vec::remove`], the entry is removed by shifting all of the + /// elements that follow it, preserving their relative order. This perturbs + /// the index of all of those elements! + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn shift_remove_entry(&mut self, key: &Q) -> Option<(String, Value)> + where + String: Borrow, + Q: ?Sized + Ord + Eq + Hash, + { + self.map.shift_remove_entry(key) } /// Moves all elements from other into self, leaving other empty. @@ -276,7 +318,6 @@ impl Map { /// /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` /// returns `false`. - #[cfg(not(no_btreemap_retain))] #[inline] pub fn retain(&mut self, f: F) where diff --git a/src/number.rs b/src/number.rs index 21a7641..b0231a8 100644 --- a/src/number.rs +++ b/src/number.rs @@ -279,6 +279,62 @@ impl Number { } } + /// Returns the exact original JSON representation that this Number was + /// parsed from. + /// + /// For numbers constructed not via parsing, such as by `From`, returns + /// the JSON representation that serde\_json would serialize for this + /// number. + /// + /// ``` + /// # use serde_json::Number; + /// for value in [ + /// "7", + /// "12.34", + /// "34e-56789", + /// "0.0123456789000000012345678900000001234567890000123456789", + /// "343412345678910111213141516171819202122232425262728293034", + /// "-343412345678910111213141516171819202122232425262728293031", + /// ] { + /// let number: Number = serde_json::from_str(value).unwrap(); + /// assert_eq!(number.as_str(), value); + /// } + /// ``` + #[cfg(feature = "arbitrary_precision")] + #[cfg_attr(docsrs, doc(cfg(feature = "arbitrary_precision")))] + pub fn as_str(&self) -> &str { + &self.n + } + + pub(crate) fn as_f32(&self) -> Option { + #[cfg(not(feature = "arbitrary_precision"))] + match self.n { + N::PosInt(n) => Some(n as f32), + N::NegInt(n) => Some(n as f32), + N::Float(n) => Some(n as f32), + } + #[cfg(feature = "arbitrary_precision")] + self.n.parse::().ok().filter(|float| float.is_finite()) + } + + pub(crate) fn from_f32(f: f32) -> Option { + if f.is_finite() { + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + N::Float(f as f64) + } + #[cfg(feature = "arbitrary_precision")] + { + ryu::Buffer::new().format_finite(f).to_owned() + } + }; + Some(Number { n }) + } else { + None + } + } + #[cfg(feature = "arbitrary_precision")] /// Not public API. Only tests use this. #[doc(hidden)] @@ -332,8 +388,8 @@ impl Serialize for Number { { use serde::ser::SerializeStruct; - let mut s = serializer.serialize_struct(TOKEN, 1)?; - s.serialize_field(TOKEN, &self.n)?; + let mut s = tri!(serializer.serialize_struct(TOKEN, 1)); + tri!(s.serialize_field(TOKEN, &self.n)); s.end() } } @@ -377,11 +433,11 @@ impl<'de> Deserialize<'de> for Number { where V: de::MapAccess<'de>, { - let value = visitor.next_key::()?; + let value = tri!(visitor.next_key::()); if value.is_none() { return Err(de::Error::invalid_type(Unexpected::Map, &self)); } - let v: NumberFromString = visitor.next_value()?; + let v: NumberFromString = tri!(visitor.next_value()); Ok(v.value) } } @@ -420,7 +476,7 @@ impl<'de> de::Deserialize<'de> for NumberKey { } } - deserializer.deserialize_identifier(FieldVisitor)?; + tri!(deserializer.deserialize_identifier(FieldVisitor)); Ok(NumberKey) } } @@ -523,7 +579,7 @@ macro_rules! deserialize_number { where V: de::Visitor<'de>, { - visitor.$visit(self.n.parse().map_err(|_| invalid_number())?) + visitor.$visit(tri!(self.n.parse().map_err(|_| invalid_number()))) } }; } diff --git a/src/raw.rs b/src/raw.rs index 6aa4ffc..a2bf0ec 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -177,11 +177,9 @@ impl RawValue { /// - the input has no leading or trailing whitespace, and /// - the input has capacity equal to its length. pub fn from_string(json: String) -> Result, Error> { - { - let borrowed = crate::from_str::<&Self>(&json)?; - if borrowed.json.len() < json.len() { - return Ok(borrowed.to_owned()); - } + let borrowed = tri!(crate::from_str::<&Self>(&json)); + if borrowed.json.len() < json.len() { + return Ok(borrowed.to_owned()); } Ok(Self::from_owned(json.into_boxed_str())) } @@ -287,7 +285,7 @@ pub fn to_raw_value(value: &T) -> Result, Error> where T: ?Sized + Serialize, { - let json_string = crate::to_string(value)?; + let json_string = tri!(crate::to_string(value)); Ok(RawValue::from_owned(json_string.into_boxed_str())) } @@ -298,8 +296,8 @@ impl Serialize for RawValue { where S: Serializer, { - let mut s = serializer.serialize_struct(TOKEN, 1)?; - s.serialize_field(TOKEN, &self.json)?; + let mut s = tri!(serializer.serialize_struct(TOKEN, 1)); + tri!(s.serialize_field(TOKEN, &self.json)); s.end() } } @@ -322,7 +320,7 @@ impl<'de: 'a, 'a> Deserialize<'de> for &'a RawValue { where V: MapAccess<'de>, { - let value = visitor.next_key::()?; + let value = tri!(visitor.next_key::()); if value.is_none() { return Err(de::Error::invalid_type(Unexpected::Map, &self)); } @@ -352,7 +350,7 @@ impl<'de> Deserialize<'de> for Box { where V: MapAccess<'de>, { - let value = visitor.next_key::()?; + let value = tri!(visitor.next_key::()); if value.is_none() { return Err(de::Error::invalid_type(Unexpected::Map, &self)); } @@ -392,7 +390,7 @@ impl<'de> Deserialize<'de> for RawKey { } } - deserializer.deserialize_identifier(FieldVisitor)?; + tri!(deserializer.deserialize_identifier(FieldVisitor)); Ok(RawKey) } } @@ -529,3 +527,251 @@ impl<'de> MapAccess<'de> for BorrowedRawDeserializer<'de> { seed.deserialize(BorrowedStrDeserializer::new(self.raw_value.take().unwrap())) } } + +impl<'de> IntoDeserializer<'de, Error> for &'de RawValue { + type Deserializer = &'de RawValue; + + fn into_deserializer(self) -> Self::Deserializer { + self + } +} + +impl<'de> Deserializer<'de> for &'de RawValue { + type Error = Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_any(visitor) + } + + fn deserialize_bool(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_bool(visitor) + } + + fn deserialize_i8(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_i8(visitor) + } + + fn deserialize_i16(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_i16(visitor) + } + + fn deserialize_i32(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_i32(visitor) + } + + fn deserialize_i64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_i64(visitor) + } + + fn deserialize_i128(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_i128(visitor) + } + + fn deserialize_u8(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_u8(visitor) + } + + fn deserialize_u16(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_u16(visitor) + } + + fn deserialize_u32(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_u32(visitor) + } + + fn deserialize_u64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_u64(visitor) + } + + fn deserialize_u128(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_u128(visitor) + } + + fn deserialize_f32(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_f32(visitor) + } + + fn deserialize_f64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_f64(visitor) + } + + fn deserialize_char(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_char(visitor) + } + + fn deserialize_str(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_str(visitor) + } + + fn deserialize_string(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_string(visitor) + } + + fn deserialize_bytes(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_bytes(visitor) + } + + fn deserialize_byte_buf(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_byte_buf(visitor) + } + + fn deserialize_option(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_option(visitor) + } + + fn deserialize_unit(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_unit(visitor) + } + + fn deserialize_unit_struct(self, name: &'static str, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_unit_struct(name, visitor) + } + + fn deserialize_newtype_struct( + self, + name: &'static str, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_newtype_struct(name, visitor) + } + + fn deserialize_seq(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_seq(visitor) + } + + fn deserialize_tuple(self, len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_tuple(len, visitor) + } + + fn deserialize_tuple_struct( + self, + name: &'static str, + len: usize, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_tuple_struct(name, len, visitor) + } + + fn deserialize_map(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_map(visitor) + } + + fn deserialize_struct( + self, + name: &'static str, + fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_struct(name, fields, visitor) + } + + fn deserialize_enum( + self, + name: &'static str, + variants: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_enum(name, variants, visitor) + } + + fn deserialize_identifier(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_identifier(visitor) + } + + fn deserialize_ignored_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_ignored_any(visitor) + } +} diff --git a/src/read.rs b/src/read.rs index fc3a3ca..7446f28 100644 --- a/src/read.rs +++ b/src/read.rs @@ -81,7 +81,7 @@ pub trait Read<'de>: private::Sealed { #[doc(hidden)] fn ignore_str(&mut self) -> Result<()>; - /// Assumes the previous byte was a hex escape sequnce ('\u') in a string. + /// Assumes the previous byte was a hex escape sequence ('\u') in a string. /// Parses next hexadecimal sequence. #[doc(hidden)] fn decode_hex_escape(&mut self) -> Result; diff --git a/src/ser.rs b/src/ser.rs index 80c2deb..3742e0b 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -189,12 +189,9 @@ where #[inline] fn serialize_bytes(self, value: &[u8]) -> Result<()> { - use serde::ser::SerializeSeq; - let mut seq = tri!(self.serialize_seq(Some(value.len()))); - for byte in value { - tri!(seq.serialize_element(byte)); - } - seq.end() + self.formatter + .write_byte_array(&mut self.writer, value) + .map_err(Error::io) } #[inline] @@ -439,17 +436,15 @@ where .formatter .begin_string(&mut self.writer) .map_err(Error::io)); - { - let mut adapter = Adapter { - writer: &mut self.writer, - formatter: &mut self.formatter, - error: None, - }; - match write!(adapter, "{}", value) { - Ok(()) => debug_assert!(adapter.error.is_none()), - Err(fmt::Error) => { - return Err(Error::io(adapter.error.expect("there should be an error"))); - } + let mut adapter = Adapter { + writer: &mut self.writer, + formatter: &mut self.formatter, + error: None, + }; + match write!(adapter, "{}", value) { + Ok(()) => debug_assert!(adapter.error.is_none()), + Err(fmt::Error) => { + return Err(Error::io(adapter.error.expect("there should be an error"))); } } self.formatter @@ -789,6 +784,10 @@ fn key_must_be_a_string() -> Error { Error::syntax(ErrorCode::KeyMustBeAString, 0, 0) } +fn float_key_must_be_finite() -> Error { + Error::syntax(ErrorCode::FloatKeyMustBeFinite, 0, 0) +} + impl<'a, W, F> ser::Serializer for MapKeySerializer<'a, W, F> where W: io::Write, @@ -828,8 +827,21 @@ where type SerializeStruct = Impossible<(), Error>; type SerializeStructVariant = Impossible<(), Error>; - fn serialize_bool(self, _value: bool) -> Result<()> { - Err(key_must_be_a_string()) + fn serialize_bool(self, value: bool) -> Result<()> { + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(Error::io)); + tri!(self + .ser + .formatter + .write_bool(&mut self.ser.writer, value) + .map_err(Error::io)); + self.ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(Error::io) } fn serialize_i8(self, value: i8) -> Result<()> { @@ -1002,12 +1014,46 @@ where .map_err(Error::io) } - fn serialize_f32(self, _value: f32) -> Result<()> { - Err(key_must_be_a_string()) + fn serialize_f32(self, value: f32) -> Result<()> { + if !value.is_finite() { + return Err(float_key_must_be_finite()); + } + + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(Error::io)); + tri!(self + .ser + .formatter + .write_f32(&mut self.ser.writer, value) + .map_err(Error::io)); + self.ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(Error::io) } - fn serialize_f64(self, _value: f64) -> Result<()> { - Err(key_must_be_a_string()) + fn serialize_f64(self, value: f64) -> Result<()> { + if !value.is_finite() { + return Err(float_key_must_be_finite()); + } + + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(Error::io)); + tri!(self + .ser + .formatter + .write_f64(&mut self.ser.writer, value) + .map_err(Error::io)); + self.ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(Error::io) } fn serialize_char(self, value: char) -> Result<()> { @@ -1043,11 +1089,11 @@ where Err(key_must_be_a_string()) } - fn serialize_some(self, _value: &T) -> Result<()> + fn serialize_some(self, value: &T) -> Result<()> where T: ?Sized + Serialize, { - Err(key_must_be_a_string()) + value.serialize(self) } fn serialize_seq(self, _len: Option) -> Result { @@ -1734,6 +1780,24 @@ pub trait Formatter { writer.write_all(s) } + /// Writes the representation of a byte array. Formatters can choose whether + /// to represent bytes as a JSON array of integers (the default), or some + /// JSON string encoding like hex or base64. + fn write_byte_array(&mut self, writer: &mut W, value: &[u8]) -> io::Result<()> + where + W: ?Sized + io::Write, + { + tri!(self.begin_array(writer)); + let mut first = true; + for byte in value { + tri!(self.begin_array_value(writer, first)); + tri!(self.write_u8(writer, *byte)); + tri!(self.end_array_value(writer)); + first = false; + } + self.end_array(writer) + } + /// Called before every array. Writes a `[` to the specified /// writer. #[inline] @@ -2062,7 +2126,9 @@ static ESCAPE: [u8; 256] = [ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F ]; -/// Serialize the given data structure as JSON into the IO stream. +/// Serialize the given data structure as JSON into the I/O stream. +/// +/// Serialization guarantees it only feeds valid UTF-8 sequences to the writer. /// /// # Errors /// @@ -2079,9 +2145,11 @@ where value.serialize(&mut ser) } -/// Serialize the given data structure as pretty-printed JSON into the IO +/// Serialize the given data structure as pretty-printed JSON into the I/O /// stream. /// +/// Serialization guarantees it only feeds valid UTF-8 sequences to the writer. +/// /// # Errors /// /// Serialization can fail if `T`'s implementation of `Serialize` decides to diff --git a/src/value/de.rs b/src/value/de.rs index 9c266d0..1e8b5ac 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -1,4 +1,4 @@ -use crate::error::Error; +use crate::error::{Error, ErrorCode}; use crate::map::Map; use crate::number::Number; use crate::value::Value; @@ -106,15 +106,15 @@ impl<'de> Deserialize<'de> for Value { where V: MapAccess<'de>, { - match visitor.next_key_seed(KeyClassifier)? { + match tri!(visitor.next_key_seed(KeyClassifier)) { #[cfg(feature = "arbitrary_precision")] Some(KeyClass::Number) => { - let number: NumberFromString = visitor.next_value()?; + let number: NumberFromString = tri!(visitor.next_value()); Ok(Value::Number(number.value)) } #[cfg(feature = "raw_value")] Some(KeyClass::RawValue) => { - let value = visitor.next_value_seed(crate::raw::BoxedFromString)?; + let value = tri!(visitor.next_value_seed(crate::raw::BoxedFromString)); crate::from_str(value.get()).map_err(de::Error::custom) } Some(KeyClass::Map(first_key)) => { @@ -482,6 +482,14 @@ impl<'de> IntoDeserializer<'de, Error> for Value { } } +impl<'de> IntoDeserializer<'de, Error> for &'de Value { + type Deserializer = Self; + + fn into_deserializer(self) -> Self::Deserializer { + self + } +} + struct VariantDeserializer { value: Option, } @@ -1120,18 +1128,30 @@ struct MapKeyDeserializer<'de> { key: Cow<'de, str>, } -macro_rules! deserialize_integer_key { - ($method:ident => $visit:ident) => { +macro_rules! deserialize_numeric_key { + ($method:ident) => { + deserialize_numeric_key!($method, deserialize_number); + }; + + ($method:ident, $using:ident) => { fn $method(self, visitor: V) -> Result where V: Visitor<'de>, { - match (self.key.parse(), self.key) { - (Ok(integer), _) => visitor.$visit(integer), - (Err(_), Cow::Borrowed(s)) => visitor.visit_borrowed_str(s), - #[cfg(any(feature = "std", feature = "alloc"))] - (Err(_), Cow::Owned(s)) => visitor.visit_string(s), + let mut de = crate::Deserializer::from_str(&self.key); + + match tri!(de.peek()) { + Some(b'0'..=b'9' | b'-') => {} + _ => return Err(Error::syntax(ErrorCode::ExpectedNumericKey, 0, 0)), + } + + let number = tri!(de.$using(visitor)); + + if tri!(de.peek()).is_some() { + return Err(Error::syntax(ErrorCode::ExpectedNumericKey, 0, 0)); } + + Ok(number) } }; } @@ -1146,16 +1166,38 @@ impl<'de> serde::Deserializer<'de> for MapKeyDeserializer<'de> { BorrowedCowStrDeserializer::new(self.key).deserialize_any(visitor) } - deserialize_integer_key!(deserialize_i8 => visit_i8); - deserialize_integer_key!(deserialize_i16 => visit_i16); - deserialize_integer_key!(deserialize_i32 => visit_i32); - deserialize_integer_key!(deserialize_i64 => visit_i64); - deserialize_integer_key!(deserialize_i128 => visit_i128); - deserialize_integer_key!(deserialize_u8 => visit_u8); - deserialize_integer_key!(deserialize_u16 => visit_u16); - deserialize_integer_key!(deserialize_u32 => visit_u32); - deserialize_integer_key!(deserialize_u64 => visit_u64); - deserialize_integer_key!(deserialize_u128 => visit_u128); + deserialize_numeric_key!(deserialize_i8); + deserialize_numeric_key!(deserialize_i16); + deserialize_numeric_key!(deserialize_i32); + deserialize_numeric_key!(deserialize_i64); + deserialize_numeric_key!(deserialize_u8); + deserialize_numeric_key!(deserialize_u16); + deserialize_numeric_key!(deserialize_u32); + deserialize_numeric_key!(deserialize_u64); + #[cfg(not(feature = "float_roundtrip"))] + deserialize_numeric_key!(deserialize_f32); + deserialize_numeric_key!(deserialize_f64); + + #[cfg(feature = "float_roundtrip")] + deserialize_numeric_key!(deserialize_f32, do_deserialize_f32); + deserialize_numeric_key!(deserialize_i128, do_deserialize_i128); + deserialize_numeric_key!(deserialize_u128, do_deserialize_u128); + + fn deserialize_bool(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + if self.key == "true" { + visitor.visit_bool(true) + } else if self.key == "false" { + visitor.visit_bool(false) + } else { + Err(serde::de::Error::invalid_type( + Unexpected::Str(&self.key), + &visitor, + )) + } + } #[inline] fn deserialize_option(self, visitor: V) -> Result @@ -1193,8 +1235,8 @@ impl<'de> serde::Deserializer<'de> for MapKeyDeserializer<'de> { } forward_to_deserialize_any! { - bool f32 f64 char str string bytes byte_buf unit unit_struct seq tuple - tuple_struct map struct identifier ignored_any + char str string bytes byte_buf unit unit_struct seq tuple tuple_struct + map struct identifier ignored_any } } @@ -1327,7 +1369,7 @@ impl<'de> de::EnumAccess<'de> for BorrowedCowStrDeserializer<'de> { where T: de::DeserializeSeed<'de>, { - let value = seed.deserialize(self)?; + let value = tri!(seed.deserialize(self)); Ok((value, UnitOnly)) } } diff --git a/src/value/from.rs b/src/value/from.rs index c5a6a39..ed1e333 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -4,7 +4,6 @@ use crate::number::Number; use alloc::borrow::Cow; use alloc::string::{String, ToString}; use alloc::vec::Vec; -use core::iter::FromIterator; macro_rules! from_integer { ($($ty:ident)*) => { @@ -29,7 +28,8 @@ from_integer! { } impl From for Value { - /// Convert 32-bit floating point number to `Value` + /// Convert 32-bit floating point number to `Value::Number`, or + /// `Value::Null` if infinite or NaN. /// /// # Examples /// @@ -40,12 +40,13 @@ impl From for Value { /// let x: Value = f.into(); /// ``` fn from(f: f32) -> Self { - From::from(f as f64) + Number::from_f32(f).map_or(Value::Null, Value::Number) } } impl From for Value { - /// Convert 64-bit floating point number to `Value` + /// Convert 64-bit floating point number to `Value::Number`, or + /// `Value::Null` if infinite or NaN. /// /// # Examples /// @@ -61,7 +62,7 @@ impl From for Value { } impl From for Value { - /// Convert boolean to `Value` + /// Convert boolean to `Value::Bool`. /// /// # Examples /// @@ -77,7 +78,7 @@ impl From for Value { } impl From for Value { - /// Convert `String` to `Value` + /// Convert `String` to `Value::String`. /// /// # Examples /// @@ -92,8 +93,8 @@ impl From for Value { } } -impl<'a> From<&'a str> for Value { - /// Convert string slice to `Value` +impl From<&str> for Value { + /// Convert string slice to `Value::String`. /// /// # Examples /// @@ -109,7 +110,7 @@ impl<'a> From<&'a str> for Value { } impl<'a> From> for Value { - /// Convert copy-on-write string to `Value` + /// Convert copy-on-write string to `Value::String`. /// /// # Examples /// @@ -134,7 +135,7 @@ impl<'a> From> for Value { } impl From for Value { - /// Convert `Number` to `Value` + /// Convert `Number` to `Value::Number`. /// /// # Examples /// @@ -150,7 +151,7 @@ impl From for Value { } impl From> for Value { - /// Convert map (with string keys) to `Value` + /// Convert map (with string keys) to `Value::Object`. /// /// # Examples /// @@ -167,7 +168,7 @@ impl From> for Value { } impl> From> for Value { - /// Convert a `Vec` to `Value` + /// Convert a `Vec` to `Value::Array`. /// /// # Examples /// @@ -182,8 +183,8 @@ impl> From> for Value { } } -impl<'a, T: Clone + Into> From<&'a [T]> for Value { - /// Convert a slice to `Value` +impl> From<&[T]> for Value { + /// Convert a slice to `Value::Array`. /// /// # Examples /// @@ -193,13 +194,13 @@ impl<'a, T: Clone + Into> From<&'a [T]> for Value { /// let v: &[&str] = &["lorem", "ipsum", "dolor"]; /// let x: Value = v.into(); /// ``` - fn from(f: &'a [T]) -> Self { + fn from(f: &[T]) -> Self { Value::Array(f.iter().cloned().map(Into::into).collect()) } } impl> FromIterator for Value { - /// Convert an iteratable type to a `Value` + /// Create a `Value::Array` by collecting an iterator of array elements. /// /// # Examples /// @@ -229,7 +230,7 @@ impl> FromIterator for Value { } impl, V: Into> FromIterator<(K, V)> for Value { - /// Convert an iteratable type to a `Value` + /// Create a `Value::Object` by collecting an iterator of key-value pairs. /// /// # Examples /// @@ -249,7 +250,7 @@ impl, V: Into> FromIterator<(K, V)> for Value { } impl From<()> for Value { - /// Convert `()` to `Value` + /// Convert `()` to `Value::Null`. /// /// # Examples /// diff --git a/src/value/index.rs b/src/value/index.rs index c74042b..891ca8e 100644 --- a/src/value/index.rs +++ b/src/value/index.rs @@ -116,7 +116,7 @@ impl Index for String { } } -impl<'a, T> Index for &'a T +impl Index for &T where T: ?Sized + Index, { diff --git a/src/value/mod.rs b/src/value/mod.rs index 470b6b2..b3f51ea 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -106,6 +106,7 @@ pub use crate::map::Map; pub use crate::number::Number; #[cfg(feature = "raw_value")] +#[cfg_attr(docsrs, doc(cfg(feature = "raw_value")))] pub use crate::raw::{to_raw_value, RawValue}; /// Represents any valid JSON value. @@ -182,11 +183,11 @@ impl Debug for Value { Value::Number(number) => Debug::fmt(number, formatter), Value::String(string) => write!(formatter, "String({:?})", string), Value::Array(vec) => { - formatter.write_str("Array ")?; + tri!(formatter.write_str("Array ")); Debug::fmt(vec, formatter) } Value::Object(map) => { - formatter.write_str("Object ")?; + tri!(formatter.write_str("Object ")); Debug::fmt(map, formatter) } } @@ -514,6 +515,28 @@ impl Value { } } + /// If the `Value` is a Number, returns the associated [`Number`]. Returns + /// None otherwise. + /// + /// ``` + /// # use serde_json::{json, Number}; + /// # + /// let v = json!({ "a": 1, "b": 2.2, "c": -3, "d": "4" }); + /// + /// assert_eq!(v["a"].as_number(), Some(&Number::from(1u64))); + /// assert_eq!(v["b"].as_number(), Some(&Number::from_f64(2.2).unwrap())); + /// assert_eq!(v["c"].as_number(), Some(&Number::from(-3i64))); + /// + /// // The string `"4"` is not a number. + /// assert_eq!(v["d"].as_number(), None); + /// ``` + pub fn as_number(&self) -> Option<&Number> { + match self { + Value::Number(number) => Some(number), + _ => None, + } + } + /// Returns true if the `Value` is an integer between `i64::MIN` and /// `i64::MAX`. /// @@ -889,7 +912,6 @@ mod ser; /// ``` /// use serde::Serialize; /// use serde_json::json; -/// /// use std::error::Error; /// /// #[derive(Serialize)] @@ -898,7 +920,7 @@ mod ser; /// location: String, /// } /// -/// fn compare_json_values() -> Result<(), Box> { +/// fn compare_json_values() -> Result<(), Box> { /// let u = User { /// fingerprint: "0xF9BA143B95FF6D82".to_owned(), /// location: "Menlo Park, CA".to_owned(), diff --git a/src/value/partial_eq.rs b/src/value/partial_eq.rs index b4ef84c..46c1dbc 100644 --- a/src/value/partial_eq.rs +++ b/src/value/partial_eq.rs @@ -9,6 +9,13 @@ fn eq_u64(value: &Value, other: u64) -> bool { value.as_u64().map_or(false, |i| i == other) } +fn eq_f32(value: &Value, other: f32) -> bool { + match value { + Value::Number(n) => n.as_f32().map_or(false, |i| i == other), + _ => false, + } +} + fn eq_f64(value: &Value, other: f64) -> bool { value.as_f64().map_or(false, |i| i == other) } @@ -27,7 +34,7 @@ impl PartialEq for Value { } } -impl<'a> PartialEq<&'a str> for Value { +impl PartialEq<&str> for Value { fn eq(&self, other: &&str) -> bool { eq_str(self, *other) } @@ -39,7 +46,7 @@ impl PartialEq for str { } } -impl<'a> PartialEq for &'a str { +impl PartialEq for &str { fn eq(&self, other: &Value) -> bool { eq_str(other, *self) } @@ -90,6 +97,7 @@ macro_rules! partialeq_numeric { partialeq_numeric! { eq_i64[i8 i16 i32 i64 isize] eq_u64[u8 u16 u32 u64 usize] - eq_f64[f32 f64] + eq_f32[f32] + eq_f64[f64] eq_bool[bool] } diff --git a/src/value/ser.rs b/src/value/ser.rs index a29814e..835fa90 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -1,12 +1,9 @@ use crate::error::{Error, ErrorCode, Result}; use crate::map::Map; -use crate::number::Number; use crate::value::{to_value, Value}; use alloc::borrow::ToOwned; use alloc::string::{String, ToString}; use alloc::vec::Vec; -#[cfg(not(feature = "arbitrary_precision"))] -use core::convert::TryFrom; use core::fmt::Display; use core::result; use serde::ser::{Impossible, Serialize}; @@ -149,13 +146,13 @@ impl serde::Serializer for Serializer { } #[inline] - fn serialize_f32(self, value: f32) -> Result { - self.serialize_f64(value as f64) + fn serialize_f32(self, float: f32) -> Result { + Ok(Value::from(float)) } #[inline] - fn serialize_f64(self, value: f64) -> Result { - Ok(Number::from_f64(value).map_or(Value::Null, Value::Number)) + fn serialize_f64(self, float: f64) -> Result { + Ok(Value::from(float)) } #[inline] @@ -452,6 +449,10 @@ fn key_must_be_a_string() -> Error { Error::syntax(ErrorCode::KeyMustBeAString, 0, 0) } +fn float_key_must_be_finite() -> Error { + Error::syntax(ErrorCode::FloatKeyMustBeFinite, 0, 0) +} + impl serde::Serializer for MapKeySerializer { type Ok = String; type Error = Error; @@ -482,8 +483,8 @@ impl serde::Serializer for MapKeySerializer { value.serialize(self) } - fn serialize_bool(self, _value: bool) -> Result { - Err(key_must_be_a_string()) + fn serialize_bool(self, value: bool) -> Result { + Ok(value.to_string()) } fn serialize_i8(self, value: i8) -> Result { @@ -518,12 +519,20 @@ impl serde::Serializer for MapKeySerializer { Ok(value.to_string()) } - fn serialize_f32(self, _value: f32) -> Result { - Err(key_must_be_a_string()) + fn serialize_f32(self, value: f32) -> Result { + if value.is_finite() { + Ok(ryu::Buffer::new().format_finite(value).to_owned()) + } else { + Err(float_key_must_be_finite()) + } } - fn serialize_f64(self, _value: f64) -> Result { - Err(key_must_be_a_string()) + fn serialize_f64(self, value: f64) -> Result { + if value.is_finite() { + Ok(ryu::Buffer::new().format_finite(value).to_owned()) + } else { + Err(float_key_must_be_finite()) + } } #[inline] @@ -641,7 +650,7 @@ impl serde::ser::SerializeStruct for SerializeMap { #[cfg(feature = "arbitrary_precision")] SerializeMap::Number { out_value } => { if key == crate::number::TOKEN { - *out_value = Some(value.serialize(NumberValueEmitter)?); + *out_value = Some(tri!(value.serialize(NumberValueEmitter))); Ok(()) } else { Err(invalid_number()) @@ -650,7 +659,7 @@ impl serde::ser::SerializeStruct for SerializeMap { #[cfg(feature = "raw_value")] SerializeMap::RawValue { out_value } => { if key == crate::raw::TOKEN { - *out_value = Some(value.serialize(RawValueEmitter)?); + *out_value = Some(tri!(value.serialize(RawValueEmitter))); Ok(()) } else { Err(invalid_raw_value()) diff --git a/tests/lexical.rs b/tests/lexical.rs index d3dfb85..368c844 100644 --- a/tests/lexical.rs +++ b/tests/lexical.rs @@ -26,11 +26,6 @@ extern crate alloc; #[path = "../src/lexical/mod.rs"] mod lexical; -mod lib { - pub use std::vec::Vec; - pub use std::{cmp, iter, mem, ops}; -} - #[path = "lexical/algorithm.rs"] mod algorithm; diff --git a/tests/lexical/parse.rs b/tests/lexical/parse.rs index 80ca25e..03ec1a9 100644 --- a/tests/lexical/parse.rs +++ b/tests/lexical/parse.rs @@ -1,7 +1,7 @@ // Adapted from https://github.com/Alexhuszagh/rust-lexical. use crate::lexical::num::Float; -use crate::lexical::parse::{parse_concise_float, parse_truncated_float}; +use crate::lexical::{parse_concise_float, parse_truncated_float}; use core::f64; use core::fmt::Debug; diff --git a/tests/map.rs b/tests/map.rs index ae01969..538cd16 100644 --- a/tests/map.rs +++ b/tests/map.rs @@ -35,7 +35,6 @@ fn test_append() { assert!(val.is_empty()); } -#[cfg(not(no_btreemap_retain))] #[test] fn test_retain() { let mut v: Value = from_str(r#"{"b":null,"a":null,"c":null}"#).unwrap(); diff --git a/tests/regression/issue1004.rs b/tests/regression/issue1004.rs new file mode 100644 index 0000000..c09fb96 --- /dev/null +++ b/tests/regression/issue1004.rs @@ -0,0 +1,12 @@ +#![cfg(feature = "arbitrary_precision")] + +#[test] +fn test() { + let float = 5.55f32; + let value = serde_json::to_value(float).unwrap(); + let json = serde_json::to_string(&value).unwrap(); + + // If the f32 were cast to f64 by Value before serialization, then this + // would incorrectly serialize as 5.550000190734863. + assert_eq!(json, "5.55"); +} diff --git a/tests/regression/issue845.rs b/tests/regression/issue845.rs index 56037ae..e8b0c0f 100644 --- a/tests/regression/issue845.rs +++ b/tests/regression/issue845.rs @@ -1,7 +1,6 @@ #![allow(clippy::trait_duplication_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/8757 use serde::{Deserialize, Deserializer}; -use std::convert::TryFrom; use std::fmt::{self, Display}; use std::marker::PhantomData; use std::str::FromStr; diff --git a/tests/test.rs b/tests/test.rs index 6c08cc8..05b7f86 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -14,9 +14,6 @@ clippy::vec_init_then_push, clippy::zero_sized_map_values )] -#![cfg_attr(feature = "trace-macros", feature(trace_macros))] -#[cfg(feature = "trace-macros")] -trace_macros!(true); #[macro_use] mod macros; @@ -33,11 +30,12 @@ use serde_json::{ from_reader, from_slice, from_str, from_value, json, to_string, to_string_pretty, to_value, to_vec, Deserializer, Number, Value, }; -use std::collections::hash_map::DefaultHasher; use std::collections::BTreeMap; #[cfg(feature = "raw_value")] use std::collections::HashMap; use std::fmt::{self, Debug}; +use std::hash::BuildHasher; +#[cfg(feature = "raw_value")] use std::hash::{Hash, Hasher}; use std::io; use std::iter; @@ -53,7 +51,7 @@ macro_rules! treemap { () => { BTreeMap::new() }; - ($($k:expr => $v:expr),+) => { + ($($k:expr => $v:expr),+ $(,)?) => { { let mut m = BTreeMap::new(); $( @@ -160,17 +158,29 @@ fn test_write_f64() { #[test] fn test_encode_nonfinite_float_yields_null() { - let v = to_value(::std::f64::NAN).unwrap(); + let v = to_value(::std::f64::NAN.copysign(1.0)).unwrap(); + assert!(v.is_null()); + + let v = to_value(::std::f64::NAN.copysign(-1.0)).unwrap(); assert!(v.is_null()); let v = to_value(::std::f64::INFINITY).unwrap(); assert!(v.is_null()); - let v = to_value(::std::f32::NAN).unwrap(); + let v = to_value(-::std::f64::INFINITY).unwrap(); + assert!(v.is_null()); + + let v = to_value(::std::f32::NAN.copysign(1.0)).unwrap(); + assert!(v.is_null()); + + let v = to_value(::std::f32::NAN.copysign(-1.0)).unwrap(); assert!(v.is_null()); let v = to_value(::std::f32::INFINITY).unwrap(); assert!(v.is_null()); + + let v = to_value(-::std::f32::INFINITY).unwrap(); + assert!(v.is_null()); } #[test] @@ -264,7 +274,7 @@ fn test_write_object() { ( treemap!( "a".to_string() => true, - "b".to_string() => false + "b".to_string() => false, ), "{\"a\":true,\"b\":false}", ), @@ -275,7 +285,7 @@ fn test_write_object() { treemap![ "a".to_string() => treemap![], "b".to_string() => treemap![], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], "{\"a\":{},\"b\":{},\"c\":{}}", ), @@ -284,10 +294,10 @@ fn test_write_object() { "a".to_string() => treemap![ "a".to_string() => treemap!["a" => vec![1,2,3]], "b".to_string() => treemap![], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], "b".to_string() => treemap![], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], "{\"a\":{\"a\":{\"a\":[1,2,3]},\"b\":{},\"c\":{}},\"b\":{},\"c\":{}}", ), @@ -297,9 +307,9 @@ fn test_write_object() { "b".to_string() => treemap![ "a".to_string() => treemap!["a" => vec![1,2,3]], "b".to_string() => treemap![], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], "{\"a\":{},\"b\":{\"a\":{\"a\":[1,2,3]},\"b\":{},\"c\":{}},\"c\":{}}", ), @@ -310,8 +320,8 @@ fn test_write_object() { "c".to_string() => treemap![ "a".to_string() => treemap!["a" => vec![1,2,3]], "b".to_string() => treemap![], - "c".to_string() => treemap![] - ] + "c".to_string() => treemap![], + ], ], "{\"a\":{},\"b\":{},\"c\":{\"a\":{\"a\":[1,2,3]},\"b\":{},\"c\":{}}}", ), @@ -324,7 +334,7 @@ fn test_write_object() { treemap![ "a".to_string() => treemap![], "b".to_string() => treemap![], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], pretty_str!({ "a": {}, @@ -337,10 +347,10 @@ fn test_write_object() { "a".to_string() => treemap![ "a".to_string() => treemap!["a" => vec![1,2,3]], "b".to_string() => treemap![], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], "b".to_string() => treemap![], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], pretty_str!({ "a": { @@ -364,9 +374,9 @@ fn test_write_object() { "b".to_string() => treemap![ "a".to_string() => treemap!["a" => vec![1,2,3]], "b".to_string() => treemap![], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], pretty_str!({ "a": {}, @@ -391,8 +401,8 @@ fn test_write_object() { "c".to_string() => treemap![ "a".to_string() => treemap!["a" => vec![1,2,3]], "b".to_string() => treemap![], - "c".to_string() => treemap![] - ] + "c".to_string() => treemap![], + ], ], pretty_str!({ "a": {}, @@ -423,7 +433,7 @@ fn test_write_object() { ( treemap!( "a".to_string() => true, - "b".to_string() => false + "b".to_string() => false, ), pretty_str!( { "a": true, @@ -1192,8 +1202,8 @@ fn test_parse_object() { treemap!( "a".to_string() => treemap!( "b".to_string() => 3u64, - "c".to_string() => 4 - ) + "c".to_string() => 4, + ), ), )]); @@ -1369,7 +1379,7 @@ fn test_parse_enum() { ), treemap!( "a".to_string() => Animal::Dog, - "b".to_string() => Animal::Frog("Henry".to_string(), vec![]) + "b".to_string() => Animal::Frog("Henry".to_string(), vec![]), ), )]); } @@ -1452,7 +1462,6 @@ fn test_serialize_seq_with_no_len() { where T: ser::Serialize, { - #[inline] fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, @@ -1479,7 +1488,6 @@ fn test_serialize_seq_with_no_len() { formatter.write_str("array") } - #[inline] fn visit_unit(self) -> Result, E> where E: de::Error, @@ -1487,7 +1495,6 @@ fn test_serialize_seq_with_no_len() { Ok(MyVec(Vec::new())) } - #[inline] fn visit_seq(self, mut visitor: V) -> Result, V::Error> where V: de::SeqAccess<'de>, @@ -1538,7 +1545,6 @@ fn test_serialize_map_with_no_len() { K: ser::Serialize + Ord, V: ser::Serialize, { - #[inline] fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, @@ -1566,7 +1572,6 @@ fn test_serialize_map_with_no_len() { formatter.write_str("map") } - #[inline] fn visit_unit(self) -> Result, E> where E: de::Error, @@ -1574,7 +1579,6 @@ fn test_serialize_map_with_no_len() { Ok(MyMap(BTreeMap::new())) } - #[inline] fn visit_map(self, mut visitor: Visitor) -> Result, Visitor::Error> where Visitor: de::MapAccess<'de>, @@ -1660,23 +1664,12 @@ fn test_deserialize_from_stream() { assert_eq!(request, response); } -#[test] -fn test_serialize_rejects_bool_keys() { - let map = treemap!( - true => 2, - false => 4 - ); - - let err = to_vec(&map).unwrap_err(); - assert_eq!(err.to_string(), "key must be a string"); -} - #[test] fn test_serialize_rejects_adt_keys() { let map = treemap!( Some("a") => 2, Some("b") => 4, - None => 6 + None => 6, ); let err = to_vec(&map).unwrap_err(); @@ -1890,23 +1883,41 @@ fn test_integer_key() { // map with integer keys let map = treemap!( 1 => 2, - -1 => 6 + -1 => 6, ); let j = r#"{"-1":6,"1":2}"#; test_encode_ok(&[(&map, j)]); test_parse_ok(vec![(j, map)]); - let j = r#"{"x":null}"#; - test_parse_err::>(&[( - j, - "invalid type: string \"x\", expected i32 at line 1 column 4", - )]); + test_parse_err::>(&[ + ( + r#"{"x":null}"#, + "invalid value: expected key to be a number in quotes at line 1 column 2", + ), + ( + r#"{" 123":null}"#, + "invalid value: expected key to be a number in quotes at line 1 column 2", + ), + (r#"{"123 ":null}"#, "expected `\"` at line 1 column 6"), + ]); + + let err = from_value::>(json!({" 123":null})).unwrap_err(); + assert_eq!( + err.to_string(), + "invalid value: expected key to be a number in quotes", + ); + + let err = from_value::>(json!({"123 ":null})).unwrap_err(); + assert_eq!( + err.to_string(), + "invalid value: expected key to be a number in quotes", + ); } #[test] fn test_integer128_key() { let map = treemap! { - 100000000000000000000000000000000000000u128 => () + 100000000000000000000000000000000000000u128 => (), }; let j = r#"{"100000000000000000000000000000000000000":null}"#; assert_eq!(to_string(&map).unwrap(), j); @@ -1914,23 +1925,106 @@ fn test_integer128_key() { } #[test] -fn test_deny_float_key() { - #[derive(Eq, PartialEq, Ord, PartialOrd)] +fn test_float_key() { + #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone)] struct Float; impl Serialize for Float { fn serialize(&self, serializer: S) -> Result where S: Serializer, { - serializer.serialize_f32(1.0) + serializer.serialize_f32(1.23) + } + } + impl<'de> Deserialize<'de> for Float { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + f32::deserialize(deserializer).map(|_| Float) } } // map with float key - let map = treemap!(Float => "x"); + let map = treemap!(Float => "x".to_owned()); + let j = r#"{"1.23":"x"}"#; + + test_encode_ok(&[(&map, j)]); + test_parse_ok(vec![(j, map)]); + + let j = r#"{"x": null}"#; + test_parse_err::>(&[( + j, + "invalid value: expected key to be a number in quotes at line 1 column 2", + )]); +} + +#[test] +fn test_deny_non_finite_f32_key() { + // We store float bits so that we can derive Ord, and other traits. In a + // real context the code might involve a crate like ordered-float. + + #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone)] + struct F32Bits(u32); + impl Serialize for F32Bits { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_f32(f32::from_bits(self.0)) + } + } + + let map = treemap!(F32Bits(f32::INFINITY.to_bits()) => "x".to_owned()); + assert!(serde_json::to_string(&map).is_err()); + assert!(serde_json::to_value(map).is_err()); + + let map = treemap!(F32Bits(f32::NEG_INFINITY.to_bits()) => "x".to_owned()); + assert!(serde_json::to_string(&map).is_err()); + assert!(serde_json::to_value(map).is_err()); + + let map = treemap!(F32Bits(f32::NAN.to_bits()) => "x".to_owned()); + assert!(serde_json::to_string(&map).is_err()); assert!(serde_json::to_value(map).is_err()); } +#[test] +fn test_deny_non_finite_f64_key() { + // We store float bits so that we can derive Ord, and other traits. In a + // real context the code might involve a crate like ordered-float. + + #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone)] + struct F64Bits(u64); + impl Serialize for F64Bits { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_f64(f64::from_bits(self.0)) + } + } + + let map = treemap!(F64Bits(f64::INFINITY.to_bits()) => "x".to_owned()); + assert!(serde_json::to_string(&map).is_err()); + assert!(serde_json::to_value(map).is_err()); + + let map = treemap!(F64Bits(f64::NEG_INFINITY.to_bits()) => "x".to_owned()); + assert!(serde_json::to_string(&map).is_err()); + assert!(serde_json::to_value(map).is_err()); + + let map = treemap!(F64Bits(f64::NAN.to_bits()) => "x".to_owned()); + assert!(serde_json::to_string(&map).is_err()); + assert!(serde_json::to_value(map).is_err()); +} + +#[test] +fn test_boolean_key() { + let map = treemap!(false => 0, true => 1); + let j = r#"{"false":0,"true":1}"#; + test_encode_ok(&[(&map, j)]); + test_parse_ok(vec![(j, map)]); +} + #[test] fn test_borrowed_key() { let map: BTreeMap<&str, ()> = from_str("{\"borrowed\":null}").unwrap(); @@ -1954,7 +2048,7 @@ fn test_effectively_string_keys() { } let map = treemap! { Enum::One => 1, - Enum::Two => 2 + Enum::Two => 2, }; let expected = r#"{"One":1,"Two":2}"#; test_encode_ok(&[(&map, expected)]); @@ -1964,7 +2058,7 @@ fn test_effectively_string_keys() { struct Wrapper(String); let map = treemap! { Wrapper("zero".to_owned()) => 0, - Wrapper("one".to_owned()) => 1 + Wrapper("one".to_owned()) => 1, }; let expected = r#"{"one":1,"zero":0}"#; test_encode_ok(&[(&map, expected)]); @@ -2385,25 +2479,27 @@ fn test_value_into_deserializer() { let mut map = BTreeMap::new(); map.insert("inner", json!({ "string": "Hello World" })); + let outer = Outer::deserialize(serde::de::value::MapDeserializer::new( + map.iter().map(|(k, v)| (*k, v)), + )) + .unwrap(); + assert_eq!(outer.inner.string, "Hello World"); + let outer = Outer::deserialize(map.into_deserializer()).unwrap(); assert_eq!(outer.inner.string, "Hello World"); } #[test] fn hash_positive_and_negative_zero() { - fn hash(obj: impl Hash) -> u64 { - let mut hasher = DefaultHasher::new(); - obj.hash(&mut hasher); - hasher.finish() - } + let rand = std::hash::RandomState::new(); let k1 = serde_json::from_str::("0.0").unwrap(); let k2 = serde_json::from_str::("-0.0").unwrap(); if cfg!(feature = "arbitrary_precision") { assert_ne!(k1, k2); - assert_ne!(hash(k1), hash(k2)); + assert_ne!(rand.hash_one(k1), rand.hash_one(k2)); } else { assert_eq!(k1, k2); - assert_eq!(hash(k1), hash(k2)); + assert_eq!(rand.hash_one(k1), rand.hash_one(k2)); } } diff --git a/tests/ui/parse_key.stderr b/tests/ui/parse_key.stderr index f10c218..15662dc 100644 --- a/tests/ui/parse_key.stderr +++ b/tests/ui/parse_key.stderr @@ -2,4 +2,4 @@ error[E0609]: no field `s` on type `&'static str` --> tests/ui/parse_key.rs:4:16 | 4 | json!({ "".s : true }); - | ^ + | ^ unknown field -- cgit v1.2.3