ron-0.12.0/.cargo_vcs_info.json0000644000000001360000000000100117120ustar { "git": { "sha1": "c6a8cff1eff986cc1ad9b026e838335187d411f0" }, "path_in_vcs": "" }ron-0.12.0/.github/actions/setup/action.yml000064400000000000000000000006061046102023000166440ustar 00000000000000name: Setup Rust Environment inputs: key: description: Cache key required: true runs: using: composite steps: - uses: actions/cache@v4 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ inputs.key }}-cargo-${{ hashFiles('Cargo.lock') }} ron-0.12.0/.github/dependabot.yml000064400000000000000000000002621046102023000146720ustar 00000000000000version: 2 updates: - package-ecosystem: cargo directory: "/" schedule: interval: daily open-pull-requests-limit: 10 assignees: - torkleyy labels: - dependency ron-0.12.0/.github/pull_request_template.md000064400000000000000000000000611046102023000170000ustar 00000000000000 * [ ] I've included my change in `CHANGELOG.md` ron-0.12.0/.github/workflows/ci.yaml000064400000000000000000000140131046102023000153550ustar 00000000000000name: CI on: push: branches: - master - "v*.*" pull_request: branches: - master - "v*.*" schedule: - cron: "0 0 * * 0" jobs: lock: name: Cargo.lock runs-on: ubuntu-latest steps: - name: Checkout the Repository uses: actions/checkout@v4 - name: Install the Rust toolchain uses: actions-rs/toolchain@v1 with: toolchain: nightly profile: minimal override: true - name: Generate the minimum version lockfile run: | cargo update -Z minimal-versions cargo update -Z direct-minimal-versions mv Cargo.lock Cargo.lock.min - name: Generate the current version lockfile run: cargo update - name: Upload the Cargo lockfiles uses: actions/upload-artifact@v4 with: name: Cargo.lock path: | Cargo.lock Cargo.lock.min check: name: Check (${{ matrix.rust }}${{ matrix.lock == '"Cargo.lock.min"' && ' (Cargo.lock.min)' || ''}}) runs-on: ubuntu-latest needs: lock strategy: matrix: rust: [1.64.0, stable, nightly] lock: ["Cargo.lock", "Cargo.lock.min"] steps: - uses: actions/checkout@v4 - name: Download the Cargo lockfiles uses: actions/download-artifact@v4 with: name: Cargo.lock - name: Rename ${{ matrix.lock }} to Cargo.lock run: mv ${{ matrix.lock }} Cargo.lock if: ${{ matrix.lock != 'Cargo.lock' }} - uses: ./.github/actions/setup with: key: test-${{ matrix.rust }}-${{ matrix.lock }} - uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} profile: minimal components: clippy override: true - uses: taiki-e/install-action@cargo-hack - name: Downgrade dependencies to MSRV 1.64 run: | cargo update -p serde_bytes --precise 0.11.16 cargo update -p indexmap --precise 2.5.0 cargo update -p once_cell --precise 1.20.3 cargo update -p quote --precise 1.0.41 cargo update -p syn$(perl -ne 'm/syn (2\.\d+\.\d+)/ && print "\@$1\n"' Cargo.lock) --precise 2.0.106 cargo update -p typetag --precise 0.2.20 cargo update -p erased-serde --precise 0.4.8 if: ${{ matrix.rust == '1.64.0' }} - name: Check run: | cargo hack check --feature-powerset --no-dev-deps - name: Clean run: | cargo clean - name: Clippy run: | cargo hack clippy --feature-powerset --no-dev-deps -- -D warnings -A unknown-lints # Clippy will report lints that cannot be acted on without increasing MSRV on later versions of Rust if: ${{ matrix.rust == '1.64.0' }} - name: Clean run: | cargo clean - name: Test run: | cargo hack test --feature-powerset --clean-per-run clippy-fuzz: name: "Clippy: Fuzzer" runs-on: ubuntu-latest continue-on-error: true steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup with: key: clippy-fuzz - uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal components: clippy override: true - run: cd fuzz && cargo clippy --all -- -D warnings rustfmt: name: "Format: stable" runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup with: key: rustfmt-stable - uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal components: rustfmt override: true - run: cargo fmt --all -- --check - run: cd fuzz && cargo fmt --all -- --check coverage: name: "Coverage: stable" runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup with: key: coverage - uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal components: llvm-tools-preview override: true - name: Download grcov run: | curl -sL https://github.com/mozilla/grcov/releases/download/v0.8.18/grcov-x86_64-unknown-linux-gnu.tar.bz2 | tar jxf - chmod +x ./grcov - name: Generate the coverage data run: | cargo clean cargo test --all-targets cargo test --features integer128 --all-targets cargo test --features indexmap --all-targets cargo test --all-features --all-targets env: CARGO_INCREMENTAL: 0 RUSTFLAGS: -Cinstrument-coverage RUSTDOCFLAGS: -Cinstrument-coverage LLVM_PROFILE_FILE: coverage/coverage-%p-%m.profraw - name: Generate the coverage reports run: | ./grcov . -s . --binary-path ./target/debug/deps \ -t lcov -o coverage.lcov --branch \ --keep-only "src/*" \ --keep-only "tests/*" \ --ignore-not-existing \ --excl-line GRCOV_EXCL_LINE \ --excl-start GRCOV_EXCL_START \ --excl-stop GRCOV_EXCL_STOP ./grcov . -s . --binary-path ./target/debug/deps \ -t html --branch \ --keep-only "src/*" \ --keep-only "tests/*" \ --ignore-not-existing \ --excl-line GRCOV_EXCL_LINE \ --excl-start GRCOV_EXCL_START \ --excl-stop GRCOV_EXCL_STOP rm -rf html/badges # - name: Upload the coverage report to codecov.io # uses: codecov/codecov-action@v1 # with: # files: coverage.lcov # fail_ci_if_error: true - name: Deploy the code coverage report uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./html destination_dir: coverage if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' ron-0.12.0/.github/workflows/cifuzz.yml000064400000000000000000000012651046102023000161400ustar 00000000000000name: CIFuzz on: [pull_request] jobs: Fuzzing: runs-on: ubuntu-latest steps: - name: Build Fuzzers id: build uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master with: oss-fuzz-project-name: 'ron' dry-run: false language: rust - name: Run Fuzzers uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master with: oss-fuzz-project-name: 'ron' fuzz-seconds: 300 dry-run: false language: rust - name: Upload Crash uses: actions/upload-artifact@v4 if: failure() && steps.build.outcome == 'success' with: name: artifacts path: ./out/artifacts ron-0.12.0/.gitignore000064400000000000000000000002431046102023000124710ustar 00000000000000# Compiled files *.o *.so *.rlib *.dll # Executables *.exe # Generated by Cargo /target/ /Cargo.lock # VSCode Extensions /.trunk # MacOS Shenanigans .DS_Store ron-0.12.0/CHANGELOG.md000064400000000000000000000473061046102023000123250ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased ## [0.12.0] - 2025-11-12 ### API Changes - Breaking: Removed the `ron::error::Error::Base64Error` variant. ([#566](https://github.com/ron-rs/ron/pull/566)) - Added `into_inner()` method to `ron::ser::Serializer` to retrieve the inner writer. ([#588](https://github.com/ron-rs/ron/pull/588)) - Removed the `base64` dependency. ([#566](https://github.com/ron-rs/ron/pull/566)) ### Format Changes - **Format-Breaking:** Remote base64-encoded byte strings deserialisation, replaced by Rusty byte strings in v0.9.0 ([#566](https://github.com/ron-rs/ron/pull/566)) ### Bug Fixes - Fixed untagged enum deserialisation for serde >= 1.0.220 with better serde content detection ([#582](https://github.com/ron-rs/ron/pull/582)) ## [0.11.0] - 2025-08-27 ### API Changes - Breaking: `SpannedError` now stores the full error span in span: Span { start: Position, end: Position }`, to facilitate, e.g., language server highlighting of syntax errors. - Breaking: Added `no_std` support via a new `std` feature (enabled by default). With default features disabled, you must enable the `std` feature to access `de::from_reader`, and the `std::io` operations on `Options`, such as `from_reader`, `from_reader_seed`, `to_io_writer`, and `to_io_writer_pretty` ([#567](https://github.com/ron-rs/ron/pull/567)) - Breaking: Fixed (again) `ron::value::Number` to ensure it is non-exhaustive, to avoid breaking `match`es when feature unification enables more of its variants than expected ([#568](https://github.com/ron-rs/ron/pull/568)) ### Examples - Add a new example `file_read_write_vec.rs` for reading and writing `Vec` to/from files. ([#573](https://github.com/ron-rs/ron/pull/573)) ## [0.10.1] - 2025-04-08 ### API Changes - Add `ron::Options::to_io_writer` and `ron::Options::to_io_writer_pretty` to allow writing into an `io::Writer` ([#561](https://github.com/ron-rs/ron/pull/561)) - Breaking: `ron::value::Number` is now non-exhaustive, to avoid breaking `match`es when feature unification enables more of its variants than expected ([#564](https://github.com/ron-rs/ron/pull/564)) ## [0.9.0] - 2025-03-18 ### API Changes - Add `ron::value::RawValue` helper type which can (de)serialize any valid RON ([#407](https://github.com/ron-rs/ron/pull/407)) - Add `escape_strings` option to `PrettyConfig` to allow serialising with or without escaping ([#426](https://github.com/ron-rs/ron/pull/426)) - Add `compact_maps` and `compact_structs` options to `PrettyConfig` to allow serialising maps and structs on a single line ([#448](https://github.com/ron-rs/ron/pull/448)) - Add minimal support for `#[serde(flatten)]` with roundtripping through RON maps ([#455](https://github.com/ron-rs/ron/pull/455)) - Add minimal roundtripping support for `#[serde(tag = "tag")]`, `#[serde(tag = "tag", content = "content")]`, and `#[serde(untagged)]` enums ([#451](https://github.com/ron-rs/ron/pull/451)) - Breaking: Expand the `value::Number` enum to explicitly encode all possible number types ([#479](https://github.com/ron-rs/ron/pull/479)) - Add `number_suffixes` option to `PrettyConfig` to allow serialising numbers with their explicit type suffix, e.g. `42i32` ([#481](https://github.com/ron-rs/ron/pull/481)) - Allow `ron::value::RawValue` to capture any whitespace to the left and right of a ron value ([#487](https://github.com/ron-rs/ron/pull/487)) - Breaking: Enforce that ron always writes valid UTF-8 ([#488](https://github.com/ron-rs/ron/pull/488)) - Add convenient `Value::from` impls ([#498](https://github.com/ron-rs/ron/pull/498)) - Add new extension `explicit_struct_names` which requires that struct names are included during deserialization ([#522](https://github.com/ron-rs/ron/pull/522)) - Add new path-based field metadata serialization support via `PrettyConfig` ([#544](https://github.com/ron-rs/ron/pull/544)) - Breaking: Change `PrettyConfig` so that `new_line`, `indentor` and `separator` are all `Cow<'static, str>` instead of `String` ([#546](https://github.com/ron-rs/ron/pull/546)) ### Format Changes - [Non-API] Breaking: Treat `Some` like a newtype variant with `unwrap_variant_newtypes` ([#465](https://github.com/ron-rs/ron/pull/465)) - Allow parsing floating point literals with underscores ([#481](https://github.com/ron-rs/ron/pull/481)) - **Format-Breaking:** Switch from base64-encoded to Rusty byte strings, still allow base64 deserialising for now ([#438](https://github.com/ron-rs/ron/pull/438)) - Fix issue [#241](https://github.com/ron-rs/ron/issues/241) and allow parsing numbers with explicit type suffixes, e.g. `1u8` or `-1f32` ([#481](https://github.com/ron-rs/ron/pull/481)) - Add support for byte literals as strongly typed unsigned 8-bit integers ([#438](https://github.com/ron-rs/ron/pull/438)) - Fix issue [#321](https://github.com/ron-rs/ron/issues/321) and allow parsing UTF-8 identifiers ([#488](https://github.com/ron-rs/ron/pull/488)) ### Bug Fixes - Fix parsing `r` as a self-describing struct or variant name (and not the start of a raw string) ([#465](https://github.com/ron-rs/ron/pull/465)) - Fix serialising raw strings containing a literal backslash ([#465](https://github.com/ron-rs/ron/pull/465)) - Fix serialising `None` inside a stack of nested `Option`s with `#![enable(implicit_some)]` enabled ([#465](https://github.com/ron-rs/ron/pull/465)) - Fix deserialising deserialising `A('/')` into a `ron::Value` ([#465](https://github.com/ron-rs/ron/pull/465)) - Fix issue [#445](https://github.com/ron-rs/ron/issues/445) and allow parsing `+unsigned` as an unsigned int ([#479](https://github.com/ron-rs/ron/pull/479)) - Fix serialising reserved identifiers `true`, `false`, `Some`, `None`, `inf`[`f32`|`f64`], and `Nan`[`f32`|`f64`] ([#487](https://github.com/ron-rs/ron/pull/487)) - Disallow unclosed line comments at the end of `ron::value::RawValue` ([#489](https://github.com/ron-rs/ron/pull/489)) - Fix parsing of struct/variant names starting in `None`, `Some`, `true`, or `false` ([#499](https://github.com/ron-rs/ron/pull/499)) - Fix deserialising owned string field names in structs, allowing deserializing into `serde_json::Value`s ([#511](https://github.com/ron-rs/ron/pull/512)) ### Miscellaneous - Add CIFuzz GitHub action ([#429](https://github.com/ron-rs/ron/pull/429)) - Update the arbitrary fuzzer to check arbitrary serde data types, values, and `ron::ser::PrettyConfig`s ([#465](https://github.com/ron-rs/ron/pull/465)) - Add a benchmark for PRs that runs over the latest fuzzer corpus ([#465](https://github.com/ron-rs/ron/pull/465)) - Fuzz serde enum representations and collect current limitations in ron and serde ([#502](https://github.com/ron-rs/ron/pull/502)) - Update `base64` dependency to version 0.22 ([#529](https://github.com/ron-rs/ron/pull/529)) - Fix issue [#556](https://github.com/ron-rs/ron/issues/556) and update minium dependency versions ([#557](https://github.com/ron-rs/ron/pull/557)) ## [0.8.1] - 2023-08-17 - Fix issues [#277](https://github.com/ron-rs/ron/issues/277) and [#405](https://github.com/ron-rs/ron/issues/405) with `Value::Map` `IntoIter` and extraneous item check for `Value::Seq` ([#406](https://github.com/ron-rs/ron/pull/406)) - Fix issue [#401](https://github.com/ron-rs/ron/issues/401) with correct raw struct name identifier parsing ([#402](https://github.com/ron-rs/ron/pull/402)) - Fix issue [#410](https://github.com/ron-rs/ron/issues/410) trailing comma parsing in tuples and `Some` ([#412](https://github.com/ron-rs/ron/pull/412)) - Error instead of panic when deserializing non-identifiers as field names ([#415](https://github.com/ron-rs/ron/pull/415)) - [Non-API] Breaking: Fix issue [#307](https://github.com/ron-rs/ron/issues/307) stack overflow with explicit recursion limits in serialising and deserialising ([#420](https://github.com/ron-rs/ron/pull/420)) - Fix issue [#423](https://github.com/ron-rs/ron/issues/423) deserialising an identifier into a borrowed str ([#424](https://github.com/ron-rs/ron/pull/424)) - Bump MSRV to 1.57.0 and bump dependency: `base64` to 0.20 ([#431](https://github.com/ron-rs/ron/pull/431)) - Bump dependency `base64` to 0.21 ([#433](https://github.com/ron-rs/ron/pull/433)) - Depend on `serde_derive` directly to potentially enable more compilation parallelism ([#441](https://github.com/ron-rs/ron/pull/441)) - [Non-API] Breaking: Bump `bitflags` dependency to 2.0, changes `serde` impls of `Extensions` ([#443](https://github.com/ron-rs/ron/pull/443)) - Add `Map::retain` method ([#460](https://github.com/ron-rs/ron/pull/460)) - Bump MSRV to 1.64.0 and bump dependency: `indexmap` to 2.0 ([#459](https://github.com/ron-rs/ron/pull/459)) ## [0.8.0] - 2022-08-17 - Bump dependencies: `bitflags` to 1.3, `indexmap` to 1.9 ([#399](https://github.com/ron-rs/ron/pull/399)) - Add `integer128` feature that guards `i128` and `u128` ([#304](https://github.com/ron-rs/ron/pull/304), [#351](https://github.com/ron-rs/ron/pull/351)) - Fix issue [#265](https://github.com/ron-rs/ron/issues/265) with better missing comma error ([#353](https://github.com/ron-rs/ron/pull/353)) - Fix issue [#301](https://github.com/ron-rs/ron/issues/301) with better error messages ([#354](https://github.com/ron-rs/ron/pull/354)) - Fix issue [#337](https://github.com/ron-rs/ron/issues/337) by removing `decimal_floats` PrettyConfig option and unconditional decimals in floats ([#363](https://github.com/ron-rs/ron/pull/363)) - Fix issue [#203](https://github.com/ron-rs/ron/issues/203) with full de error positioning ([#356](https://github.com/ron-rs/ron/pull/356)) - Expand the `ron::Error` enum to distinguish `serde` errors like `NoSuchEnumVariant` and `MissingStructField` with error positioning ([#394](https://github.com/ron-rs/ron/pull/394)) - Bump MSRV to 1.56.0 ([#396](https://github.com/ron-rs/ron/pull/396)) ## [0.7.1] - 2022-06-15 - Add `struct_names` option to `PrettyConfig` ([#329](https://github.com/ron-rs/ron/pull/329)) - Fix newtype variant unwrapping around enum, seq and map ([#331](https://github.com/ron-rs/ron/pull/331)) - Implement `unwrap_newtypes` extension during serialization ([#333](https://github.com/ron-rs/ron/pull/333)) - Implement `unwrap_variant_newtypes` extension during serialization ([#336](https://github.com/ron-rs/ron/pull/336)) - Add `compact_arrays` ([#299](https://github.com/ron-rs/ron/pull/299)) and `separator` options to `PrettyConfig` ([#349](https://github.com/ron-rs/ron/pull/349)) - Fix issue [#338](https://github.com/ron-rs/ron/issues/338) value map roundtrip ([#341](https://github.com/ron-rs/ron/pull/341)) - Fix issue [#289](https://github.com/ron-rs/ron/issues/289) enumerate_arrays comments ([#344](https://github.com/ron-rs/ron/pull/344)) - Report struct name in expected struct error ([#342](https://github.com/ron-rs/ron/pull/342)) - Add `Options` builder to configure the RON serde roundtrip ([#343](https://github.com/ron-rs/ron/pull/343)) - Fix issue [#367](https://github.com/ron-rs/ron/issues/367) with eager implicit some ([#368](https://github.com/ron-rs/ron/pull/368)) - Fix issue [#359](https://github.com/ron-rs/ron/issues/359) with `DeserializeSeed` support ([#360](https://github.com/ron-rs/ron/pull/360)) - Fix issue [#370](https://github.com/ron-rs/ron/issues/370) with `FromStr`-equivalent float EBNF and `Error::FloatUnderscore` ([#371](https://github.com/ron-rs/ron/pull/371)) - Fix issue [#374](https://github.com/ron-rs/ron/issues/374) extraneous .0 for small floats ([#372](https://github.com/ron-rs/ron/pull/372)) - Deprecate `Serializer::new` ([#382](https://github.com/ron-rs/ron/issues/382)) ## [0.7.0] - 2021-10-22 - Add `unwrap_variant_newtypes` extension ([#319](https://github.com/ron-rs/ron/pull/319)) - Change MSRV to 1.36 ## [0.6.6] - 2021-10-21 - Fix serialization of raw identifiers ([#323](https://github.com/ron-rs/ron/pull/323)) ## [0.6.5] - 2021-09-09 - support serde renames that start with a digit ## [0.6.3] - 2020-12-18 - bump `base64` dependency to 0.13 ## [0.6.2] - 2020-09-09 - Added `decimal_floats` PrettyConfig option, which always includes decimals in floats (`1.0` vs `1`) ([#237](https://github.com/ron-rs/ron/pull/237)) - Fixed EBNF grammar for raw strings ([#236](https://github.com/ron-rs/ron/pull/236), unsigned integers ([#248](https://github.com/ron-rs/ron/pull/248)), and nested comments ([#272](https://github.com/ron-rs/ron/pull/272)) - Added `ser::to_writer_pretty` ([#269](https://github.com/ron-rs/ron/pull/269)) - Sped up parsing using table predicates ([#276](https://github.com/ron-rs/ron/pull/276)) ## [0.6.1] - 2020-07-14 ### Fixes - Fix array formatting regression ([#260](https://github.com/ron-rs/ron/pull/260)) ## [0.6.0] - 2020-05-21 ### Additions - Implement integer support in Numbers ([#210](https://github.com/ron-rs/ron/pull/210)) - Port `ser::Serializer` to `io::Write` ([#206](https://github.com/ron-rs/ron/pull/206)) - Support i128 and u128 ([#219](https://github.com/ron-rs/ron/pull/219)) - Allow pretty ser to work with implicit-some extension ([#182](https://github.com/ron-rs/ron/pull/182)) - Make PrettyConfig future-proof ([#173](https://github.com/ron-rs/ron/pull/173)) - Use indexmap to preserve order (optional) ([#172](https://github.com/ron-rs/ron/pull/172)) - Add tests for different enum representations ([#166](https://github.com/ron-rs/ron/pull/166)) - Implement inf, -inf and NaN handling ([#163](https://github.com/ron-rs/ron/pull/163)) - Add VS code language tooling ([#160](https://github.com/ron-rs/ron/pull/160)) - Be smarter about integer deserialization ([#157](https://github.com/ron-rs/ron/pull/157)) ### Fixes - Fix parsing of borrowed strings ([#228](https://github.com/ron-rs/ron/pull/228)) - Fix depth limit test for off-by-one fix ([#225](https://github.com/ron-rs/ron/pull/225)) - Remove deprecated uses of `Error::description` ([#208](https://github.com/ron-rs/ron/pull/208)) - Preserve ordering of map sequences ([#197](https://github.com/ron-rs/ron/pull/197)) - Remove unneeded Neg requirement for signed_integer ([#193](https://github.com/ron-rs/ron/pull/193)) - Ensure "Untagged tuple-like enum variants not deserializing correctly……" is fixed ([#170](https://github.com/ron-rs/ron/pull/170)) ### Changes - Update `serde` requirement to 1.0.60 ([#226](https://github.com/ron-rs/ron/pull/226)) - Replace Travis with GitHub actions ([#223](https://github.com/ron-rs/ron/pull/223)) - Rename `format_doc_comments` to `format_code_in_doc_comment` - Update base64 requirement from 0.11 to 0.12 ([#204](https://github.com/ron-rs/ron/pull/204)) - Update base64 requirement from 0.10 to 0.11 ([#195](https://github.com/ron-rs/ron/pull/195)) - Update `serde_bytes` to 0.11 ([#164](https://github.com/ron-rs/ron/pull/164)) ## [0.5.1] - 2019-04-05 ### Fixes - Increase source compability from Rust `1.34.0` to `1.31.0` by not relying on `as _` imports ([#156](https://github.com/ron-rs/ron/pull/156)) ## [0.5.0] - 2019-03-31 ### Additions - Don't insert new lines in empty arrays or maps ([#150](https://github.com/ron-rs/ron/pull/150)) ### Changes - Transition to Rust 2018 ([#149](https://github.com/ron-rs/ron/pull/149)) ## [0.4.2] - 2019-03-01 ### Additions - Add integer check for deserializer ([#148](https://github.com/ron-rs/ron/pull/148)) - Implement `Value::into_rust` ([#146](https://github.com/ron-rs/ron/pull/146)) ## [0.4.1] - 2019-01-09 ### Additions - Allow underscores in integers ([#135](https://github.com/ron-rs/ron/pull/135)) - Added extension documentation ([#130](https://github.com/ron-rs/ron/pull/130)) ### Changes - Move sublime text syntax to separate repo ([#138](https://github.com/ron-rs/ron/pull/138)) - Update `base64` crate dependency to 0.10 ([#137](https://github.com/ron-rs/ron/pull/137)) ## [0.4.0] - 2018-08-11 ### Fixes - Handle tuple deserialization in deserialize_any properly ([#124](https://github.com/ron-rs/ron/pull/124)) ### Changes - Add raw string syntax to grammar ([#125](https://github.com/ron-rs/ron/pull/125)) - Reexport `Value` at root ([#120](https://github.com/ron-rs/ron/pull/120)) ## [0.3.0] - 2018-06-15 ### Additions - `serde_bytes` fields to be encoded using base64. ([#109](https://github.com/ron-rs/ron/pull/109)) ### Fixes - Allow raw string literals ([#114](https://github.com/ron-rs/ron/pull/114)) ### Changes - Now depends on `base64` 0.9.2. ## [0.2.2] - 2018-05-19 ### Fixes - Allow whitespace in newtype variants ([#104](https://github.com/ron-rs/ron/pull/104)) ## [0.2.1] - 2018-05-04 ### Additions - Add multi-line comments ([#98](https://github.com/ron-rs/ron/pull/98)) ### Fixes - Allow more whitespace inside newtypes ([#103](https://github.com/ron-rs/ron/pull/103)) ## [0.2.0] - 2018-02-14 ### Additions - Limit the pretty depth ([#93](https://github.com/ron-rs/ron/pull/93)) - Add support for `\x??` and improve unicode escapes ([#84](https://github.com/ron-rs/ron/pull/84)) ## [0.1.7] - 2018-01-24 ### Additions - Deep array indexing ([#88](https://github.com/ron-rs/ron/pull/88)) - Pretty sequence indexing ([#86](https://github.com/ron-rs/ron/pull/86)) - Add unicode support for chars ([#80](https://github.com/ron-rs/ron/pull/80)) - Add support for hex, oct and bin numbers ([#78](https://github.com/ron-rs/ron/pull/78)) - Allow implicit Some ([#75](https://github.com/ron-rs/ron/pull/75)) - Add grammar specification ([#73](https://github.com/ron-rs/ron/pull/73)) - Add extension support and first extension, unwrap_newtypes ([#72](https://github.com/ron-rs/ron/pull/72)) ### Fixes - Directly serialize `f32` ([#81](https://github.com/ron-rs/ron/pull/81)) ## [0.1.6] - 2018-01-24 ### Additions - Implement sequence indexing ([#87](https://github.com/ron-rs/ron/pull/87)) ### Fixes - Remove ident variable from Sublime syntax ([#71](https://github.com/ron-rs/ron/pull/71)) ## [0.1.5] - 2017-12-27 ### Additions - Allow creating a new serializer ([#70](https://github.com/ron-rs/ron/pull/70)) - Sublime syntax highlighter ([#67](https://github.com/ron-rs/ron/pull/67)) - Add support for integers ([#65](https://github.com/ron-rs/ron/pull/65)) - Implement `Deserializer` for `Value` ([#64](https://github.com/ron-rs/ron/pull/64)) ## [0.1.4] - 2017-10-12 ### Additions - Add `PrettyConfig` ([#61](https://github.com/ron-rs/ron/pull/61)) - impl `deserialize_ignored_any` for `id` ([#60](https://github.com/ron-rs/ron/pull/60)) ### Fixes - Fix deserializing of ignored fields ([#62](https://github.com/ron-rs/ron/pull/62)) ## [0.1.3] - 2017-10-06 ### Fixes - Removed indentation from tuple variant pretty encoder ([#57](https://github.com/ron-rs/ron/pull/57)) ## [0.1.2] - 2017-10-06 ### Fixes - Fix decoding of string literals ([#56](https://github.com/ron-rs/ron/pull/56)) - Add `Value` and implement `deserialize_any` ([#53](https://github.com/ron-rs/ron/pull/53)) ## [0.1.1] - 2017-08-07 ### Fixes - Be more permissive wrt whitespace decoding ([#41](https://github.com/ron-rs/ron/pull/41)) ### Additions - Add utility function to deserialize from `std::io::Read` ([#42](https://github.com/ron-rs/ron/pull/42)) ## [0.1.0] - 2015-08-04 ### Changes - Reorganize deserialization modules ([#30](https://github.com/ron-rs/ron/pull/30)) - Rework deserializer not to require `pom` crate [#27](https://github.com/ron-rs/ron/pull/27), ([#38](https://github.com/ron-rs/ron/pull/38)) - Dual license under Apache 2.0 and MIT ([#26](https://github.com/ron-rs/ron/pull/26)) ### Fixes - Use CRLF for serializatio on Windows ([#32](https://github.com/ron-rs/ron/pull/32)) - Fix bors-ng to work with travis ([#31](https://github.com/ron-rs/ron/pull/31)) - Handle escapes ([#23](https://github.com/ron-rs/ron/pull/23)) ### Additions - Improve error reporting ([#29](https://github.com/ron-rs/ron/pull/29)) - Allow decoding of comments ([#28](https://github.com/ron-rs/ron/pull/28)) - Add `pretty` option to serializer ([#25](https://github.com/ron-rs/ron/pull/25)) - Add roundtrip tests ([#24](https://github.com/ron-rs/ron/pull/24)) ## [0.0.1] - 2015-07-30 Initial release ron-0.12.0/Cargo.lock0000644000000142720000000000100076730ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c70beb79cbb5ce9c4f8e20849978f34225931f665bb49efa6982875a4d5facb3" dependencies = [ "serde", ] [[package]] name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" dependencies = [ "serde", ] [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erased-serde" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "259d404d09818dec19332e31d94558aeb442fea04c817006456c24b5460bbd4b" dependencies = [ "serde", "serde_core", "typeid", ] [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "indexmap" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown", "serde", ] [[package]] name = "inventory" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" dependencies = [ "rustversion", ] [[package]] name = "itoa" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "once_cell" version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "option_set" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6f45dc15724e4f030fde1e2017fa675144e04aa65b7f1e5596554826afbc06e" dependencies = [ "heck", "serde", ] [[package]] name = "proc-macro2" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] [[package]] name = "ron" version = "0.12.0" dependencies = [ "base64", "bitflags", "bytes", "indexmap", "once_cell", "option_set", "serde", "serde_bytes", "serde_derive", "serde_json", "typeid", "typetag", "unicode-ident", "unicode-segmentation", ] [[package]] name = "rustversion" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "serde" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", ] [[package]] name = "serde_bytes" version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "364fec0df39c49a083c9a8a18a23a6bcfd9af130fe9fe321d18520a0d113e09e" dependencies = [ "serde", ] [[package]] name = "serde_core" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "syn" version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "typeid" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typetag" version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73f22b40dd7bfe8c14230cf9702081366421890435b2d625fa92b4acc4c3de6f" dependencies = [ "erased-serde", "inventory", "once_cell", "serde", "typetag-impl", ] [[package]] name = "typetag-impl" version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35f5380909ffc31b4de4f4bdf96b877175a016aa2ca98cee39fcfd8c4d53d952" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-ident" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" [[package]] name = "unicode-segmentation" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" ron-0.12.0/Cargo.toml0000644000000056540000000000100077220ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.64.0" name = "ron" version = "0.12.0" authors = [ "Christopher Durham ", "Dzmitry Malyshau ", "Thomas Schaller ", "Juniper Tyree ", ] description = "Rusty Object Notation" homepage = "https://github.com/ron-rs/ron" documentation = "https://docs.rs/ron/" readme = "README.md" keywords = [ "parser", "serde", "serialization", ] categories = ["encoding"] license = "MIT OR Apache-2.0" repository = "https://github.com/ron-rs/ron" [package.metadata.docs.rs] features = [ "integer128", "indexmap", ] rustdoc-args = ["--generate-link-to-definition"] [[example]] name = "base64" required-features = [] [[example]] name = "decode_file" required-features = ["std"] [[example]] name = "decode" required-features = [] [[example]] name = "encode_file" required-features = ["std"] [[example]] name = "encode" required-features = [] [[example]] name = "transcode" required-features = [] [dependencies.bitflags] version = "2.1" features = ["serde"] default-features = false [dependencies.indexmap] version = "2.0" features = ["serde"] optional = true default-features = false [dependencies.once_cell] version = "1.20" features = [ "alloc", "race", ] default-features = false [dependencies.serde] version = "1.0.181" features = ["alloc"] default-features = false [dependencies.serde_derive] version = "1.0.181" default-features = false [dependencies.typeid] version = "1.0.1" default-features = false [dependencies.unicode-ident] version = "1.0" default-features = false [dependencies.unicode-segmentation] version = "1.12.0" optional = true default-features = false [dev-dependencies.base64] version = "0.22" features = ["std"] default-features = false [dev-dependencies.bytes] version = "1.3" features = ["serde"] default-features = false [dev-dependencies.option_set] version = "0.3" default-features = false [dev-dependencies.serde] version = "1.0.181" features = [ "std", "derive", ] default-features = false [dev-dependencies.serde_bytes] version = "0.11" features = ["std"] default-features = false [dev-dependencies.serde_json] version = "1.0.60" features = ["std"] default-features = false [dev-dependencies.typetag] version = "0.2" default-features = false [features] default = ["std"] indexmap = [ "std", "dep:indexmap", ] integer128 = [] internal-span-substring-test = ["unicode-segmentation"] std = ["serde/std"] ron-0.12.0/Cargo.toml.orig000064400000000000000000000052651046102023000134010ustar 00000000000000[package] name = "ron" # Memo: update version in src/lib.rs too (doc link) version = "0.12.0" license = "MIT OR Apache-2.0" keywords = ["parser", "serde", "serialization"] authors = [ "Christopher Durham ", "Dzmitry Malyshau ", "Thomas Schaller ", "Juniper Tyree ", ] edition = "2021" description = "Rusty Object Notation" categories = ["encoding"] readme = "README.md" homepage = "https://github.com/ron-rs/ron" repository = "https://github.com/ron-rs/ron" documentation = "https://docs.rs/ron/" rust-version = "1.64.0" [features] default = ["std"] integer128 = [] std = ["serde/std"] indexmap = ["std", "dep:indexmap"] # Note: this feature is a gate for internal error span tests and should not be enabled by users. internal-span-substring-test = ["unicode-segmentation"] [dependencies] bitflags = { version = "2.1", default-features = false, features = ["serde"] } indexmap = { version = "2.0", default-features = false, features = ["serde"], optional = true } once_cell = { version = "1.20", default-features = false, features = ["alloc", "race"] } # serde supports i128/u128 from 1.0.60 onwards # serde's IntoDeserializer impls suport new constructor from 1.0.139 onwards # serde's adjacently tagged enums support integer tags from 1.0.181 onwards serde = { version = "1.0.181", default-features = false, features = ["alloc"] } serde_derive = { version = "1.0.181", default-features = false } typeid = { version = "1.0.1", default-features = false } unicode-ident = { version = "1.0", default-features = false } unicode-segmentation = { version = "1.12.0", default-features = false, optional = true } [dev-dependencies] base64 = { version = "0.22", default-features = false, features = ["std"] } bytes = { version = "1.3", default-features = false, features = ["serde"] } option_set = { version = "0.3", default-features = false } serde = { version = "1.0.181", default-features = false, features = ["std", "derive"] } serde_bytes = { version = "0.11", default-features = false, features = ["std"] } # serde_json supports the std feature from 1.0.60 onwards serde_json = { version = "1.0.60", default-features = false, features = ["std"] } typetag = { version = "0.2", default-features = false } [package.metadata.docs.rs] features = ["integer128", "indexmap"] rustdoc-args = ["--generate-link-to-definition"] [[example]] name = "base64" required-features = [] [[example]] name = "decode_file" required-features = ["std"] [[example]] name = "decode" required-features = [] [[example]] name = "encode_file" required-features = ["std"] [[example]] name = "encode" required-features = [] [[example]] name = "transcode" required-features = [] ron-0.12.0/LICENSE-APACHE000064400000000000000000000251371046102023000124360ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ron-0.12.0/LICENSE-MIT000064400000000000000000000020421046102023000121340ustar 00000000000000Copyright (c) 2017 RON developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ron-0.12.0/README.md000064400000000000000000000254601046102023000117700ustar 00000000000000# Rusty Object Notation [![MSRV](https://img.shields.io/badge/MSRV-1.64.0-orange)](https://github.com/ron-rs/ron) [![Crates.io](https://img.shields.io/crates/v/ron.svg)](https://crates.io/crates/ron) [![Docs](https://docs.rs/ron/badge.svg)](https://docs.rs/ron) [![CI](https://github.com/ron-rs/ron/actions/workflows/ci.yaml/badge.svg)](https://github.com/ron-rs/ron/actions/workflows/ci.yaml) [![Coverage](https://img.shields.io/endpoint?url=https%3A%2F%2Fron-rs.github.io%2Fron%2Fcoverage%2Fcoverage.json)](https://ron-rs.github.io/ron/coverage/) [![Fuzzing](https://oss-fuzz-build-logs.storage.googleapis.com/badges/ron.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:ron) [![Matrix](https://img.shields.io/matrix/ron-rs:matrix.org.svg)](https://matrix.to/#/#ron-rs:matrix.org) RON is a simple readable data serialization format that looks similar to Rust syntax. It's designed to support all of [Serde's data model](https://serde.rs/data-model.html), so structs, enums, tuples, arrays, generic maps, and primitive values. ## Example ```ron GameConfig( // optional struct name window_size: (800, 600), window_title: "PAC-MAN", fullscreen: false, mouse_sensitivity: 1.4, key_bindings: { "up": Up, "down": Down, "left": Left, "right": Right, // Uncomment to enable WASD controls /* "W": Up, "S": Down, "A": Left, "D": Right, */ }, difficulty_options: ( start_difficulty: Easy, adaptive: false, ), ) ``` ## RON syntax overview * Numbers: `42`, `3.14`, `0xFF`, `0b0110` * Strings: `"Hello"`, `"with\\escapes\n"`, `r#"raw string, great for regex\."#` * Byte Strings: `b"Hello"`, `b"with \x65\x73\x63\x61\x70\x65\x73\n"`, `br#"raw, too"#` * Booleans: `true`, `false` * Chars: `'e'`, `'\n'` * Optionals: `Some("string")`, `Some(Some(1.34))`, `None` * Tuples: `("abc", 1.23, true)`, `()` * Lists: `["abc", "def"]` * Structs: `( foo: 1.0, bar: ( baz: "I'm nested" ) )` * Maps: `{ "arbitrary": "keys", "are": "allowed" }` > **Note:** Serde's data model represents fixed-size Rust arrays as tuple (instead of as list) RON also supports several extensions, which are documented [here](docs/extensions.md). ## Specification RON's formal and complete grammar is available [here](docs/grammar.md). There also is a very basic, work in progress specification available on [the wiki page](https://github.com/ron-rs/ron/wiki/Specification). ## Why RON? ### Example in JSON ```json { "materials": { "metal": { "reflectivity": 1.0 }, "plastic": { "reflectivity": 0.5 } }, "entities": [ { "name": "hero", "material": "metal" }, { "name": "monster", "material": "plastic" } ] } ``` ### Same example in RON ```ron Scene( // class name is optional materials: { // this is a map "metal": ( reflectivity: 1.0, ), "plastic": ( reflectivity: 0.5, ), }, entities: [ // this is an array ( name: "hero", material: "metal", ), ( name: "monster", material: "plastic", ), ], ) ``` Note the following advantages of RON over JSON: * trailing commas allowed * single- and multi-line comments * field names aren't quoted, so it's less verbose * optional struct names improve readability * enums are supported (and less verbose than their JSON representation) ## Quickstart ### `Cargo.toml` ```toml [dependencies] ron = "0.8" serde = { version = "1", features = ["derive"] } ``` ### `main.rs` ```rust use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, Serialize)] struct MyStruct { boolean: bool, float: f32, } fn main() { let x: MyStruct = ron::from_str("(boolean: true, float: 1.23)").unwrap(); println!("RON: {}", ron::to_string(&x).unwrap()); println!("Pretty RON: {}", ron::ser::to_string_pretty( &x, ron::ser::PrettyConfig::default()).unwrap(), ); } ``` ## Tooling | Editor | Plugin | | -------------- | ------------------------------------------------------------------------ | | IntelliJ | [intellij-ron](https://github.com/ron-rs/intellij-ron) | | VS Code | [a5huynh/vscode-ron](https://github.com/a5huynh/vscode-ron) | | Sublime Text | [RON](https://packagecontrol.io/packages/RON) | | Atom | [language-ron](https://atom.io/packages/language-ron) | | Vim | [ron-rs/ron.vim](https://github.com/ron-rs/ron.vim) | | EMACS | [emacs-ron](https://chiselapp.com/user/Hutzdog/repository/ron-mode/home) | | Multiple / LSP | [ron-lsp](https://github.com/jasonjmcghee/ron-lsp) | ## Limitations RON requires struct, enum, and variant names to be valid Rust identifiers and will reject invalid ones created by `#[serde(rename = "...")]` at serialization / deserialization time. RON is not designed to be a fully self-describing format (unlike JSON) and is thus not guaranteed to work when [`deserialize_any`](https://docs.rs/serde/latest/serde/trait.Deserializer.html#tymethod.deserialize_any) is used instead of its typed alternatives. In particular, the following Serde attributes only have limited support: - `#[serde(tag = "tag")]`, i.e. internally tagged enums [^serde-enum-hack] - `#[serde(tag = "tag", content = "content")]`, i.e. adjacently tagged enums [^serde-enum-hack] - `#[serde(untagged)]`, i.e. untagged enums [^serde-enum-hack] - `#[serde(flatten)]`, i.e. flattening of structs into maps [^serde-flatten-hack] While data structures with any of these attributes should generally roundtrip through RON, some restrictions apply [^serde-restrictions] and their textual representation may not always match your expectation: - ron only supports string keys inside maps flattened into structs - internally (or adjacently) tagged or untagged enum variants or `#[serde(flatten)]`ed fields must not contain: - struct names, e.g. by enabling the `#[enable(explicit_struct_names)]` extension or the `PrettyConfig::struct_names` setting - newtypes - zero-length arrays / tuples / tuple structs / structs / tuple variants / struct variants - `Option`s with `#[enable(implicit_some)]` must not contain any of these or a unit, unit struct, or an untagged unit variant - externally tagged tuple variants with just one field (that are not newtype variants) - tuples or arrays or tuple structs with just one element are not supported inside newtype variants with `#[enable(unwrap_variant_newtypes)]` (including `Some`) - a `ron::value::RawValue` - untagged tuple / struct variants with no fields are not supported - untagged tuple variants with just one field (that are not newtype variants) are not supported when the `#![enable(unwrap_variant_newtypes)]` extension is enabled - serializing a `ron::value::RawValue` using a `PrettyConfig` may add leading and trailing whitespace and comments, which the `ron::value::RawValue` absorbs upon deserialization Furthermore, serde imposes the following restrictions for data to roundtrip: - structs or struct variants that contain a `#[serde(flatten)]`ed field: - are only serialised as maps and deserialised from maps - must not contain duplicate fields / keys, e.g. where an inner-struct field matches an outer-struct or inner-struct field - must not contain more than one (within the super-struct of all flattened structs) `#[serde(flatten)]`ed map field, which collects all unknown fields - if they contain a `#[serde(flatten)]`ed map, they must not contain: - a struct that is not flattened itself but contains some flattened fields and is flattened into the outer struct (variant) - an untagged struct variant that contains some flattened fields - a flattened externally tagged newtype, tuple, or struct variant, flattened internally tagged unit, newtype, or struct variant, or any flattened adjacently tagged variant - a flattened tagged struct - internally (or adjacently) tagged or untagged enum variants or `#[serde(flatten)]`ed fields must not contain: - `i128` or `u128` values - internally tagged newtype variants and `#[serde(flatten)]`ed fields must not contain: - a unit or a unit struct inside an untagged newtype variant - an untagged unit variant - internally tagged newtype variants, which are `#[serde(flatten)]`ed together with other fields, must not contain: - a unit or unit struct or an untagged unit variant While RON offers a best-effort implementation for `#[serde(flatten)]`, it may be unsupported in further cases and combinations not listed above. These limitations stem primarily from serde rather than RON. Enumerating all such cases based on serde's behavior is nontrivial, so the lists above are not exhaustive. Please file a [new issue](https://github.com/ron-rs/ron/issues/new) if you come across a use case which is not listed among the above restrictions but still breaks. While RON guarantees roundtrips like Rust -> RON -> Rust for Rust types using non-`deserialize_any`-based implementations, RON does not yet make any guarantees about roundtrips through `ron::Value`. For instance, even when RON -> Rust works, RON -> `ron::Value` -> Rust, or RON -> `ron::Value` -> RON -> Rust may not work. We plan on improving `ron::Value` in an upcoming version of RON, though this work is partially blocked on [serde#1183](https://github.com/serde-rs/serde/issues/1183). [^serde-enum-hack]: Deserialising an internally, adjacently, or un-tagged enum requires detecting `serde`'s internal default buffer/content type so that RON can describe the deserialised data structure in serde's internal JSON-like format. A more robust implementation is blocked on [serde#1183](https://github.com/serde-rs/serde/issues/1183). [^serde-flatten-hack]: Deserialising a flattened struct from a map requires that the struct's [`Visitor::expecting`](https://docs.rs/serde/latest/serde/de/trait.Visitor.html#tymethod.expecting) implementation formats a string starting with `"struct "`. This is the case for automatically-derived [`Deserialize`](https://docs.rs/serde/latest/serde/de/trait.Deserialize.html) impls on structs. See [#455](https://github.com/ron-rs/ron/pull/455) for more details. [^serde-restrictions]: Most of these restrictions are currently blocked on [serde#1183](https://github.com/serde-rs/serde/issues/1183), which limits non-self-describing formats from roundtripping format-specific information through internally (or adjacently) tagged or untagged enums or `#[serde(flatten)]`ed fields. ## License RON is dual-licensed under Apache-2.0 and MIT. Any contribution intentionally submitted for inclusion in the work must be provided under the same dual-license terms. ron-0.12.0/clippy.toml000064400000000000000000000000201046102023000126670ustar 00000000000000msrv = "1.64.0" ron-0.12.0/docs/extensions.md000064400000000000000000000076701046102023000141650ustar 00000000000000## RON extensions RON has extensions that can be enabled by adding the following attribute at the top of your RON document: `#![enable(...)]` # unwrap_newtypes You can add this extension by adding the following attribute at the top of your RON document: `#![enable(unwrap_newtypes)]` This feature enables RON to automatically unwrap simple tuples. ```rust struct NewType(u32); struct Object { pub new_type: NewType, } ``` Without `unwrap_newtypes`, because the value `5` can not be saved into `NewType(u32)`, your RON document would look like this: ```ron ( new_type: (5), ) ``` With the `unwrap_newtypes` extension, this coercion is done automatically. So `5` will be interpreted as `(5)`. ```ron #![enable(unwrap_newtypes)] ( new_type: 5, ) ``` # implicit_some You can add this extension by adding the following attribute at the top of your RON document: `#![enable(implicit_some)]` This feature enables RON to automatically convert any value to `Some(value)` if the deserialized type requires it. ```rust struct Object { pub value: Option, } ``` Without this feature, you would have to write this RON document. ```ron ( value: Some(5), ) ``` Enabling the feature would automatically infer `Some(x)` if `x` is given. In this case, RON automatically casts this `5` into a `Some(5)`. ```ron ( value: 5, ) ``` With this extension enabled, explicitly given `None` and `Some(..)` will be matched eagerly on `Option>>`, i.e. * `5` -> `Some(Some(Some(5)))` * `None` -> `None` * `Some(5)` -> `Some(Some(Some(5)))` * `Some(None)` -> `Some(None)` * `Some(Some(5))` -> `Some(Some(Some(5)))` * `Some(Some(None))` -> `Some(Some(None))` * `Some(Some(Some(5)))` -> `Some(Some(Some(5)))` # unwrap_variant_newtypes You can add this extension by adding the following attribute at the top of your RON document: `#![enable(unwrap_variant_newtypes)]` This feature enables RON to automatically unwrap newtype enum variants. ```rust #[derive(Deserialize)] struct Inner { pub a: u8, pub b: bool, } #[derive(Deserialize)] pub enum Enum { A(Inner), B, } ``` Without `unwrap_variant_newtypes`, your RON document would look like this: ```ron ( variant: A(Inner(a: 4, b: true)), ) ``` With the `unwrap_variant_newtypes` extension, the first structural layer inside a newtype variant will be unwrapped automatically: ```ron #![enable(unwrap_newtypes)] ( variant: A(a: 4, b: true), ) ``` Note that when the `unwrap_variant_newtypes` extension is enabled, the first layer inside a newtype variant will **always** be unwrapped, i.e. it is no longer possible to write `A(Inner(a: 4, b: true))` or `A((a: 4, b: true))`. # explicit_struct_names During serialization, this extension emits struct names. For instance, this would be emitted: ```ron Foo( bar: Bar(42), ) ``` During deserialization, this extension requires that all structs have names attached to them. For example, the following deserializes perfectly fine: ```ron Foo( bar: Bar(42), ) ``` However, with the `explicit_struct_names` extension enabled, the following will throw an `ExpectedStructName` error: ```ron ( bar: Bar(42), ) ``` Similarly, the following will throw the same error: ```ron Foo( bar: (42), ) ``` Note that if what you are parsing is spread across many files, you would likely use `Options::with_default_extension` to enable `Extensions::EXPLICIT_STRUCT_NAMES` before the parsing stage. This is because prepending `#![enable(explicit_struct_names)]` to the contents of every file you parse would violate DRY (Don't Repeat Yourself). Here is an example of how to enable `explicit_struct_names` using this method: ```rust use ron::extensions::Extensions; use ron::options::Options; // Setup the options let options = Options::default().with_default_extension(Extensions::EXPLICIT_STRUCT_NAMES); // Retrieve the contents of the file let file_contents: &str = /* ... */; // Parse the file's contents let foo: Foo = options.from_str(file_contents)?; ``` ron-0.12.0/docs/grammar.md000064400000000000000000000135771046102023000134170ustar 00000000000000# RON grammar This file describes the structure of a RON file in [EBNF notation][ebnf]. If extensions are enabled, some rules will be replaced. For that, see the [extensions document][exts] which describes all extensions and what they override. [ebnf]: https://en.wikipedia.org/wiki/Extended_Backus–Naur_form [exts]: ./extensions.md ## RON file ```ebnf RON = [extensions], ws, value, ws; ``` ## Whitespace and comments ```ebnf ws = { ws_single | comment }; ws_single = "\n" | "\t" | "\r" | " " | U+000B | U+000C | U+0085 | U+200E | U+200F | U+2028 | U+2029; comment = ["//", { no_newline }, "\n"] | ["/*", nested_block_comment, "*/"]; nested_block_comment = { ? any characters except "/*" or "*/" ? }, [ "/*", nested_block_comment, "*/", nested_block_comment ]; ``` ## Commas ```ebnf comma = ws, ",", ws; ``` ## Extensions ```ebnf extensions = { "#", ws, "!", ws, "[", ws, extensions_inner, ws, "]", ws }; extensions_inner = "enable", ws, "(", extension_name, { comma, extension_name }, [comma], ws, ")"; ``` For the extension names see the [`extensions.md`][exts] document. ## Value ```ebnf value = integer | byte | float | string | byte_string | char | bool | option | list | map | tuple | struct | enum_variant; ``` ## Numbers ```ebnf digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"; digit_binary = "0" | "1"; digit_octal = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7"; digit_hexadecimal = digit | "A" | "a" | "B" | "b" | "C" | "c" | "D" | "d" | "E" | "e" | "F" | "f"; integer = ["+" | "-"], unsigned, [integer_suffix]; integer_suffix = ("i", "u"), ("8", "16", "32", "64", "128"); unsigned = unsigned_binary | unsigned_octal | unsigned_hexadecimal | unsigned_decimal; unsigned_binary = "0b", digit_binary, { digit_binary | "_" }; unsigned_octal = "0o", digit_octal, { digit_octal | "_" }; unsigned_hexadecimal = "0x", digit_hexadecimal, { digit_hexadecimal | "_" }; unsigned_decimal = digit, { digit | "_" }; byte = "b", "'", byte_content, "'"; byte_content = ascii | ("\\", (escape_ascii | escape_byte)); float = ["+" | "-"], ("inf" | "NaN" | float_num), [float_suffix]; float_num = (float_int | float_std | float_frac), [float_exp]; float_int = digit, { digit | "_" }; float_std = digit, { digit | "_" }, ".", [digit, { digit | "_" }]; float_frac = ".", digit, { digit | "_" }; float_exp = ("e" | "E"), ["+" | "-"], { digit | "_" }, digit, { digit | "_" }; float_suffix = "f", ("32", "64"); ``` > Note: `ascii` refers to any ASCII character, i.e. any byte in range `0x00 ..= 0x7F`. ## String ```ebnf string = string_std | string_raw; string_std = "\"", { no_double_quotation_marks | string_escape }, "\""; string_escape = "\\", (escape_ascii | escape_byte | escape_unicode); string_raw = "r", string_raw_content; string_raw_content = ("#", string_raw_content, "#") | "\"", { unicode_non_greedy }, "\""; escape_ascii = "'" | "\"" | "\\" | "n" | "r" | "t" | "0"; escape_byte = "x", digit_hexadecimal, digit_hexadecimal; escape_unicode = "u", digit_hexadecimal, [digit_hexadecimal, [digit_hexadecimal, [digit_hexadecimal, [digit_hexadecimal, [digit_hexadecimal]]]]]; ``` > Note: Raw strings start with an `r`, followed by n `#`s and a quotation mark `"`. They may contain any characters or escapes (except the end sequence). A raw string ends with a quotation mark (`"`), followed by n `#`s. n may be any number, including zero. Example: ```rust r##"This is a "raw string". It can contain quotations or backslashes (\)!"## ``` Raw strings cannot be written in EBNF, as they are context-sensitive. Also see [the Rust document] about context-sensitivity of raw strings. [the Rust document]: https://github.com/rust-lang/rust/blob/d046ffddc4bd50e04ffc3ff9f766e2ac71f74d50/src/grammar/raw-string-literal-ambiguity.md ## Byte String ```ebnf byte_string = byte_string_std | byte_string_raw; byte_string_std = "b\"", { no_double_quotation_marks | string_escape }, "\""; byte_string_raw = "br", string_raw_content; ``` > Note: Byte strings are similar to normal strings but are not required to contain only valid UTF-8 text. RON's byte strings follow the updated Rust byte string literal rules as proposed in [RFC #3349], i.e. byte strings allow the exact same characters and escape codes as normal strings. [RFC #3349](https://github.com/rust-lang/rfcs/pull/3349) > Note: Raw byte strings start with an `br` prefix and follow the same rules as raw strings, which are outlined above. ## Char ```ebnf char = "'", (no_apostrophe | "\\\\" | "\\'"), "'"; ``` ## Boolean ```ebnf bool = "true" | "false"; ``` ## Optional ```ebnf option = "None" | option_some; option_some = "Some", ws, "(", ws, value, ws, ")"; ``` ## List ```ebnf list = "[", [value, { comma, value }, [comma]], "]"; ``` ## Map ```ebnf map = "{", [map_entry, { comma, map_entry }, [comma]], "}"; map_entry = value, ws, ":", ws, value; ``` ## Tuple ```ebnf tuple = "(", [value, { comma, value }, [comma]], ")"; ``` ## Struct ```ebnf struct = unit_struct | tuple_struct | named_struct; unit_struct = ident | "()"; tuple_struct = [ident], ws, tuple; named_struct = [ident], ws, "(", ws, [named_field, { comma, named_field }, [comma]], ")"; named_field = ident, ws, ":", ws, value; ``` ## Enum ```ebnf enum_variant = enum_variant_unit | enum_variant_tuple | enum_variant_named; enum_variant_unit = ident; enum_variant_tuple = ident, ws, tuple; enum_variant_named = ident, ws, "(", [named_field, { comma, named_field }, [comma]], ")"; ``` ## Identifier ```ebnf ident = ident_std | ident_raw; ident_std = ident_std_first, { ident_std_rest }; ident_std_first = XID_Start | "_"; ident_std_rest = XID_Continue; ident_raw = "r", "#", ident_raw_rest, { ident_raw_rest }; ident_raw_rest = ident_std_rest | "." | "+" | "-"; ``` > Note: [XID_Start](http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Start%3A%5D&abb=on&g=&i=) and [XID_Continue](http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Continue%3A%5D&abb=on&g=&i=) refer to Unicode character sets. ron-0.12.0/examples/base64.rs000064400000000000000000000115631046102023000137600ustar 00000000000000//! ron initially encoded byte-slices and byte-bufs as base64-encoded strings. //! However, since v0.9, ron now uses Rusty byte string literals instead. //! //! This example shows how the previous behaviour can be restored by serialising //! bytes with strongly-typed base64-encoded strings, or accepting both Rusty //! byte strings and the legacy base64-encoded string syntax. use base64::engine::{general_purpose::STANDARD as BASE64, Engine}; use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; #[derive(Debug, PartialEq, Serialize, Deserialize)] struct Config { #[serde(with = "ByteStr")] bytes: Vec, #[serde(with = "Base64")] base64: Vec, #[serde(with = "ByteStrOrBase64")] bytes_or_base64: Vec, } enum ByteStr {} impl ByteStr { fn serialize(data: &[u8], serializer: S) -> Result { serializer.serialize_bytes(data) } fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result, D::Error> { struct ByteStrVisitor; impl<'de> Visitor<'de> for ByteStrVisitor { type Value = Vec; fn expecting(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { fmt.write_str("a Rusty byte string") } fn visit_bytes(self, bytes: &[u8]) -> Result { Ok(bytes.to_vec()) } fn visit_byte_buf(self, bytes: Vec) -> Result { Ok(bytes) } } deserializer.deserialize_byte_buf(ByteStrVisitor) } } enum Base64 {} impl Base64 { fn serialize(data: &[u8], serializer: S) -> Result { serializer.serialize_str(&BASE64.encode(data)) } fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result, D::Error> { let base64_str = <&str>::deserialize(deserializer)?; BASE64.decode(base64_str).map_err(serde::de::Error::custom) } } enum ByteStrOrBase64 {} impl ByteStrOrBase64 { fn serialize(data: &[u8], serializer: S) -> Result { if cfg!(all()) { // either of these would work serializer.serialize_str(&BASE64.encode(data)) } else { serializer.serialize_bytes(data) } } fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result, D::Error> { struct ByteStrOrBase64Visitor; impl<'de> Visitor<'de> for ByteStrOrBase64Visitor { type Value = Vec; fn expecting(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { fmt.write_str("a Rusty byte string or a base64-encoded string") } fn visit_str(self, base64_str: &str) -> Result { BASE64.decode(base64_str).map_err(serde::de::Error::custom) } fn visit_bytes(self, bytes: &[u8]) -> Result { Ok(bytes.to_vec()) } fn visit_byte_buf(self, bytes: Vec) -> Result { Ok(bytes) } } deserializer.deserialize_any(ByteStrOrBase64Visitor) } } fn main() { let ron = r#"Config( bytes: b"only byte strings are allowed", base64: "b25seSBiYXNlNjQtZW5jb2RlZCBzdHJpbmdzIGFyZSBhbGxvd2Vk", bytes_or_base64: b"both byte strings and base64-encoded strings work", )"#; assert_eq!( ron::from_str::(ron).unwrap(), Config { bytes: b"only byte strings are allowed".to_vec(), base64: b"only base64-encoded strings are allowed".to_vec(), bytes_or_base64: b"both byte strings and base64-encoded strings work".to_vec() } ); let ron = r#"Config( bytes: b"only byte strings are allowed", base64: "b25seSBiYXNlNjQtZW5jb2RlZCBzdHJpbmdzIGFyZSBhbGxvd2Vk", bytes_or_base64: "Ym90aCBieXRlIHN0cmluZ3MgYW5kIGJhc2U2NC1lbmNvZGVkIHN0cmluZ3Mgd29yaw==", )"#; assert_eq!( ron::from_str::(ron).unwrap(), Config { bytes: b"only byte strings are allowed".to_vec(), base64: b"only base64-encoded strings are allowed".to_vec(), bytes_or_base64: b"both byte strings and base64-encoded strings work".to_vec() } ); println!( "{}", ron::ser::to_string_pretty( &Config { bytes: b"only byte strings are allowed".to_vec(), base64: b"only base64-encoded strings are allowed".to_vec(), bytes_or_base64: b"both byte strings and base64-encoded strings work".to_vec() }, ron::ser::PrettyConfig::default().struct_names(true) ) .unwrap() ); } ron-0.12.0/examples/decode.rs000064400000000000000000000025021046102023000141100ustar 00000000000000#![allow(dead_code)] use std::collections::HashMap; use ron::de::from_str; use serde::Deserialize; #[derive(Debug, Deserialize)] struct Config { boolean: bool, float: f32, map: HashMap, nested: Nested, option: Option, tuple: (u32, u32), } #[derive(Debug, Deserialize)] struct Nested { a: String, b: char, } const CONFIG: &str = " /* * RON now has multi-line (C-style) block comments! * They can be freely nested: * /* This is a nested comment */ * If you just want a single-line comment, * do it like here: // Just put two slashes before the comment and the rest of the line // can be used freely! */ // Note that block comments can not be started in a line comment // (Putting a /* here will have no effect) ( boolean: true, float: 8.2, map: { 1: '1', 2: '4', 3: '9', 4: '1', 5: '2', 6: '3', }, nested: Nested( a: \"Decode me!\", b: 'z', ), option: Some(\t \"Weird formatting!\" \n\n ), tuple: (3 /*(2 + 1)*/, 7 /*(2 * 5 - 3)*/), )"; fn main() { let config: Config = match from_str(CONFIG) { Ok(x) => x, Err(e) => { println!("Failed to load config: {}", e); std::process::exit(1); } }; println!("Config: {:?}", &config); } ron-0.12.0/examples/decode_file.rs000064400000000000000000000014031046102023000151060ustar 00000000000000#![allow(dead_code)] use std::{collections::HashMap, fs::File}; use ron::de::from_reader; use serde::Deserialize; #[derive(Debug, Deserialize)] struct Config { boolean: bool, float: f32, map: HashMap, nested: Nested, tuple: (u32, u32), vec: Vec, } #[derive(Debug, Deserialize)] struct Nested { a: String, b: char, } fn main() { let input_path = format!("{}/examples/example.ron", env!("CARGO_MANIFEST_DIR")); let f = File::open(input_path).expect("Failed opening file"); let config: Config = match from_reader(f) { Ok(x) => x, Err(e) => { println!("Failed to load config: {}", e); std::process::exit(1); } }; println!("Config: {:?}", &config); } ron-0.12.0/examples/encode.rs000064400000000000000000000020611046102023000141220ustar 00000000000000use std::{collections::HashMap, iter::FromIterator}; use ron::ser::{to_string_pretty, PrettyConfig}; use serde::Serialize; #[derive(Serialize)] struct Config { float: (f32, f64), tuple: TupleStruct, map: HashMap, nested: Nested, var: Variant, array: Vec<()>, } #[derive(Serialize)] struct TupleStruct((), bool); #[derive(Serialize)] enum Variant { A(u8, &'static str), } #[derive(Serialize)] struct Nested { a: String, b: char, } fn main() { let data = Config { float: (2.18, -1.1), tuple: TupleStruct((), false), map: HashMap::from_iter(vec![(0, '1'), (1, '2'), (3, '5'), (8, '1')]), nested: Nested { a: "Hello from \"RON\"".to_string(), b: 'b', }, var: Variant::A(!0, ""), array: vec![(); 3], }; let pretty = PrettyConfig::new() .depth_limit(2) .separate_tuple_members(true) .enumerate_arrays(true); let s = to_string_pretty(&data, pretty).expect("Serialization failed"); println!("{}", s); } ron-0.12.0/examples/encode_file.rs000064400000000000000000000024351046102023000151260ustar 00000000000000#![allow(dead_code)] use std::{collections::HashMap, fs::File}; use serde::Serialize; #[derive(Debug, Serialize)] struct Config { boolean: bool, float: f32, map: HashMap, nested: Nested, tuple: (u32, u32), vec: Vec, } #[derive(Debug, Serialize)] struct Nested { a: String, b: char, } fn main() { let config = Config { boolean: true, float: 8.2, map: [(1, '1'), (2, '4'), (3, '9'), (4, '1'), (5, '2'), (6, '3')] .into_iter() .collect(), nested: Nested { a: String::from("Decode me!"), b: 'z', }, tuple: (3, 7), vec: vec![ Nested { a: String::from("Nested 1"), b: 'x', }, Nested { a: String::from("Nested 2"), b: 'y', }, Nested { a: String::from("Nested 3"), b: 'z', }, ], }; let f = File::options() .create(true) .write(true) .open("example-out.ron") .expect("Failed opening file"); ron::Options::default() .to_io_writer_pretty(f, &config, ron::ser::PrettyConfig::new()) .expect("Failed to write to file"); } ron-0.12.0/examples/example.ron000064400000000000000000000005441046102023000144760ustar 00000000000000( boolean: true, float: 8.2, map: { 1: '1', 2: '4', 3: '9', 4: '1', 5: '2', 6: '3', }, nested: Nested( a: "Decode me!", b: 'z', ), tuple: (3, 7), vec: [ (a: "Nested 1", b: 'x'), (a: "Nested 2", b: 'y'), (a: "Nested 3", b: 'z'), ], )ron-0.12.0/examples/file_read_write_vec.rs000064400000000000000000000071001046102023000166450ustar 00000000000000/// Getting RON with type derives and reading/writing a Vec to/from a file. use ron::{error::SpannedResult, ser::PrettyConfig, Error}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::{ fs::File, io::{Read, Write}, path::PathBuf, str::FromStr, }; #[derive(Debug, Deserialize, Serialize)] struct MetaData { created_at: String, author: String, } #[derive(Debug, Deserialize, Serialize)] enum UserRole { User, Admin { key: usize }, } #[derive(Debug, Deserialize, Serialize)] struct User { name: String, email: String, comment: String, role: UserRole, meta: MetaData, } fn create_records() -> Vec { vec![ User { name: "Alice".into(), email: "alice@example.com".into(), comment: "New\nLine, and \"quotes\"".into(), role: UserRole::Admin { key: 0xDEADFEED }, meta: MetaData { created_at: "2025-06-22".into(), author: "Admin".to_string(), }, }, User { name: "Bob".into(), email: "bob@example.com".into(), comment: "Tabs\ttoo".into(), role: UserRole::User, meta: MetaData { created_at: "2025-06-22".into(), author: "Admin".to_string(), }, }, ] } /// Serializes a list of T into a string with one record per line fn write_ron_vec_to_str(records: &[T]) -> Result { let mut mut_str = String::new(); let as_strings = { records .into_iter() .map(|record| { ron::ser::to_string_pretty( &record, PrettyConfig::new() .compact_arrays(true) .compact_maps(true) .compact_structs(true) .escape_strings(true), ) }) .collect::, _>>()? }; as_strings.into_iter().for_each(|s| { mut_str.push_str(&s); mut_str.push_str(if cfg!(not(target_os = "windows")) { "\n" } else { "\r\n" }) }); Ok(mut_str) } /// Serializes a list of T into a text file with one record per line fn write_ron_vec_to_file(path: &PathBuf, records: &[T]) -> Result { let mut file = File::create(path).map_err(|e| Error::Io(e.to_string()))?; file.write(write_ron_vec_to_str(records)?.as_bytes()) .map_err(|err| Error::Io(err.to_string())) } /// This reader assumes that every row has one entry, so it would not work if they are split across lines. fn read_ron_vec_from_str(s: &str) -> SpannedResult> { s //_ .lines() .map(|s| ron::from_str::(s)) .collect::, _>>() } fn read_ron_vec_from_file(path: &PathBuf) -> Result, Error> { let mut file = File::open(path).map_err(|e| Error::Io(e.to_string()))?; let mut content = String::new(); file.read_to_string(&mut content) .map_err(|e| Error::Io(e.to_string()))?; read_ron_vec_from_str(&content).map_err(|e| e.code) } pub fn main() { let users = create_records(); let path = PathBuf::from_str("vec-example.ron").unwrap(); write_ron_vec_to_file(&path, &users).unwrap(); let read_users: Vec = read_ron_vec_from_file(&path).unwrap(); // Comment this out if you want to view the file: std::fs::remove_file("vec-example.ron").unwrap(); println!("{:?}", read_users); } ron-0.12.0/examples/transcode.rs000064400000000000000000000015271046102023000146550ustar 00000000000000use ron::value::Value; use serde::Serialize; fn main() { let data = r#" Scene( // class name is optional materials: { // this is a map "metal": ( reflectivity: 1.0, ), "plastic": ( reflectivity: 0.5, ), }, entities: [ // this is an array ( name: "hero", material: "metal", ), ( name: "monster", material: "plastic", ), ], ) "#; let value: Value = data.parse().expect("Failed to deserialize"); let mut ser = serde_json::Serializer::pretty(std::io::stdout()); value.serialize(&mut ser).expect("Failed to serialize"); } ron-0.12.0/rustfmt.toml000064400000000000000000000001341046102023000131010ustar 00000000000000hard_tabs = false use_field_init_shorthand = true use_try_shorthand = true edition = "2021" ron-0.12.0/src/de/id.rs000064400000000000000000000126201046102023000126240ustar 00000000000000use serde::de::{self, Visitor}; use super::{Error, Result}; pub struct Deserializer<'a, 'b: 'a> { de: &'a mut super::Deserializer<'b>, map_as_struct: bool, } impl<'a, 'b: 'a> Deserializer<'a, 'b> { pub fn new(de: &'a mut super::Deserializer<'b>, map_as_struct: bool) -> Self { Deserializer { de, map_as_struct } } } impl<'a, 'b: 'a, 'c> de::Deserializer<'b> for &'c mut Deserializer<'a, 'b> { type Error = Error; fn deserialize_identifier(self, visitor: V) -> Result where V: Visitor<'b>, { if self.map_as_struct { // We only allow string keys in flattened structs and maps self.de.deserialize_str(visitor) } else { self.de.deserialize_identifier(visitor) } } fn deserialize_str(self, visitor: V) -> Result where V: Visitor<'b>, { self.deserialize_identifier(visitor) } fn deserialize_string(self, visitor: V) -> Result where V: Visitor<'b>, { self.deserialize_identifier(visitor) } fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'b>, { self.deserialize_identifier(visitor) } fn deserialize_bool(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_i8(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_i16(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_i32(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_i64(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } #[cfg(feature = "integer128")] fn deserialize_i128(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_u8(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_u16(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_u32(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_u64(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } #[cfg(feature = "integer128")] fn deserialize_u128(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_f32(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_f64(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_char(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_bytes(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_byte_buf(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_option(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_unit(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_unit_struct(self, _: &'static str, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_newtype_struct(self, _: &'static str, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_seq(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_tuple(self, _: usize, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_tuple_struct(self, _: &'static str, _: usize, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_map(self, _: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_struct( self, _: &'static str, _: &'static [&'static str], _: V, ) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_enum( self, _: &'static str, _: &'static [&'static str], _: V, ) -> Result where V: Visitor<'b>, { Err(Error::ExpectedIdentifier) } fn deserialize_ignored_any(self, visitor: V) -> Result where V: Visitor<'b>, { self.deserialize_any(visitor) } } ron-0.12.0/src/de/mod.rs000064400000000000000000001050741046102023000130150ustar 00000000000000/// Deserialization module. use alloc::{ borrow::ToOwned, string::{String, ToString}, vec::Vec, }; use core::str; use serde::{ de::{self, DeserializeSeed, Deserializer as _, Visitor}, Deserialize, }; pub use crate::error::{Error, Position, Span, SpannedError}; use crate::{ error::{Result, SpannedResult}, extensions::Extensions, options::Options, parse::{NewtypeMode, ParsedByteStr, ParsedStr, Parser, StructType, TupleMode}, }; #[cfg(feature = "std")] use std::io; mod id; mod tag; #[cfg(test)] mod tests; mod value; /// The RON deserializer. /// /// If you just want to simply deserialize a value, /// you can use the [`from_str`] convenience function. pub struct Deserializer<'de> { pub(crate) parser: Parser<'de>, newtype_variant: bool, serde_content_newtype: bool, last_identifier: Option<&'de str>, recursion_limit: Option, } impl<'de> Deserializer<'de> { // Cannot implement trait here since output is tied to input lifetime 'de. #[allow(clippy::should_implement_trait)] pub fn from_str(input: &'de str) -> SpannedResult { Self::from_str_with_options(input, &Options::default()) } pub fn from_bytes(input: &'de [u8]) -> SpannedResult { Self::from_bytes_with_options(input, &Options::default()) } pub fn from_str_with_options(input: &'de str, options: &Options) -> SpannedResult { let mut deserializer = Deserializer { parser: Parser::new(input)?, newtype_variant: false, serde_content_newtype: false, last_identifier: None, recursion_limit: options.recursion_limit, }; deserializer.parser.exts |= options.default_extensions; Ok(deserializer) } // FIXME: panic is not actually possible, remove once utf8_chunks is stabilized #[allow(clippy::missing_panics_doc)] pub fn from_bytes_with_options(input: &'de [u8], options: &Options) -> SpannedResult { let err = match str::from_utf8(input) { Ok(input) => return Self::from_str_with_options(input, options), Err(err) => err, }; // FIXME: use [`utf8_chunks`](https://github.com/rust-lang/rust/issues/99543) once stabilised #[allow(clippy::expect_used)] let valid_input = str::from_utf8(&input[..err.valid_up_to()]).expect("source is valid up to error"); Err(SpannedError { code: err.into(), span: Span { start: Position { line: 1, col: 1 }, end: Position::from_src_end(valid_input), }, }) } #[must_use] pub fn remainder(&self) -> &'de str { self.parser.src() } #[must_use] pub fn span_error(&self, code: Error) -> SpannedError { self.parser.span_error(code) } #[must_use] pub fn extensions(&self) -> Extensions { self.parser.exts } } /// A convenience function for building a deserializer /// and deserializing a value of type `T` from a reader. #[cfg(feature = "std")] pub fn from_reader(rdr: R) -> SpannedResult where R: io::Read, T: de::DeserializeOwned, { Options::default().from_reader(rdr) } /// A convenience function for building a deserializer /// and deserializing a value of type `T` from a string. pub fn from_str<'a, T>(s: &'a str) -> SpannedResult where T: de::Deserialize<'a>, { Options::default().from_str(s) } /// A convenience function for building a deserializer /// and deserializing a value of type `T` from bytes. pub fn from_bytes<'a, T>(s: &'a [u8]) -> SpannedResult where T: de::Deserialize<'a>, { Options::default().from_bytes(s) } macro_rules! guard_recursion { ($self:expr => $expr:expr) => {{ if let Some(limit) = &mut $self.recursion_limit { if let Some(new_limit) = limit.checked_sub(1) { *limit = new_limit; } else { return Err(Error::ExceededRecursionLimit); } } let result = $expr; if let Some(limit) = &mut $self.recursion_limit { *limit = limit.saturating_add(1); } result }}; } impl<'de> Deserializer<'de> { /// Check if the remaining bytes are whitespace only, /// otherwise return an error. pub fn end(&mut self) -> Result<()> { self.parser.skip_ws()?; if self.parser.src().is_empty() { Ok(()) } else { Err(Error::TrailingCharacters) } } /// Called from [`deserialize_any`][serde::Deserializer::deserialize_any] /// when a struct was detected. Decides if there is a unit, tuple or usual /// struct and deserializes it accordingly. /// /// This method assumes there is no identifier left. fn handle_any_struct(&mut self, visitor: V, ident: Option<&str>) -> Result where V: Visitor<'de>, { // HACK: switch to JSON enum semantics for JSON content // Robust impl blocked on https://github.com/serde-rs/serde/issues/1183 let is_serde_content = is_serde_content::() || is_serde_tag_or_content::(); let old_serde_content_newtype = self.serde_content_newtype; self.serde_content_newtype = false; match ( self.parser.check_struct_type( NewtypeMode::NoParensMeanUnit, if old_serde_content_newtype { TupleMode::DifferentiateNewtype // separate match on NewtypeOrTuple below } else { TupleMode::ImpreciseTupleOrNewtype // Tuple and NewtypeOrTuple match equally }, )?, ident, ) { (StructType::Unit, Some(ident)) if is_serde_content => { // serde's Content type needs the ident for unit variants visitor.visit_str(ident) } (StructType::Unit, _) => visitor.visit_unit(), (_, Some(ident)) if is_serde_content => { // serde's Content type uses a singleton map encoding for enums visitor.visit_map(SerdeEnumContent { de: self, ident: Some(ident), }) } (StructType::Named, _) => { // giving no name results in worse errors but is necessary here self.handle_struct_after_name("", visitor) } (StructType::NewtypeTuple, _) if old_serde_content_newtype => { // deserialize a newtype struct or variant self.parser.consume_char('('); self.parser.skip_ws()?; let result = self.deserialize_any(visitor); self.parser.skip_ws()?; self.parser.consume_char(')'); result } ( StructType::AnyTuple | StructType::EmptyTuple | StructType::NewtypeTuple | StructType::NonNewtypeTuple, _, ) => { // first argument is technically incorrect, but ignored anyway self.deserialize_tuple(0, visitor) } } } /// Called from /// [`deserialize_struct`][serde::Deserializer::deserialize_struct], /// [`struct_variant`][serde::de::VariantAccess::struct_variant], and /// [`handle_any_struct`][Self::handle_any_struct]. Handles /// deserialising the enclosing parentheses and everything in between. /// /// This method assumes there is no struct name identifier left. fn handle_struct_after_name( &mut self, name_for_pretty_errors_only: &'static str, visitor: V, ) -> Result where V: Visitor<'de>, { if self.newtype_variant || self.parser.consume_char('(') { let old_newtype_variant = self.newtype_variant; self.newtype_variant = false; let value = guard_recursion! { self => visitor .visit_map(CommaSeparated::new(Terminator::Struct, self)) .map_err(|err| { struct_error_name( err, if !old_newtype_variant && !name_for_pretty_errors_only.is_empty() { Some(name_for_pretty_errors_only) } else { None }, ) })? }; self.parser.skip_ws()?; if old_newtype_variant || self.parser.consume_char(')') { Ok(value) } else { Err(Error::ExpectedStructLikeEnd) } } else if name_for_pretty_errors_only.is_empty() { Err(Error::ExpectedStructLike) } else { Err(Error::ExpectedNamedStructLike(name_for_pretty_errors_only)) } } } impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { type Error = Error; fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { if self.newtype_variant { if self.parser.check_char(')') { // newtype variant wraps the unit type / a unit struct without name return self.deserialize_unit(visitor); } #[allow(clippy::wildcard_in_or_patterns)] match self .parser .check_struct_type(NewtypeMode::InsideNewtype, TupleMode::DifferentiateNewtype)? { StructType::Named => { // newtype variant wraps a named struct // giving no name results in worse errors but is necessary here return self.handle_struct_after_name("", visitor); } StructType::EmptyTuple | StructType::NonNewtypeTuple => { // newtype variant wraps a tuple (struct) // first argument is technically incorrect, but ignored anyway return self.deserialize_tuple(0, visitor); } // StructType::Unit is impossible with NewtypeMode::InsideNewtype // StructType::AnyTuple is impossible with TupleMode::DifferentiateNewtype StructType::NewtypeTuple | _ => { // continue as usual with the inner content of the newtype variant self.newtype_variant = false; } } } if self.parser.consume_ident("true") { return visitor.visit_bool(true); } else if self.parser.consume_ident("false") { return visitor.visit_bool(false); } else if self.parser.check_ident("Some") { return self.deserialize_option(visitor); } else if self.parser.consume_ident("None") { return visitor.visit_none(); } else if self.parser.consume_str("()") { return visitor.visit_unit(); } else if self.parser.consume_ident("inf") || self.parser.consume_ident("inff32") { return visitor.visit_f32(core::f32::INFINITY); } else if self.parser.consume_ident("inff64") { return visitor.visit_f64(core::f64::INFINITY); } else if self.parser.consume_ident("NaN") || self.parser.consume_ident("NaNf32") { return visitor.visit_f32(core::f32::NAN); } else if self.parser.consume_ident("NaNf64") { return visitor.visit_f64(core::f64::NAN); } // `skip_identifier` does not change state if it fails if let Some(ident) = self.parser.skip_identifier() { self.parser.skip_ws()?; return self.handle_any_struct(visitor, Some(ident)); } match self.parser.peek_char_or_eof()? { '(' => self.handle_any_struct(visitor, None), '[' => self.deserialize_seq(visitor), '{' => self.deserialize_map(visitor), '0'..='9' | '+' | '-' | '.' => self.parser.any_number()?.visit(visitor), '"' | 'r' => self.deserialize_string(visitor), 'b' if self.parser.src().starts_with("b'") => self.parser.any_number()?.visit(visitor), 'b' => self.deserialize_byte_buf(visitor), '\'' => self.deserialize_char(visitor), other => Err(Error::UnexpectedChar(other)), } } fn deserialize_bool(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_bool(self.parser.bool()?) } fn deserialize_i8(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_i8(self.parser.integer()?) } fn deserialize_i16(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_i16(self.parser.integer()?) } fn deserialize_i32(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_i32(self.parser.integer()?) } fn deserialize_i64(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_i64(self.parser.integer()?) } #[cfg(feature = "integer128")] fn deserialize_i128(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_i128(self.parser.integer()?) } fn deserialize_u8(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_u8(self.parser.integer()?) } fn deserialize_u16(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_u16(self.parser.integer()?) } fn deserialize_u32(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_u32(self.parser.integer()?) } fn deserialize_u64(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_u64(self.parser.integer()?) } #[cfg(feature = "integer128")] fn deserialize_u128(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_u128(self.parser.integer()?) } fn deserialize_f32(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_f32(self.parser.float()?) } fn deserialize_f64(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_f64(self.parser.float()?) } fn deserialize_char(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_char(self.parser.char()?) } fn deserialize_str(self, visitor: V) -> Result where V: Visitor<'de>, { match self.parser.string()? { ParsedStr::Allocated(s) => visitor.visit_string(s), ParsedStr::Slice(s) => visitor.visit_borrowed_str(s), } } fn deserialize_string(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_str(visitor) } fn deserialize_bytes(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_byte_buf(visitor) } fn deserialize_byte_buf(self, visitor: V) -> Result where V: Visitor<'de>, { if self.parser.check_char('[') { let bytes = Vec::::deserialize(self)?; return visitor.visit_byte_buf(bytes); } match self.parser.byte_string()? { ParsedByteStr::Allocated(byte_buf) => visitor.visit_byte_buf(byte_buf), ParsedByteStr::Slice(bytes) => visitor.visit_borrowed_bytes(bytes), } } fn deserialize_option(self, visitor: V) -> Result where V: Visitor<'de>, { if self.parser.consume_ident("None") { visitor.visit_none() } else if self.parser.consume_ident("Some") && { self.parser.skip_ws()?; self.parser.consume_char('(') } { self.parser.skip_ws()?; self.newtype_variant = self .parser .exts .contains(Extensions::UNWRAP_VARIANT_NEWTYPES); let v = guard_recursion! { self => visitor.visit_some(&mut *self)? }; self.newtype_variant = false; self.parser.comma()?; if self.parser.consume_char(')') { Ok(v) } else { Err(Error::ExpectedOptionEnd) } } else if self.parser.exts.contains(Extensions::IMPLICIT_SOME) { guard_recursion! { self => visitor.visit_some(&mut *self) } } else { Err(Error::ExpectedOption) } } // In Serde, unit means an anonymous value containing no data. fn deserialize_unit(self, visitor: V) -> Result where V: Visitor<'de>, { if self.newtype_variant || self.parser.consume_str("()") { self.newtype_variant = false; visitor.visit_unit() } else { Err(Error::ExpectedUnit) } } fn deserialize_unit_struct(self, name: &'static str, visitor: V) -> Result where V: Visitor<'de>, { if self.newtype_variant || self.parser.consume_struct_name(name)? { self.newtype_variant = false; visitor.visit_unit() } else { self.deserialize_unit(visitor) } } fn deserialize_newtype_struct(self, name: &'static str, visitor: V) -> Result where V: Visitor<'de>, { if name == crate::value::raw::RAW_VALUE_TOKEN { let src_before = self.parser.pre_ws_src(); self.parser.skip_ws()?; let _ignored = self.deserialize_ignored_any(de::IgnoredAny)?; self.parser.skip_ws()?; let src_after = self.parser.src(); if self.parser.has_unclosed_line_comment() { return Err(Error::UnclosedLineComment); } let ron_str = &src_before[..src_before.len() - src_after.len()]; return visitor .visit_borrowed_str::(ron_str) .map_err(|_| Error::ExpectedRawValue); } if self.parser.exts.contains(Extensions::UNWRAP_NEWTYPES) || self.newtype_variant { self.newtype_variant = false; return guard_recursion! { self => visitor.visit_newtype_struct(&mut *self) }; } self.parser.consume_struct_name(name)?; self.parser.skip_ws()?; if self.parser.consume_char('(') { self.parser.skip_ws()?; let value = guard_recursion! { self => visitor.visit_newtype_struct(&mut *self)? }; self.parser.comma()?; if self.parser.consume_char(')') { Ok(value) } else { Err(Error::ExpectedStructLikeEnd) } } else if name.is_empty() { Err(Error::ExpectedStructLike) } else { Err(Error::ExpectedNamedStructLike(name)) } } fn deserialize_seq(self, visitor: V) -> Result where V: Visitor<'de>, { self.newtype_variant = false; if self.parser.consume_char('[') { let value = guard_recursion! { self => visitor.visit_seq(CommaSeparated::new(Terminator::Seq, self))? }; self.parser.skip_ws()?; if self.parser.consume_char(']') { Ok(value) } else { Err(Error::ExpectedArrayEnd) } } else { Err(Error::ExpectedArray) } } fn deserialize_tuple(self, _len: usize, visitor: V) -> Result where V: Visitor<'de>, { if self.newtype_variant || self.parser.consume_char('(') { let old_newtype_variant = self.newtype_variant; self.newtype_variant = false; let value = guard_recursion! { self => visitor.visit_seq(CommaSeparated::new(Terminator::Tuple, self))? }; self.parser.skip_ws()?; if old_newtype_variant || self.parser.consume_char(')') { Ok(value) } else { Err(Error::ExpectedStructLikeEnd) } } else { Err(Error::ExpectedStructLike) } } fn deserialize_tuple_struct( self, name: &'static str, len: usize, visitor: V, ) -> Result where V: Visitor<'de>, { if !self.newtype_variant { self.parser.consume_struct_name(name)?; } self.deserialize_tuple(len, visitor).map_err(|e| match e { Error::ExpectedStructLike if !name.is_empty() => Error::ExpectedNamedStructLike(name), e => e, }) } fn deserialize_map(self, visitor: V) -> Result where V: Visitor<'de>, { struct VisitorExpecting(V); impl<'de, V: Visitor<'de>> core::fmt::Display for VisitorExpecting<&'_ V> { fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { self.0.expecting(fmt) } } self.newtype_variant = false; // TODO: Avoid allocating to perform this check. let serde_flatten_canary = VisitorExpecting(&visitor) .to_string() .starts_with("struct "); let terminator = if serde_flatten_canary { Terminator::MapAsStruct } else { Terminator::Map }; if self.parser.consume_char('{') { let value = guard_recursion! { self => visitor.visit_map(CommaSeparated::new(terminator, self))? }; self.parser.skip_ws()?; if self.parser.consume_char('}') { Ok(value) } else { Err(Error::ExpectedMapEnd) } } else { Err(Error::ExpectedMap) } } fn deserialize_struct( self, name: &'static str, _fields: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { if !self.newtype_variant { self.parser.consume_struct_name(name)?; } self.parser.skip_ws()?; self.handle_struct_after_name(name, visitor) } fn deserialize_enum( self, name: &'static str, _variants: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { self.newtype_variant = false; match guard_recursion! { self => visitor.visit_enum(Enum::new(self)) } { Ok(value) => Ok(value), Err(Error::NoSuchEnumVariant { expected, found, outer: None, }) if !name.is_empty() => Err(Error::NoSuchEnumVariant { expected, found, outer: Some(String::from(name)), }), Err(e) => Err(e), } } fn deserialize_identifier(self, visitor: V) -> Result where V: Visitor<'de>, { let identifier = self.parser.identifier()?; self.last_identifier = Some(identifier); visitor.visit_borrowed_str(identifier) } fn deserialize_ignored_any(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_any(visitor) } } enum Terminator { Map, MapAsStruct, Tuple, Struct, Seq, } impl Terminator { fn as_char(&self) -> char { match self { Terminator::Map | Terminator::MapAsStruct => '}', Terminator::Tuple | Terminator::Struct => ')', Terminator::Seq => ']', } } } struct CommaSeparated<'a, 'de: 'a> { de: &'a mut Deserializer<'de>, terminator: Terminator, had_comma: bool, inside_internally_tagged_enum: bool, } impl<'a, 'de> CommaSeparated<'a, 'de> { fn new(terminator: Terminator, de: &'a mut Deserializer<'de>) -> Self { CommaSeparated { de, terminator, had_comma: true, inside_internally_tagged_enum: false, } } fn has_element(&mut self) -> Result { self.de.parser.skip_ws()?; match ( self.had_comma, !self.de.parser.check_char(self.terminator.as_char()), ) { // Trailing comma, maybe has a next element (true, has_element) => Ok(has_element), // No trailing comma but terminator (false, false) => Ok(false), // No trailing comma or terminator (false, true) => Err(Error::ExpectedComma), } } } impl<'de, 'a> de::SeqAccess<'de> for CommaSeparated<'a, 'de> { type Error = Error; fn next_element_seed(&mut self, seed: T) -> Result> where T: DeserializeSeed<'de>, { if self.has_element()? { let res = guard_recursion! { self.de => seed.deserialize(&mut *self.de)? }; self.had_comma = self.de.parser.comma()?; Ok(Some(res)) } else { Ok(None) } } } impl<'de, 'a> de::MapAccess<'de> for CommaSeparated<'a, 'de> { type Error = Error; fn next_key_seed(&mut self, seed: K) -> Result> where K: DeserializeSeed<'de>, { if self.has_element()? { self.inside_internally_tagged_enum = is_serde_tag_or_content::(); match self.terminator { Terminator::Struct => guard_recursion! { self.de => seed.deserialize(&mut id::Deserializer::new(&mut *self.de, false)).map(Some) }, Terminator::MapAsStruct => guard_recursion! { self.de => seed.deserialize(&mut id::Deserializer::new(&mut *self.de, true)).map(Some) }, _ => guard_recursion! { self.de => seed.deserialize(&mut *self.de).map(Some) }, } } else { Ok(None) } } fn next_value_seed(&mut self, seed: V) -> Result where V: DeserializeSeed<'de>, { self.de.parser.skip_ws()?; if self.de.parser.consume_char(':') { self.de.parser.skip_ws()?; let res = if self.inside_internally_tagged_enum && !is_serde_content::() { guard_recursion! { self.de => seed.deserialize(&mut tag::Deserializer::new(&mut *self.de))? } } else { guard_recursion! { self.de => seed.deserialize(&mut *self.de)? } }; self.had_comma = self.de.parser.comma()?; Ok(res) } else { Err(Error::ExpectedMapColon) } } } struct Enum<'a, 'de: 'a> { de: &'a mut Deserializer<'de>, } impl<'a, 'de> Enum<'a, 'de> { fn new(de: &'a mut Deserializer<'de>) -> Self { Enum { de } } } impl<'de, 'a> de::EnumAccess<'de> for Enum<'a, 'de> { type Error = Error; type Variant = Self; fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant)> where V: DeserializeSeed<'de>, { self.de.parser.skip_ws()?; let value = guard_recursion! { self.de => seed.deserialize(&mut *self.de)? }; Ok((value, self)) } } impl<'de, 'a> de::VariantAccess<'de> for Enum<'a, 'de> { type Error = Error; fn unit_variant(self) -> Result<()> { Ok(()) } fn newtype_variant_seed(self, seed: T) -> Result where T: DeserializeSeed<'de>, { let newtype_variant = self.de.last_identifier; self.de.parser.skip_ws()?; if self.de.parser.consume_char('(') { self.de.parser.skip_ws()?; self.de.newtype_variant = self .de .parser .exts .contains(Extensions::UNWRAP_VARIANT_NEWTYPES); let val = guard_recursion! { self.de => seed .deserialize(&mut *self.de) .map_err(|err| struct_error_name(err, newtype_variant))? }; self.de.newtype_variant = false; self.de.parser.comma()?; if self.de.parser.consume_char(')') { Ok(val) } else { Err(Error::ExpectedStructLikeEnd) } } else { Err(Error::ExpectedStructLike) } } fn tuple_variant(self, len: usize, visitor: V) -> Result where V: Visitor<'de>, { self.de.parser.skip_ws()?; self.de.deserialize_tuple(len, visitor) } fn struct_variant(self, _fields: &'static [&'static str], visitor: V) -> Result where V: Visitor<'de>, { let struct_variant = self.de.last_identifier; self.de.parser.skip_ws()?; self.de .handle_struct_after_name("", visitor) .map_err(|err| struct_error_name(err, struct_variant)) } } fn struct_error_name(error: Error, name: Option<&str>) -> Error { match error { Error::NoSuchStructField { expected, found, outer: None, } => Error::NoSuchStructField { expected, found, outer: name.map(ToOwned::to_owned), }, Error::MissingStructField { field, outer: None } => Error::MissingStructField { field, outer: name.map(ToOwned::to_owned), }, Error::DuplicateStructField { field, outer: None } => Error::DuplicateStructField { field, outer: name.map(ToOwned::to_owned), }, e => e, } } struct SerdeEnumContent<'a, 'de: 'a> { de: &'a mut Deserializer<'de>, ident: Option<&'a str>, } impl<'de, 'a> de::MapAccess<'de> for SerdeEnumContent<'a, 'de> { type Error = Error; fn next_key_seed(&mut self, seed: K) -> Result> where K: DeserializeSeed<'de>, { self.ident .take() .map(|ident| seed.deserialize(de::value::StrDeserializer::new(ident))) .transpose() } fn next_value_seed(&mut self, seed: V) -> Result where V: DeserializeSeed<'de>, { self.de.parser.skip_ws()?; let old_serde_content_newtype = self.de.serde_content_newtype; self.de.serde_content_newtype = true; let result = seed.deserialize(&mut *self.de); self.de.serde_content_newtype = old_serde_content_newtype; result } } fn is_serde_content() -> bool { #[derive(serde_derive::Deserialize)] enum A {} type B = A; #[derive(serde_derive::Deserialize)] #[serde(untagged)] enum UntaggedEnum { A(A), B(B), } struct TypeIdDeserializer; impl<'de> de::Deserializer<'de> for TypeIdDeserializer { type Error = TypeIdError; fn deserialize_any>(self, _visitor: V) -> Result { Err(TypeIdError(typeid::of::())) } serde::forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } } #[derive(Debug)] struct TypeIdError(core::any::TypeId); impl core::fmt::Display for TypeIdError { fn fmt(&self, _fmt: &mut core::fmt::Formatter) -> core::fmt::Result { Ok(()) } } impl de::Error for TypeIdError { #[allow(clippy::unreachable)] fn custom(_msg: T) -> Self { unreachable!() } } impl de::StdError for TypeIdError {} fn type_id_of_untagged_enum_default_buffer() -> core::any::TypeId { static TYPE_ID: once_cell::race::OnceBox = once_cell::race::OnceBox::new(); *TYPE_ID.get_or_init(|| match Deserialize::deserialize(TypeIdDeserializer) { Ok(UntaggedEnum::A(void) | UntaggedEnum::B(void)) => match void {}, Err(TypeIdError(typeid)) => alloc::boxed::Box::new(typeid), }) } typeid::of::() == type_id_of_untagged_enum_default_buffer() } fn is_serde_tag_or_content() -> bool { #[derive(serde_derive::Deserialize)] enum A {} #[derive(serde_derive::Deserialize)] #[serde(tag = "tag")] enum InternallyTaggedEnum { A { a: A }, } struct TypeIdDeserializer; impl<'de> de::Deserializer<'de> for TypeIdDeserializer { type Error = TypeIdError; fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_map(self) } serde::forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } } impl<'de> de::MapAccess<'de> for TypeIdDeserializer { type Error = TypeIdError; fn next_key_seed>( &mut self, _seed: K, ) -> Result, Self::Error> { Err(TypeIdError(typeid::of::())) } #[allow(clippy::unreachable)] fn next_value_seed>( &mut self, _seed: V, ) -> Result { unreachable!() } } #[derive(Debug)] struct TypeIdError(core::any::TypeId); impl core::fmt::Display for TypeIdError { fn fmt(&self, _fmt: &mut core::fmt::Formatter) -> core::fmt::Result { Ok(()) } } impl de::Error for TypeIdError { #[allow(clippy::unreachable)] fn custom(_msg: T) -> Self { unreachable!() } } impl de::StdError for TypeIdError {} fn type_id_of_internally_tagged_enum_default_tag_or_buffer() -> core::any::TypeId { static TYPE_ID: once_cell::race::OnceBox = once_cell::race::OnceBox::new(); *TYPE_ID.get_or_init(|| match Deserialize::deserialize(TypeIdDeserializer) { Ok(InternallyTaggedEnum::A { a: void }) => match void {}, Err(TypeIdError(typeid)) => alloc::boxed::Box::new(typeid), }) } typeid::of::() == type_id_of_internally_tagged_enum_default_tag_or_buffer() } ron-0.12.0/src/de/tag.rs000064400000000000000000000124741046102023000130120ustar 00000000000000use serde::de::{self, Visitor}; use super::{Error, Result}; pub struct Deserializer<'a, 'b: 'a> { de: &'a mut super::Deserializer<'b>, } impl<'a, 'b: 'a> Deserializer<'a, 'b> { pub fn new(de: &'a mut super::Deserializer<'b>) -> Self { Deserializer { de } } } impl<'a, 'b: 'a, 'c> de::Deserializer<'b> for &'c mut Deserializer<'a, 'b> { type Error = Error; fn deserialize_str(self, visitor: V) -> Result where V: Visitor<'b>, { self.de.deserialize_str(visitor) } fn deserialize_string(self, visitor: V) -> core::result::Result where V: Visitor<'b>, { self.deserialize_str(visitor) } fn deserialize_identifier(self, visitor: V) -> Result where V: Visitor<'b>, { self.deserialize_str(visitor) } fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'b>, { self.deserialize_str(visitor) } fn deserialize_bool(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_i8(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_i16(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_i32(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_i64(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } #[cfg(feature = "integer128")] fn deserialize_i128(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_u8(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_u16(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_u32(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_u64(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } #[cfg(feature = "integer128")] fn deserialize_u128(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_f32(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_f64(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_char(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_bytes(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_byte_buf(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_option(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_unit(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_unit_struct(self, _name: &'static str, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_newtype_struct(self, _name: &'static str, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_seq(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_tuple(self, _len: usize, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_tuple_struct( self, _name: &'static str, _len: usize, _visitor: V, ) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_map(self, _visitor: V) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_struct( self, _name: &'static str, _fields: &'static [&'static str], _visitor: V, ) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_enum( self, _name: &'static str, _variants: &'static [&'static str], _visitor: V, ) -> Result where V: Visitor<'b>, { Err(Error::ExpectedString) } fn deserialize_ignored_any(self, visitor: V) -> Result where V: Visitor<'b>, { self.deserialize_any(visitor) } } ron-0.12.0/src/de/tests.rs000064400000000000000000000542111046102023000133740ustar 00000000000000use alloc::{ borrow::ToOwned, format, string::{String, ToString}, vec, vec::Vec, }; use serde_bytes; use serde_derive::Deserialize; use crate::{ error::{Error, Position, Span, SpannedError, SpannedResult}, parse::Parser, value::Number, }; #[cfg(feature = "internal-span-substring-test")] use crate::util::span_substring::check_error_span_inclusive; #[cfg(feature = "internal-span-substring-test")] use crate::util::span_substring::check_error_span_exclusive; #[derive(Debug, PartialEq, Deserialize)] struct EmptyStruct1; #[derive(Debug, PartialEq, Deserialize)] struct EmptyStruct2 {} #[derive(Clone, Debug, PartialEq, Deserialize)] struct NewType(i32); #[derive(Clone, Copy, Debug, PartialEq, Deserialize)] #[serde(rename = "")] struct UnnamedNewType(i32); #[derive(Debug, PartialEq, Deserialize)] struct TupleStruct(f32, f32); #[derive(Clone, Debug, PartialEq, Deserialize)] #[serde(rename = "")] struct UnnamedTupleStruct(f32, f32); #[derive(Clone, Copy, Debug, PartialEq, Deserialize)] struct MyStruct { x: f32, y: f32, } #[derive(Clone, Copy, Debug, PartialEq, Deserialize)] enum MyEnum { A, B(bool), C(bool, f32), D { a: i32, b: i32 }, } #[derive(Debug, Deserialize, PartialEq)] struct BytesStruct { small: Vec, #[serde(with = "serde_bytes")] large: Vec, } #[test] fn test_empty_struct() { check_from_str_bytes_reader("EmptyStruct1", Ok(EmptyStruct1)); check_from_str_bytes_reader("EmptyStruct2()", Ok(EmptyStruct2 {})); } #[test] fn test_struct() { let my_struct = MyStruct { x: 4.0, y: 7.0 }; check_from_str_bytes_reader("MyStruct(x:4,y:7,)", Ok(my_struct)); check_from_str_bytes_reader("(x:4,y:7)", Ok(my_struct)); check_from_str_bytes_reader("NewType(42)", Ok(NewType(42))); check_from_str_bytes_reader("(33)", Ok(NewType(33))); let bogus_struct = "NewType"; let expected_err = Err(SpannedError { code: Error::ExpectedNamedStructLike("NewType"), span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 8 }, }, }); check_from_str_bytes_reader::(bogus_struct, expected_err.clone()); #[cfg(feature = "internal-span-substring-test")] check_error_span_exclusive::(bogus_struct, expected_err, "NewType"); check_from_str_bytes_reader::( "", Err(SpannedError { code: Error::ExpectedStructLike, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 }, }, }), ); check_from_str_bytes_reader("(33)", Ok(UnnamedNewType(33))); let bogus_struct = "NewType"; let expected_err = Err(SpannedError { code: Error::ExpectedNamedStructLike(""), span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 8 }, }, }); check_from_str_bytes_reader::(bogus_struct, expected_err.clone()); #[cfg(feature = "internal-span-substring-test")] check_error_span_exclusive::(bogus_struct, expected_err, "NewType"); check_from_str_bytes_reader("TupleStruct(2,5,)", Ok(TupleStruct(2.0, 5.0))); check_from_str_bytes_reader("(3,4)", Ok(TupleStruct(3.0, 4.0))); check_from_str_bytes_reader::( "", Err(SpannedError { code: Error::ExpectedNamedStructLike("TupleStruct"), span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 }, }, }), ); let bogus_struct = "TupleStruct(2,5,)"; let expected_err = Err(SpannedError { code: Error::ExpectedNamedStructLike(""), span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 12 }, }, }); check_from_str_bytes_reader::(bogus_struct, expected_err.clone()); #[cfg(feature = "internal-span-substring-test")] check_error_span_exclusive::(bogus_struct, expected_err, "TupleStruct"); check_from_str_bytes_reader("(3,4)", Ok(UnnamedTupleStruct(3.0, 4.0))); check_from_str_bytes_reader::( "", Err(SpannedError { code: Error::ExpectedStructLike, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 }, }, }), ); } #[test] fn test_unclosed_limited_seq_struct() { #[derive(Debug, PartialEq)] struct LimitedStruct; impl<'de> serde::Deserialize<'de> for LimitedStruct { fn deserialize>(deserializer: D) -> Result { struct Visitor; impl<'de> serde::de::Visitor<'de> for Visitor { type Value = LimitedStruct; // GRCOV_EXCL_START fn expecting(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { fmt.write_str("struct LimitedStruct") } // GRCOV_EXCL_STOP fn visit_map>( self, _map: A, ) -> Result { Ok(LimitedStruct) } } deserializer.deserialize_struct("LimitedStruct", &[], Visitor) } } check_from_str_bytes_reader::( "(", Err(SpannedError { code: Error::ExpectedStructLikeEnd, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 2 }, }, }), ) } #[test] fn test_unclosed_limited_seq() { #[derive(Debug, PartialEq)] struct LimitedSeq; impl<'de> serde::Deserialize<'de> for LimitedSeq { fn deserialize>(deserializer: D) -> Result { struct Visitor; impl<'de> serde::de::Visitor<'de> for Visitor { type Value = LimitedSeq; // GRCOV_EXCL_START fn expecting(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { fmt.write_str("an empty sequence") } // GRCOV_EXCL_STOP fn visit_seq>( self, _seq: A, ) -> Result { Ok(LimitedSeq) } } deserializer.deserialize_seq(Visitor) } } check_from_str_bytes_reader::( "[", Err(SpannedError { code: Error::ExpectedArrayEnd, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 2 }, }, }), ); assert_eq!( crate::Value::from(vec![42]).into_rust::(), Err(Error::ExpectedDifferentLength { expected: String::from("a sequence of length 0"), found: 1 }) ); } #[test] fn test_unclosed_limited_map() { #[derive(Debug, PartialEq)] struct LimitedMap; impl<'de> serde::Deserialize<'de> for LimitedMap { fn deserialize>(deserializer: D) -> Result { struct Visitor; impl<'de> serde::de::Visitor<'de> for Visitor { type Value = LimitedMap; // GRCOV_EXCL_START fn expecting(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { fmt.write_str("an empty map") } // GRCOV_EXCL_STOP fn visit_map>( self, _map: A, ) -> Result { Ok(LimitedMap) } } deserializer.deserialize_map(Visitor) } } check_from_str_bytes_reader::( "{", Err(SpannedError { code: Error::ExpectedMapEnd, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 2 }, }, }), ); assert_eq!( crate::Value::Map([("a", 42)].into_iter().collect()).into_rust::(), Err(Error::ExpectedDifferentLength { expected: String::from("a map of length 0"), found: 1 }) ); } #[test] fn test_option() { check_from_str_bytes_reader("Some(1)", Ok(Some(1u8))); check_from_str_bytes_reader("None", Ok(None::)); } #[test] fn test_enum() { check_from_str_bytes_reader("A", Ok(MyEnum::A)); check_from_str_bytes_reader("B(true,)", Ok(MyEnum::B(true))); check_from_str_bytes_reader::( "B", Err(SpannedError { code: Error::ExpectedStructLike, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 2 }, }, }), ); check_from_str_bytes_reader("C(true,3.5,)", Ok(MyEnum::C(true, 3.5))); check_from_str_bytes_reader("D(a:2,b:3,)", Ok(MyEnum::D { a: 2, b: 3 })); } #[test] fn test_array() { check_from_str_bytes_reader::<[i32; 0]>("()", Ok([])); check_from_str_bytes_reader("[]", Ok(Vec::::new())); check_from_str_bytes_reader("(2,3,4,)", Ok([2, 3, 4i32])); check_from_str_bytes_reader("[2,3,4,]", Ok([2, 3, 4i32].to_vec())); } #[cfg(feature = "std")] #[test] fn test_map() { use std::collections::HashMap; let mut map = HashMap::new(); map.insert((true, false), 4); map.insert((false, false), 123); check_from_str_bytes_reader( "{ (true,false,):4, (false,false,):123, }", Ok(map), ); } #[test] fn test_string() { check_from_str_bytes_reader("\"String\"", Ok(String::from("String"))); check_from_str_bytes_reader("r\"String\"", Ok(String::from("String"))); check_from_str_bytes_reader("r#\"String\"#", Ok(String::from("String"))); check_from_str_bytes_reader( "r#\"String with\nmultiple\nlines\n\"#", Ok(String::from("String with\nmultiple\nlines\n")), ); check_from_str_bytes_reader( "r##\"String with \"#\"##", Ok(String::from("String with \"#")), ); } #[test] fn test_char() { check_from_str_bytes_reader("'c'", Ok('c')); } #[test] fn test_escape_char() { check_from_str_bytes_reader("'\\''", Ok('\'')); } #[test] fn test_escape() { check_from_str_bytes_reader(r#""\"Quoted\"""#, Ok(String::from("\"Quoted\""))); } #[test] fn test_comment() { check_from_str_bytes_reader( "( x: 1.0, // x is just 1 // There is another comment in the very next line.. // And y is indeed y: 2.0 // 2! )", Ok(MyStruct { x: 1.0, y: 2.0 }), ); } fn err( kind: Error, (line_start, col_start): (usize, usize), (line_end, col_end): (usize, usize), ) -> SpannedResult { Err(SpannedError { code: kind, span: Span { start: Position { line: line_start, col: col_start, }, end: Position { line: line_end, col: col_end, }, }, }) } #[test] fn test_err_wrong_value() { #[cfg(feature = "std")] use std::collections::HashMap; check_from_str_bytes_reader::("'c'", err(Error::ExpectedFloat, (1, 1), (1, 1))); check_from_str_bytes_reader::("'c'", err(Error::ExpectedString, (1, 1), (1, 1))); #[cfg(feature = "std")] check_from_str_bytes_reader::>( "'c'", err(Error::ExpectedMap, (1, 1), (1, 1)), ); check_from_str_bytes_reader::<[u8; 5]>("'c'", err(Error::ExpectedStructLike, (1, 1), (1, 1))); check_from_str_bytes_reader::>("'c'", err(Error::ExpectedArray, (1, 1), (1, 1))); check_from_str_bytes_reader::("'c'", err(Error::ExpectedIdentifier, (1, 1), (1, 1))); check_from_str_bytes_reader::( "'c'", err(Error::ExpectedNamedStructLike("MyStruct"), (1, 1), (1, 1)), ); check_from_str_bytes_reader::( "NotMyStruct(x: 4, y: 2)", err( Error::ExpectedDifferentStructName { expected: "MyStruct", found: String::from("NotMyStruct"), }, (1, 1), (1, 12), ), ); check_from_str_bytes_reader::<(u8, bool)>( "'c'", err(Error::ExpectedStructLike, (1, 1), (1, 1)), ); check_from_str_bytes_reader::("notabool", err(Error::ExpectedBoolean, (1, 1), (1, 1))); let bogus_struct = "MyStruct(\n x: true)"; let expected_err = err(Error::ExpectedFloat, (2, 7), (2, 8)); check_from_str_bytes_reader::(bogus_struct, expected_err.clone()); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::(bogus_struct, expected_err, " t"); let bogus_struct = "MyStruct(\n x: 3.5, \n y:)"; let expected_err = err(Error::ExpectedFloat, (3, 7), (3, 7)); check_from_str_bytes_reader::(bogus_struct, expected_err.clone()); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::(bogus_struct, expected_err, ")"); } #[test] fn test_perm_ws() { check_from_str_bytes_reader( "\nMyStruct \t ( \n x : 3.5 , \t y\n: 4.5 \n ) \t\n", Ok(MyStruct { x: 3.5, y: 4.5 }), ); } #[test] fn untagged() { #[derive(Deserialize, Clone, Debug, PartialEq)] #[serde(untagged)] enum Untagged { U8(u8), Bool(bool), Value(crate::Value), } check_from_str_bytes_reader("true", Ok(Untagged::Bool(true))); check_from_str_bytes_reader("8", Ok(Untagged::U8(8))); // Check for a failure in Deserializer::check_struct_type // - untagged enum and a leading identifier trigger the serde content enum path // - serde content uses deserialize_any, which retriggers the struct type check // - struct type check inside a serde content performs a full newtype check // - newtype check fails on the unclosed struct // let bogus_struct = "Value(()"; let expected_err = Err(crate::error::SpannedError { code: crate::Error::Eof, span: Span { start: Position { line: 1, col: 8 }, end: crate::error::Position { line: 1, col: 9 }, }, }); check_from_str_bytes_reader::(bogus_struct, expected_err.clone()); #[cfg(feature = "internal-span-substring-test")] check_error_span_exclusive::(bogus_struct, expected_err, ")"); } #[test] fn rename() { #[derive(Deserialize, Debug, PartialEq)] enum Foo { #[serde(rename = "2d")] D2, #[serde(rename = "triangle-list")] TriangleList, } check_from_str_bytes_reader("r#2d", Ok(Foo::D2)); check_from_str_bytes_reader("r#triangle-list", Ok(Foo::TriangleList)); } #[test] fn forgot_apostrophes() { let bogus_struct = "(4, \"Hello)"; let expected_err = Err(SpannedError { code: Error::ExpectedStringEnd, span: Span { start: Position { line: 1, col: 5 }, end: Position { line: 1, col: 6 }, }, }); check_from_str_bytes_reader::<(i32, String)>(bogus_struct, expected_err.clone()); #[cfg(feature = "internal-span-substring-test")] check_error_span_exclusive::<(i32, String)>(bogus_struct, expected_err, "\""); } #[test] fn expected_attribute() { check_from_str_bytes_reader::( "#\"Hello\"", err(Error::ExpectedAttribute, (1, 2), (1, 2)), ); } #[test] fn expected_attribute_end() { let bogus_struct = "#![enable(unwrap_newtypes) \"Hello\""; let expected_err = err(Error::ExpectedAttributeEnd, (1, 27), (1, 28)); check_from_str_bytes_reader::(bogus_struct, expected_err.clone()); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::(bogus_struct, expected_err, " \""); } #[test] fn invalid_attribute() { let bogus_struct = "#![enable(invalid)] \"Hello\""; let expected_err = err( Error::NoSuchExtension("invalid".to_string()), (1, 11), (1, 18), ); check_from_str_bytes_reader::(bogus_struct, expected_err.clone()); #[cfg(feature = "internal-span-substring-test")] check_error_span_exclusive::(bogus_struct, expected_err, "invalid"); } #[test] fn multiple_attributes() { #[derive(Debug, Deserialize, PartialEq)] struct New(String); check_from_str_bytes_reader( "#![enable(unwrap_newtypes)] #![enable(unwrap_newtypes)] \"Hello\"", Ok(New("Hello".to_owned())), ); } #[test] fn uglified_attribute() { check_from_str_bytes_reader( "# !\ // We definitely want to add a comment here [\t\tenable( // best style ever unwrap_newtypes ) ] ()", Ok(()), ); } #[test] fn implicit_some() { use serde::de::DeserializeOwned; fn de(s: &str) -> Option { let enable = "#![enable(implicit_some)]\n".to_string(); super::from_str::>(&(enable + s)).unwrap() } assert_eq!(de("'c'"), Some('c')); assert_eq!(de("5"), Some(5)); assert_eq!(de("\"Hello\""), Some("Hello".to_owned())); assert_eq!(de("false"), Some(false)); assert_eq!( de("MyStruct(x: .4, y: .5)"), Some(MyStruct { x: 0.4, y: 0.5 }) ); assert_eq!(de::("None"), None); // Not concise assert_eq!(de::>>("None"), None); } #[test] fn ws_tuple_newtype_variant() { check_from_str_bytes_reader("B ( \n true \n ) ", Ok(MyEnum::B(true))); } #[test] fn test_byte_stream() { check_from_str_bytes_reader( "BytesStruct( small:[1, 2], large:b\"\\x01\\x02\\x03\\x04\" )", Ok(BytesStruct { small: vec![1, 2], large: vec![1, 2, 3, 4], }), ); } #[test] fn test_numbers() { check_from_str_bytes_reader( "[1_234, 12_345, 1_2_3_4_5_6, 1_234_567, 5_55_55_5]", Ok(vec![1234, 12345, 123_456, 1_234_567, 555_555]), ); } fn check_de_any_number< T: Copy + PartialEq + core::fmt::Debug + Into + serde::de::DeserializeOwned, >( s: &str, cmp: T, ) { let mut parser = Parser::new(s).unwrap(); let number = parser.any_number().unwrap(); assert_eq!(number, Number::new(cmp)); assert_eq!( Number::new(super::from_str::(s).unwrap()), Number::new(cmp) ); } #[test] fn test_any_number_precision() { check_de_any_number("1", 1_u8); check_de_any_number("+1", 1_u8); check_de_any_number("-1", -1_i8); check_de_any_number("-1.0", -1.0_f32); check_de_any_number("1.", 1.0_f32); check_de_any_number("-1.", -1.0_f32); check_de_any_number(".3", 0.3_f64); check_de_any_number("-.3", -0.3_f64); check_de_any_number("+.3", 0.3_f64); check_de_any_number("0.3", 0.3_f64); check_de_any_number("NaN", f32::NAN); check_de_any_number("-NaN", -f32::NAN); check_de_any_number("inf", f32::INFINITY); check_de_any_number("-inf", f32::NEG_INFINITY); macro_rules! test_min { ($($ty:ty),*) => { $(check_de_any_number(&format!("{}", <$ty>::MIN), <$ty>::MIN);)* }; } macro_rules! test_max { ($($ty:ty),*) => { $(check_de_any_number(&format!("{}", <$ty>::MAX), <$ty>::MAX);)* }; } test_min! { i8, i16, i32, i64, f64 } test_max! { u8, u16, u32, u64, f64 } #[cfg(feature = "integer128")] test_min! { i128 } #[cfg(feature = "integer128")] test_max! { u128 } } #[test] fn test_value_special_floats() { use crate::{from_str, value::Number, Value}; assert_eq!( from_str("NaN"), Ok(Value::Number(Number::F32(f32::NAN.into()))) ); assert_eq!( from_str("+NaN"), Ok(Value::Number(Number::F32(f32::NAN.into()))) ); assert_eq!( from_str("-NaN"), Ok(Value::Number(Number::F32((-f32::NAN).into()))) ); assert_eq!( from_str("inf"), Ok(Value::Number(Number::F32(f32::INFINITY.into()))) ); assert_eq!( from_str("+inf"), Ok(Value::Number(Number::F32(f32::INFINITY.into()))) ); assert_eq!( from_str("-inf"), Ok(Value::Number(Number::F32(f32::NEG_INFINITY.into()))) ); } #[test] fn test_leading_whitespace() { check_from_str_bytes_reader(" +1", Ok(1_u8)); check_from_str_bytes_reader(" EmptyStruct1", Ok(EmptyStruct1)); } fn check_from_str_bytes_reader( ron: &str, check: SpannedResult, ) { let res_str = super::from_str::(ron); assert_eq!(res_str, check); let res_bytes = super::from_bytes::(ron.as_bytes()); assert_eq!(res_bytes, check); #[cfg(feature = "std")] { let res_reader = super::from_reader::<&[u8], T>(ron.as_bytes()); assert_eq!(res_reader, check); } } #[test] fn test_remainder() { let mut deserializer = super::Deserializer::from_str(" 42 ").unwrap(); assert_eq!( ::deserialize(&mut deserializer).unwrap(), 42 ); assert_eq!(deserializer.remainder(), " "); assert_eq!(deserializer.end(), Ok(())); let mut deserializer = super::Deserializer::from_str(" 42 37 ").unwrap(); assert_eq!( ::deserialize(&mut deserializer).unwrap(), 42 ); assert_eq!(deserializer.remainder(), " 37 "); assert_eq!(deserializer.end(), Err(Error::TrailingCharacters)); } #[test] fn boolean_struct_name() { check_from_str_bytes_reader::( "true_", Err(SpannedError { code: Error::ExpectedBoolean, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 }, }, }), ); check_from_str_bytes_reader::( "false_", Err(SpannedError { code: Error::ExpectedBoolean, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 }, }, }), ); } ron-0.12.0/src/de/value.rs000064400000000000000000000265461046102023000133600ustar 00000000000000use alloc::{borrow::ToOwned, boxed::Box, string::String, vec::Vec}; use core::fmt; use serde::{ de::{Error, MapAccess, SeqAccess, Visitor}, Deserialize, Deserializer, }; use crate::{ error::SpannedResult, value::{Map, Number, Value}, }; impl core::str::FromStr for Value { type Err = crate::error::SpannedError; /// Creates a value from a string reference. fn from_str(s: &str) -> SpannedResult { let mut de = super::Deserializer::from_str(s)?; let val = Value::deserialize(&mut de).map_err(|e| de.span_error(e))?; de.end().map_err(|e| de.span_error(e))?; Ok(val) } } impl<'de> Deserialize<'de> for Value { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_any(ValueVisitor) } } struct ValueVisitor; impl<'de> Visitor<'de> for ValueVisitor { type Value = Value; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "a RON value") } fn visit_bool(self, v: bool) -> Result where E: Error, { Ok(Value::Bool(v)) } fn visit_i8(self, v: i8) -> Result where E: Error, { Ok(Value::Number(Number::new(v))) } fn visit_i16(self, v: i16) -> Result where E: Error, { Ok(Value::Number(Number::new(v))) } fn visit_i32(self, v: i32) -> Result where E: Error, { Ok(Value::Number(Number::new(v))) } fn visit_i64(self, v: i64) -> Result where E: Error, { Ok(Value::Number(Number::new(v))) } #[cfg(feature = "integer128")] fn visit_i128(self, v: i128) -> Result where E: Error, { Ok(Value::Number(Number::new(v))) } fn visit_u8(self, v: u8) -> Result where E: Error, { Ok(Value::Number(Number::new(v))) } fn visit_u16(self, v: u16) -> Result where E: Error, { Ok(Value::Number(Number::new(v))) } fn visit_u32(self, v: u32) -> Result where E: Error, { Ok(Value::Number(Number::new(v))) } fn visit_u64(self, v: u64) -> Result where E: Error, { Ok(Value::Number(Number::new(v))) } #[cfg(feature = "integer128")] fn visit_u128(self, v: u128) -> Result where E: Error, { Ok(Value::Number(Number::new(v))) } fn visit_f32(self, v: f32) -> Result where E: Error, { Ok(Value::Number(Number::new(v))) } fn visit_f64(self, v: f64) -> Result where E: Error, { Ok(Value::Number(Number::new(v))) } fn visit_char(self, v: char) -> Result where E: Error, { Ok(Value::Char(v)) } fn visit_str(self, v: &str) -> Result where E: Error, { self.visit_string(v.to_owned()) } fn visit_string(self, v: String) -> Result where E: Error, { Ok(Value::String(v)) } fn visit_bytes(self, v: &[u8]) -> Result where E: Error, { self.visit_byte_buf(v.to_vec()) } fn visit_byte_buf(self, v: Vec) -> Result where E: Error, { Ok(Value::Bytes(v)) } fn visit_none(self) -> Result where E: Error, { Ok(Value::Option(None)) } fn visit_some(self, deserializer: D) -> Result where D: Deserializer<'de>, { Ok(Value::Option(Some(Box::new( deserializer.deserialize_any(ValueVisitor)?, )))) } fn visit_unit(self) -> Result where E: Error, { Ok(Value::Unit) } fn visit_newtype_struct(self, deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_any(ValueVisitor) } fn visit_seq(self, mut seq: A) -> Result where A: SeqAccess<'de>, { let mut vec = Vec::new(); if let Some(cap) = seq.size_hint() { vec.reserve_exact(cap); } while let Some(x) = seq.next_element()? { vec.push(x); } Ok(Value::Seq(vec)) } fn visit_map(self, mut map: A) -> Result where A: MapAccess<'de>, { let mut res: Map = Map::new(); #[cfg(feature = "indexmap")] if let Some(cap) = map.size_hint() { res.0.reserve_exact(cap); } while let Some(entry) = map.next_entry::()? { res.insert(entry.0, entry.1); } Ok(Value::Map(res)) } } #[cfg(test)] mod tests { use alloc::vec; use core::str::FromStr; use super::*; fn eval(s: &str) -> Value { s.parse().expect("Failed to parse") } #[test] fn test_none() { assert_eq!(eval("None"), Value::Option(None)); } #[test] fn test_some() { assert_eq!(eval("Some(())"), Value::Option(Some(Box::new(Value::Unit)))); assert_eq!( eval("Some ( () )"), Value::Option(Some(Box::new(Value::Unit))) ); } #[test] fn test_tuples_basic() { assert_eq!( eval("(3, 4.0, 5.0)"), Value::Seq(vec![ Value::Number(Number::U8(3)), Value::Number(Number::F32(4.0.into())), Value::Number(Number::F32(5.0.into())), ],), ); } #[test] fn test_tuples_ident() { assert_eq!( eval("(true, 3, 4, 5.0)"), Value::Seq(vec![ Value::Bool(true), Value::Number(Number::U8(3)), Value::Number(Number::U8(4)), Value::Number(Number::F32(5.0.into())), ]), ); } #[test] fn test_tuples_error() { use crate::de::{Error, Position, Span, SpannedError}; assert_eq!( Value::from_str("Foo:").unwrap_err(), SpannedError { code: Error::TrailingCharacters, span: Span { start: Position { line: 1, col: 4 }, end: Position { line: 1, col: 4 } } }, ); } #[test] fn test_floats() { assert_eq!( eval("(inf, -inf, NaN)"), Value::Seq(vec![ Value::Number(Number::new(core::f32::INFINITY)), Value::Number(Number::new(core::f32::NEG_INFINITY)), Value::Number(Number::new(core::f32::NAN)), ]), ); } #[test] fn test_complex() { assert_eq!( eval( "Some([ Room ( width: 20, height: 5, name: \"The Room\" ), ( width: 10.0, height: 10.0, name: \"Another room\", enemy_levels: { \"Enemy1\": 3, \"Enemy2\": 5, \"Enemy3\": 7, }, ), ])" ), Value::Option(Some(Box::new(Value::Seq(vec![ Value::Map( vec![ ( Value::String("width".to_owned()), Value::Number(Number::U8(20)), ), ( Value::String("height".to_owned()), Value::Number(Number::U8(5)), ), ( Value::String("name".to_owned()), Value::String("The Room".to_owned()), ), ] .into_iter() .collect(), ), Value::Map( vec![ ( Value::String("width".to_owned()), Value::Number(Number::F32(10.0.into())), ), ( Value::String("height".to_owned()), Value::Number(Number::F32(10.0.into())), ), ( Value::String("name".to_owned()), Value::String("Another room".to_owned()), ), ( Value::String("enemy_levels".to_owned()), Value::Map( vec![ ( Value::String("Enemy1".to_owned()), Value::Number(Number::U8(3)), ), ( Value::String("Enemy2".to_owned()), Value::Number(Number::U8(5)), ), ( Value::String("Enemy3".to_owned()), Value::Number(Number::U8(7)), ), ] .into_iter() .collect(), ), ), ] .into_iter() .collect(), ), ])))) ); } #[test] fn test_struct() { assert_eq!( eval("(a:42)"), Value::Map( [( Value::String(String::from("a")), Value::Number(Number::U8(42)) )] .into_iter() .collect() ), ); assert_eq!( eval("(r#a:42)"), Value::Map( [( Value::String(String::from("a")), Value::Number(Number::U8(42)) )] .into_iter() .collect() ), ); assert_eq!( "(r#:42)".parse::().unwrap_err(), crate::error::SpannedError { code: crate::Error::ExpectedString, span: crate::error::Span { start: crate::error::Position { line: 1, col: 3 }, end: crate::error::Position { line: 1, col: 4 }, } }, ); // Check for a failure in Deserializer::check_struct_type // - opening brace triggers the struct type check // - unclosed block comment fails the whitespace skip assert_eq!( "( /*".parse::().unwrap_err(), crate::error::SpannedError { code: crate::Error::UnclosedBlockComment, span: crate::error::Span { start: crate::error::Position { line: 1, col: 3 }, end: crate::error::Position { line: 1, col: 5 }, } }, ); } } ron-0.12.0/src/error.rs000064400000000000000000000621571046102023000130030ustar 00000000000000use alloc::string::{String, ToString}; use core::{ fmt, str::{self, Utf8Error}, }; use serde::{ de, ser::{self, StdError}, }; use unicode_ident::is_xid_continue; use crate::parse::{is_ident_first_char, is_ident_raw_char}; #[cfg(feature = "std")] use std::io; /// This type represents all possible errors that can occur when /// serializing or deserializing RON data. #[allow(clippy::module_name_repetitions)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct SpannedError { pub code: Error, pub span: Span, } pub type Result = core::result::Result; pub type SpannedResult = core::result::Result; #[derive(Clone, Debug, PartialEq, Eq)] #[non_exhaustive] pub enum Error { Fmt, Io(String), Message(String), Eof, ExpectedArray, ExpectedArrayEnd, ExpectedAttribute, ExpectedAttributeEnd, ExpectedBoolean, ExpectedComma, ExpectedChar, ExpectedByteLiteral, ExpectedFloat, FloatUnderscore, ExpectedInteger, ExpectedOption, ExpectedOptionEnd, ExpectedMap, ExpectedMapColon, ExpectedMapEnd, ExpectedDifferentStructName { expected: &'static str, found: String, }, ExpectedStructLike, ExpectedNamedStructLike(&'static str), ExpectedStructLikeEnd, ExpectedUnit, ExpectedString, ExpectedByteString, ExpectedStringEnd, ExpectedIdentifier, InvalidEscape(&'static str), IntegerOutOfBounds, InvalidIntegerDigit { digit: char, base: u8, }, NoSuchExtension(String), UnclosedBlockComment, UnclosedLineComment, UnderscoreAtBeginning, UnexpectedChar(char), Utf8Error(Utf8Error), TrailingCharacters, InvalidValueForType { expected: String, found: String, }, ExpectedDifferentLength { expected: String, found: usize, }, NoSuchEnumVariant { expected: &'static [&'static str], found: String, outer: Option, }, NoSuchStructField { expected: &'static [&'static str], found: String, outer: Option, }, MissingStructField { field: &'static str, outer: Option, }, DuplicateStructField { field: &'static str, outer: Option, }, InvalidIdentifier(String), SuggestRawIdentifier(String), ExpectedRawValue, ExceededRecursionLimit, ExpectedStructName(String), } impl fmt::Display for SpannedError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}: {}", self.span, self.code) } } impl fmt::Display for Error { #[allow(clippy::too_many_lines)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Error::Fmt => f.write_str("Formatting RON failed"), Error::Io(ref s) | Error::Message(ref s) => f.write_str(s), Error::Eof => f.write_str("Unexpected end of RON"), Error::ExpectedArray => f.write_str("Expected opening `[`"), Error::ExpectedArrayEnd => f.write_str("Expected closing `]`"), Error::ExpectedAttribute => f.write_str("Expected an `#![enable(...)]` attribute"), Error::ExpectedAttributeEnd => { f.write_str("Expected closing `)]` after the enable attribute") } Error::ExpectedBoolean => f.write_str("Expected boolean"), Error::ExpectedComma => f.write_str("Expected comma"), Error::ExpectedChar => f.write_str("Expected char"), Error::ExpectedByteLiteral => f.write_str("Expected byte literal"), Error::ExpectedFloat => f.write_str("Expected float"), Error::FloatUnderscore => f.write_str("Unexpected underscore in float"), Error::ExpectedInteger => f.write_str("Expected integer"), Error::ExpectedOption => f.write_str("Expected option"), Error::ExpectedOptionEnd | Error::ExpectedStructLikeEnd => { f.write_str("Expected closing `)`") } Error::ExpectedMap => f.write_str("Expected opening `{`"), Error::ExpectedMapColon => f.write_str("Expected colon"), Error::ExpectedMapEnd => f.write_str("Expected closing `}`"), Error::ExpectedDifferentStructName { expected, ref found, } => write!( f, "Expected struct {} but found {}", Identifier(expected), Identifier(found) ), Error::ExpectedStructLike => f.write_str("Expected opening `(`"), Error::ExpectedNamedStructLike(name) => { if name.is_empty() { f.write_str("Expected only opening `(`, no name, for un-nameable struct") } else { write!(f, "Expected opening `(` for struct {}", Identifier(name)) } } Error::ExpectedUnit => f.write_str("Expected unit"), Error::ExpectedString => f.write_str("Expected string"), Error::ExpectedByteString => f.write_str("Expected byte string"), Error::ExpectedStringEnd => f.write_str("Expected end of string"), Error::ExpectedIdentifier => f.write_str("Expected identifier"), Error::InvalidEscape(s) => f.write_str(s), Error::IntegerOutOfBounds => f.write_str("Integer is out of bounds"), Error::InvalidIntegerDigit { digit, base } => { write!(f, "Invalid digit {:?} for base {} integers", digit, base) } Error::NoSuchExtension(ref name) => { write!(f, "No RON extension named {}", Identifier(name)) } Error::Utf8Error(ref e) => fmt::Display::fmt(e, f), Error::UnclosedBlockComment => f.write_str("Unclosed block comment"), Error::UnclosedLineComment => f.write_str( "`ron::value::RawValue` cannot end in unclosed line comment, \ try using a block comment or adding a newline", ), Error::UnderscoreAtBeginning => { f.write_str("Unexpected leading underscore in a number") } Error::UnexpectedChar(c) => write!(f, "Unexpected char {:?}", c), Error::TrailingCharacters => f.write_str("Non-whitespace trailing characters"), Error::InvalidValueForType { ref expected, ref found, } => { write!(f, "Expected {} but found {} instead", expected, found) } Error::ExpectedDifferentLength { ref expected, found, } => { write!(f, "Expected {} but found ", expected)?; match found { 0 => f.write_str("zero elements")?, 1 => f.write_str("one element")?, n => write!(f, "{} elements", n)?, } f.write_str(" instead") } Error::NoSuchEnumVariant { expected, ref found, ref outer, } => { f.write_str("Unexpected ")?; if outer.is_none() { f.write_str("enum ")?; } write!(f, "variant named {}", Identifier(found))?; if let Some(outer) = outer { write!(f, " in enum {}", Identifier(outer))?; } write!( f, ", {}", OneOf { alts: expected, none: "variants" } ) } Error::NoSuchStructField { expected, ref found, ref outer, } => { write!(f, "Unexpected field named {}", Identifier(found))?; if let Some(outer) = outer { write!(f, " in {}", Identifier(outer))?; } write!( f, ", {}", OneOf { alts: expected, none: "fields" } ) } Error::MissingStructField { field, ref outer } => { write!(f, "Unexpected missing field named {}", Identifier(field))?; match outer { Some(outer) => write!(f, " in {}", Identifier(outer)), None => Ok(()), } } Error::DuplicateStructField { field, ref outer } => { write!(f, "Unexpected duplicate field named {}", Identifier(field))?; match outer { Some(outer) => write!(f, " in {}", Identifier(outer)), None => Ok(()), } } Error::InvalidIdentifier(ref invalid) => write!(f, "Invalid identifier {:?}", invalid), Error::SuggestRawIdentifier(ref identifier) => write!( f, "Found invalid std identifier {:?}, try the raw identifier `r#{}` instead", identifier, identifier ), Error::ExpectedRawValue => f.write_str("Expected a `ron::value::RawValue`"), Error::ExceededRecursionLimit => f.write_str( "Exceeded recursion limit, try increasing `ron::Options::recursion_limit` \ and using `serde_stacker` to protect against a stack overflow", ), Error::ExpectedStructName(ref name) => write!( f, "Expected the explicit struct name {}, but none was found", Identifier(name) ), } } } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Position { pub line: usize, pub col: usize, } impl Position { pub(crate) fn from_src_end(src: &str) -> Position { let line = 1 + src.chars().filter(|&c| c == '\n').count(); let col = 1 + src.chars().rev().take_while(|&c| c != '\n').count(); Self { line, col } } } impl fmt::Display for Position { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", self.line, self.col) } } #[derive(Clone, Debug, PartialEq, Eq)] /// Spans select a range of text between two positions. /// Spans are used in [`SpannedError`] to indicate the start and end positions /// of the parser cursor before and after it encountered an error in parsing. pub struct Span { pub start: Position, pub end: Position, } impl fmt::Display for Span { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.start == self.end { write!(f, "{}", self.start) } else { write!(f, "{}-{}", self.start, self.end) } } } impl ser::Error for Error { #[cold] fn custom(msg: T) -> Self { Error::Message(msg.to_string()) } } impl de::Error for Error { #[cold] fn custom(msg: T) -> Self { Error::Message(msg.to_string()) } #[cold] fn invalid_type(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self { // Invalid type and invalid value are merged given their similarity in ron Self::invalid_value(unexp, exp) } #[cold] fn invalid_value(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self { struct UnexpectedSerdeTypeValue<'a>(de::Unexpected<'a>); impl<'a> fmt::Display for UnexpectedSerdeTypeValue<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { de::Unexpected::Bool(b) => write!(f, "the boolean `{}`", b), de::Unexpected::Unsigned(i) => write!(f, "the unsigned integer `{}`", i), de::Unexpected::Signed(i) => write!(f, "the signed integer `{}`", i), de::Unexpected::Float(n) => write!(f, "the floating point number `{}`", n), de::Unexpected::Char(c) => write!(f, "the UTF-8 character `{}`", c), de::Unexpected::Str(s) => write!(f, "the string {:?}", s), de::Unexpected::Bytes(b) => write!(f, "the byte string b\"{}\"", { b.iter() .flat_map(|c| core::ascii::escape_default(*c)) .map(char::from) .collect::() }), de::Unexpected::Unit => write!(f, "a unit value"), de::Unexpected::Option => write!(f, "an optional value"), de::Unexpected::NewtypeStruct => write!(f, "a newtype struct"), de::Unexpected::Seq => write!(f, "a sequence"), de::Unexpected::Map => write!(f, "a map"), de::Unexpected::Enum => write!(f, "an enum"), de::Unexpected::UnitVariant => write!(f, "a unit variant"), de::Unexpected::NewtypeVariant => write!(f, "a newtype variant"), de::Unexpected::TupleVariant => write!(f, "a tuple variant"), de::Unexpected::StructVariant => write!(f, "a struct variant"), de::Unexpected::Other(other) => f.write_str(other), } } } Error::InvalidValueForType { expected: exp.to_string(), found: UnexpectedSerdeTypeValue(unexp).to_string(), } } #[cold] fn invalid_length(len: usize, exp: &dyn de::Expected) -> Self { Error::ExpectedDifferentLength { expected: exp.to_string(), found: len, } } #[cold] fn unknown_variant(variant: &str, expected: &'static [&'static str]) -> Self { Error::NoSuchEnumVariant { expected, found: variant.to_string(), outer: None, } } #[cold] fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self { Error::NoSuchStructField { expected, found: field.to_string(), outer: None, } } #[cold] fn missing_field(field: &'static str) -> Self { Error::MissingStructField { field, outer: None } } #[cold] fn duplicate_field(field: &'static str) -> Self { Error::DuplicateStructField { field, outer: None } } } impl StdError for SpannedError {} impl StdError for Error {} impl From for Error { fn from(e: Utf8Error) -> Self { Error::Utf8Error(e) } } impl From for Error { fn from(_: fmt::Error) -> Self { Error::Fmt } } #[cfg(feature = "std")] impl From for Error { fn from(e: io::Error) -> Self { Error::Io(e.to_string()) } } impl From for Error { fn from(e: SpannedError) -> Self { e.code } } struct OneOf { alts: &'static [&'static str], none: &'static str, } impl fmt::Display for OneOf { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.alts { [] => write!(f, "there are no {}", self.none), [a1] => write!(f, "expected {} instead", Identifier(a1)), [a1, a2] => write!( f, "expected either {} or {} instead", Identifier(a1), Identifier(a2) ), [a1, ref alts @ .., an] => { write!(f, "expected one of {}", Identifier(a1))?; for alt in alts { write!(f, ", {}", Identifier(alt))?; } write!(f, ", or {} instead", Identifier(an)) } } } } struct Identifier<'a>(&'a str); impl<'a> fmt::Display for Identifier<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.0.is_empty() || !self.0.chars().all(is_ident_raw_char) { return write!(f, "{:?}_[invalid identifier]", self.0); } let mut chars = self.0.chars(); if !chars.next().map_or(false, is_ident_first_char) || !chars.all(is_xid_continue) { write!(f, "`r#{}`", self.0) } else { write!(f, "`{}`", self.0) } } } #[cfg(test)] mod tests { use alloc::{format, string::String}; use serde::{de::Error as DeError, de::Unexpected, ser::Error as SerError}; use super::{Error, Position, Span, SpannedError}; #[test] fn error_messages() { check_error_message(&Error::from(core::fmt::Error), "Formatting RON failed"); #[cfg(feature = "std")] check_error_message( &Error::from(std::io::Error::new( std::io::ErrorKind::InvalidData, "my-error", )), "my-error", ); check_error_message(&::custom("my-ser-error"), "my-ser-error"); check_error_message(&::custom("my-de-error"), "my-de-error"); check_error_message(&Error::Eof, "Unexpected end of RON"); check_error_message(&Error::ExpectedArray, "Expected opening `[`"); check_error_message(&Error::ExpectedArrayEnd, "Expected closing `]`"); check_error_message( &Error::ExpectedAttribute, "Expected an `#![enable(...)]` attribute", ); check_error_message( &Error::ExpectedAttributeEnd, "Expected closing `)]` after the enable attribute", ); check_error_message(&Error::ExpectedBoolean, "Expected boolean"); check_error_message(&Error::ExpectedComma, "Expected comma"); check_error_message(&Error::ExpectedChar, "Expected char"); check_error_message(&Error::ExpectedByteLiteral, "Expected byte literal"); check_error_message(&Error::ExpectedFloat, "Expected float"); check_error_message(&Error::FloatUnderscore, "Unexpected underscore in float"); check_error_message(&Error::ExpectedInteger, "Expected integer"); check_error_message(&Error::ExpectedOption, "Expected option"); check_error_message(&Error::ExpectedOptionEnd, "Expected closing `)`"); check_error_message(&Error::ExpectedStructLikeEnd, "Expected closing `)`"); check_error_message(&Error::ExpectedMap, "Expected opening `{`"); check_error_message(&Error::ExpectedMapColon, "Expected colon"); check_error_message(&Error::ExpectedMapEnd, "Expected closing `}`"); check_error_message( &Error::ExpectedDifferentStructName { expected: "raw+identifier", found: String::from("identifier"), }, "Expected struct `r#raw+identifier` but found `identifier`", ); check_error_message(&Error::ExpectedStructLike, "Expected opening `(`"); check_error_message( &Error::ExpectedNamedStructLike(""), "Expected only opening `(`, no name, for un-nameable struct", ); check_error_message( &Error::ExpectedNamedStructLike("_ident"), "Expected opening `(` for struct `_ident`", ); check_error_message(&Error::ExpectedUnit, "Expected unit"); check_error_message(&Error::ExpectedString, "Expected string"); check_error_message(&Error::ExpectedByteString, "Expected byte string"); check_error_message(&Error::ExpectedStringEnd, "Expected end of string"); check_error_message(&Error::ExpectedIdentifier, "Expected identifier"); check_error_message(&Error::InvalidEscape("Invalid escape"), "Invalid escape"); check_error_message(&Error::IntegerOutOfBounds, "Integer is out of bounds"); check_error_message( &Error::InvalidIntegerDigit { digit: 'q', base: 16, }, "Invalid digit 'q' for base 16 integers", ); check_error_message( &Error::NoSuchExtension(String::from("unknown")), "No RON extension named `unknown`", ); check_error_message(&Error::UnclosedBlockComment, "Unclosed block comment"); check_error_message( &Error::UnclosedLineComment, "`ron::value::RawValue` cannot end in unclosed line comment, \ try using a block comment or adding a newline", ); check_error_message( &Error::UnderscoreAtBeginning, "Unexpected leading underscore in a number", ); check_error_message(&Error::UnexpectedChar('🦀'), "Unexpected char \'🦀\'"); #[allow(invalid_from_utf8)] check_error_message( &Error::Utf8Error(core::str::from_utf8(b"error: \xff\xff\xff\xff").unwrap_err()), "invalid utf-8 sequence of 1 bytes from index 7", ); check_error_message( &Error::TrailingCharacters, "Non-whitespace trailing characters", ); check_error_message( &Error::invalid_value(Unexpected::Enum, &"struct `Hi`"), "Expected struct `Hi` but found an enum instead", ); check_error_message( &Error::invalid_length(0, &"two bees"), "Expected two bees but found zero elements instead", ); check_error_message( &Error::invalid_length(1, &"two bees"), "Expected two bees but found one element instead", ); check_error_message( &Error::invalid_length(3, &"two bees"), "Expected two bees but found 3 elements instead", ); check_error_message( &Error::unknown_variant("unknown", &[]), "Unexpected enum variant named `unknown`, there are no variants", ); check_error_message( &Error::NoSuchEnumVariant { expected: &["A", "B+C"], found: String::from("D"), outer: Some(String::from("E")), }, "Unexpected variant named `D` in enum `E`, \ expected either `A` or `r#B+C` instead", ); check_error_message( &Error::unknown_field("unknown", &[]), "Unexpected field named `unknown`, there are no fields", ); check_error_message( &Error::NoSuchStructField { expected: &["a"], found: String::from("b"), outer: Some(String::from("S")), }, "Unexpected field named `b` in `S`, expected `a` instead", ); check_error_message( &Error::NoSuchStructField { expected: &["a", "b+c", "d"], found: String::from("e"), outer: Some(String::from("S")), }, "Unexpected field named `e` in `S`, \ expected one of `a`, `r#b+c`, or `d` instead", ); check_error_message( &Error::missing_field("a"), "Unexpected missing field named `a`", ); check_error_message( &Error::MissingStructField { field: "", outer: Some(String::from("S+T")), }, "Unexpected missing field named \"\"_[invalid identifier] in `r#S+T`", ); check_error_message( &Error::duplicate_field("a"), "Unexpected duplicate field named `a`", ); check_error_message( &Error::DuplicateStructField { field: "b+c", outer: Some(String::from("S+T")), }, "Unexpected duplicate field named `r#b+c` in `r#S+T`", ); check_error_message( &Error::InvalidIdentifier(String::from("why+🦀+not")), "Invalid identifier \"why+🦀+not\"", ); check_error_message( &Error::SuggestRawIdentifier(String::from("raw+ident")), "Found invalid std identifier \"raw+ident\", \ try the raw identifier `r#raw+ident` instead", ); check_error_message( &Error::ExpectedRawValue, "Expected a `ron::value::RawValue`", ); check_error_message( &Error::ExceededRecursionLimit, "Exceeded recursion limit, try increasing `ron::Options::recursion_limit` \ and using `serde_stacker` to protect against a stack overflow", ); check_error_message( &Error::ExpectedStructName(String::from("Struct")), "Expected the explicit struct name `Struct`, but none was found", ); } fn check_error_message(err: &T, msg: &str) { assert_eq!(format!("{}", err), msg); } #[test] fn spanned_error_into_code() { assert_eq!( Error::from(SpannedError { code: Error::Eof, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 5 }, } }), Error::Eof ); assert_eq!( Error::from(SpannedError { code: Error::ExpectedRawValue, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 5 }, } }), Error::ExpectedRawValue ); } } ron-0.12.0/src/extensions.rs000064400000000000000000000034231046102023000140400ustar 00000000000000use serde_derive::{Deserialize, Serialize}; // GRCOV_EXCL_START bitflags::bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct Extensions: usize { const UNWRAP_NEWTYPES = 0x1; const IMPLICIT_SOME = 0x2; const UNWRAP_VARIANT_NEWTYPES = 0x4; /// During serialization, this extension emits struct names. See also [`PrettyConfig::struct_names`](crate::ser::PrettyConfig::struct_names) for the [`PrettyConfig`](crate::ser::PrettyConfig) equivalent. /// /// During deserialization, this extension requires that structs' names are stated explicitly. const EXPLICIT_STRUCT_NAMES = 0x8; } } // GRCOV_EXCL_STOP impl Extensions { /// Creates an extension flag from an ident. #[must_use] pub fn from_ident(ident: &str) -> Option { for (name, extension) in Extensions::all().iter_names() { if ident == name.to_lowercase() { return Some(extension); } } None } } // GRCOV_EXCL_START impl Default for Extensions { fn default() -> Self { Extensions::empty() } } // GRCOV_EXCL_STOP #[cfg(test)] mod tests { use super::Extensions; fn roundtrip_extensions(ext: Extensions) { let ron = crate::to_string(&ext).unwrap(); let ext2: Extensions = crate::from_str(&ron).unwrap(); assert_eq!(ext, ext2); } #[test] fn test_extension_serde() { // iterate over the powerset of all extensions (i.e. every possible combination of extensions) for bits in Extensions::empty().bits()..=Extensions::all().bits() { let extensions = Extensions::from_bits_retain(bits); roundtrip_extensions(extensions); } } } ron-0.12.0/src/lib.rs000064400000000000000000000017751046102023000124170ustar 00000000000000#![deny(clippy::correctness)] #![deny(clippy::suspicious)] #![deny(clippy::complexity)] #![deny(clippy::perf)] #![deny(clippy::style)] #![warn(clippy::pedantic)] #![cfg_attr(not(test), deny(clippy::unwrap_used))] #![cfg_attr(not(test), deny(clippy::expect_used))] #![deny(clippy::panic)] #![warn(clippy::todo)] #![deny(clippy::unimplemented)] #![deny(clippy::unreachable)] #![deny(unsafe_code)] #![allow(clippy::missing_errors_doc)] // FIXME #![warn(clippy::alloc_instead_of_core)] #![warn(clippy::std_instead_of_alloc)] #![warn(clippy::std_instead_of_core)] #![doc = include_str!("../README.md")] #![doc(html_root_url = "https://docs.rs/ron/0.12.0")] #![no_std] #[cfg(feature = "std")] extern crate std; extern crate alloc; pub mod de; pub mod ser; pub mod error; pub mod value; pub mod extensions; pub mod options; pub mod util; pub use de::{from_str, Deserializer}; pub use error::{Error, Result}; pub use options::Options; pub use ser::{to_string, Serializer}; pub use value::{Map, Number, Value}; mod parse; ron-0.12.0/src/options.rs000064400000000000000000000232161046102023000133360ustar 00000000000000//! Roundtrip serde Options module. use alloc::string::String; use core::fmt; use serde::{de, ser}; use serde_derive::{Deserialize, Serialize}; use crate::{ de::Deserializer, error::{Result, SpannedResult}, extensions::Extensions, ser::{PrettyConfig, Serializer}, }; #[cfg(feature = "std")] use { crate::error::{Position, Span, SpannedError}, alloc::vec::Vec, std::io, }; /// Roundtrip serde options. /// /// # Examples /// /// ``` /// use ron::{Options, extensions::Extensions}; /// /// let ron = Options::default() /// .with_default_extension(Extensions::IMPLICIT_SOME); /// /// let de: Option = ron.from_str("42").unwrap(); /// let ser = ron.to_string(&de).unwrap(); /// /// assert_eq!(ser, "42"); /// ``` #[derive(Clone, Debug, Serialize, Deserialize)] // GRCOV_EXCL_LINE #[serde(default)] #[non_exhaustive] pub struct Options { /// Extensions that are enabled by default during serialization and /// deserialization. /// During serialization, these extensions do NOT have to be explicitly /// enabled in the parsed RON. /// During deserialization, these extensions are used, but their explicit /// activation is NOT included in the output RON. /// No extensions are enabled by default. pub default_extensions: Extensions, /// Default recursion limit that is checked during serialization and /// deserialization. /// If set to `None`, infinite recursion is allowed and stack overflow /// errors can crash the serialization or deserialization process. /// Defaults to `Some(128)`, i.e. 128 recursive calls are allowed. pub recursion_limit: Option, } impl Default for Options { fn default() -> Self { Self { default_extensions: Extensions::empty(), recursion_limit: Some(128), } } } impl Options { #[must_use] /// Enable `default_extension` by default during serialization and deserialization. pub fn with_default_extension(mut self, default_extension: Extensions) -> Self { self.default_extensions |= default_extension; self } #[must_use] /// Do NOT enable `default_extension` by default during serialization and deserialization. pub fn without_default_extension(mut self, default_extension: Extensions) -> Self { self.default_extensions &= !default_extension; self } #[must_use] /// Set a maximum recursion limit during serialization and deserialization. pub fn with_recursion_limit(mut self, recursion_limit: usize) -> Self { self.recursion_limit = Some(recursion_limit); self } #[must_use] /// Disable the recursion limit during serialization and deserialization. /// /// If you expect to handle highly recursive datastructures, consider wrapping /// `ron` with [`serde_stacker`](https://docs.rs/serde_stacker/latest/serde_stacker/). pub fn without_recursion_limit(mut self) -> Self { self.recursion_limit = None; self } } impl Options { /// A convenience function for building a deserializer /// and deserializing a value of type `T` from a reader. #[cfg(feature = "std")] pub fn from_reader(&self, rdr: R) -> SpannedResult where R: io::Read, T: de::DeserializeOwned, { self.from_reader_seed(rdr, core::marker::PhantomData) } /// A convenience function for building a deserializer /// and deserializing a value of type `T` from a string. pub fn from_str<'a, T>(&self, s: &'a str) -> SpannedResult where T: de::Deserialize<'a>, { self.from_str_seed(s, core::marker::PhantomData) } /// A convenience function for building a deserializer /// and deserializing a value of type `T` from bytes. pub fn from_bytes<'a, T>(&self, s: &'a [u8]) -> SpannedResult where T: de::Deserialize<'a>, { self.from_bytes_seed(s, core::marker::PhantomData) } /// A convenience function for building a deserializer /// and deserializing a value of type `T` from a reader /// and a seed. // FIXME: panic is not actually possible, remove once utf8_chunks is stabilized #[allow(clippy::missing_panics_doc)] #[cfg(feature = "std")] pub fn from_reader_seed(&self, mut rdr: R, seed: S) -> SpannedResult where R: io::Read, S: for<'a> de::DeserializeSeed<'a, Value = T>, { let mut bytes = Vec::new(); let io_err = if let Err(err) = rdr.read_to_end(&mut bytes) { err } else { return self.from_bytes_seed(&bytes, seed); }; // Try to compute a good error position for the I/O error // FIXME: use [`utf8_chunks`](https://github.com/rust-lang/rust/issues/99543) once stabilised #[allow(clippy::expect_used)] let valid_input = match core::str::from_utf8(&bytes) { Ok(valid_input) => valid_input, Err(err) => core::str::from_utf8(&bytes[..err.valid_up_to()]) .expect("source is valid up to error"), }; Err(SpannedError { code: io_err.into(), span: Span { start: Position { line: 1, col: 1 }, end: Position::from_src_end(valid_input), }, }) } /// A convenience function for building a deserializer /// and deserializing a value of type `T` from a string /// and a seed. pub fn from_str_seed<'a, S, T>(&self, s: &'a str, seed: S) -> SpannedResult where S: de::DeserializeSeed<'a, Value = T>, { let mut deserializer = Deserializer::from_str_with_options(s, self)?; let value = seed .deserialize(&mut deserializer) .map_err(|e| deserializer.span_error(e))?; deserializer.end().map_err(|e| deserializer.span_error(e))?; Ok(value) } /// A convenience function for building a deserializer /// and deserializing a value of type `T` from bytes /// and a seed. pub fn from_bytes_seed<'a, S, T>(&self, s: &'a [u8], seed: S) -> SpannedResult where S: de::DeserializeSeed<'a, Value = T>, { let mut deserializer = Deserializer::from_bytes_with_options(s, self)?; let value = seed .deserialize(&mut deserializer) .map_err(|e| deserializer.span_error(e))?; deserializer.end().map_err(|e| deserializer.span_error(e))?; Ok(value) } /// Serializes `value` into `writer`. /// /// This function does not generate any newlines or nice formatting; /// if you want that, you can use /// [`to_writer_pretty`][Self::to_writer_pretty] instead. pub fn to_writer(&self, writer: W, value: &T) -> Result<()> where W: fmt::Write, T: ?Sized + ser::Serialize, { let mut s = Serializer::with_options(writer, None, self)?; value.serialize(&mut s) } /// Serializes `value` into `writer` in a pretty way. pub fn to_writer_pretty(&self, writer: W, value: &T, config: PrettyConfig) -> Result<()> where W: fmt::Write, T: ?Sized + ser::Serialize, { let mut s = Serializer::with_options(writer, Some(config), self)?; value.serialize(&mut s) } /// Serializes `value` into `writer`. /// /// This function does not generate any newlines or nice formatting; /// if you want that, you can use /// [`to_io_writer_pretty`][Self::to_io_writer_pretty] instead. #[cfg(feature = "std")] pub fn to_io_writer(&self, writer: W, value: &T) -> Result<()> where W: io::Write, T: ?Sized + ser::Serialize, { let mut adapter = Adapter { writer, error: Ok(()), }; let result = self.to_writer(&mut adapter, value); adapter.error?; result } /// Serializes `value` into `writer` in a pretty way. #[cfg(feature = "std")] pub fn to_io_writer_pretty( &self, writer: W, value: &T, config: PrettyConfig, ) -> Result<()> where W: io::Write, T: ?Sized + ser::Serialize, { let mut adapter = Adapter { writer, error: Ok(()), }; let result = self.to_writer_pretty(&mut adapter, value, config); adapter.error?; result } /// Serializes `value` and returns it as string. /// /// This function does not generate any newlines or nice formatting; /// if you want that, you can use /// [`to_string_pretty`][Self::to_string_pretty] instead. pub fn to_string(&self, value: &T) -> Result where T: ?Sized + ser::Serialize, { let mut output = String::new(); let mut s = Serializer::with_options(&mut output, None, self)?; value.serialize(&mut s)?; Ok(output) } /// Serializes `value` in the recommended RON layout in a pretty way. pub fn to_string_pretty(&self, value: &T, config: PrettyConfig) -> Result where T: ?Sized + ser::Serialize, { let mut output = String::new(); let mut s = Serializer::with_options(&mut output, Some(config), self)?; value.serialize(&mut s)?; Ok(output) } } // Adapter from io::Write to fmt::Write that keeps the error #[cfg(feature = "std")] struct Adapter { writer: W, error: io::Result<()>, } #[cfg(feature = "std")] impl fmt::Write for Adapter { fn write_str(&mut self, s: &str) -> fmt::Result { match self.writer.write_all(s.as_bytes()) { Ok(()) => Ok(()), Err(e) => { self.error = Err(e); Err(fmt::Error) } } } } ron-0.12.0/src/parse.rs000064400000000000000000001707331046102023000127640ustar 00000000000000#![allow(clippy::identity_op)] use alloc::{ format, string::{String, ToString}, vec::Vec, }; use core::{ char::from_u32 as char_from_u32, str::{self, from_utf8, FromStr, Utf8Error}, }; use unicode_ident::{is_xid_continue, is_xid_start}; use crate::{ error::{Error, Position, Result, Span, SpannedError, SpannedResult}, extensions::Extensions, value::Number, }; const fn is_int_char(c: char) -> bool { c.is_ascii_hexdigit() || c == '_' } const fn is_float_char(c: char) -> bool { c.is_ascii_digit() || matches!(c, 'e' | 'E' | '.' | '+' | '-' | '_') } pub fn is_ident_first_char(c: char) -> bool { c == '_' || is_xid_start(c) } pub fn is_ident_raw_char(c: char) -> bool { matches!(c, '.' | '+' | '-') | is_xid_continue(c) } pub const fn is_whitespace_char(c: char) -> bool { matches!( c, ' ' | '\t' | '\n' | '\r' | '\x0B' | '\x0C' | '\u{85}' | '\u{200E}' | '\u{200F}' | '\u{2028}' | '\u{2029}' ) } #[cfg(feature = "integer128")] pub(crate) type LargeUInt = u128; #[cfg(not(feature = "integer128"))] pub(crate) type LargeUInt = u64; #[cfg(feature = "integer128")] pub(crate) type LargeSInt = i128; #[cfg(not(feature = "integer128"))] pub(crate) type LargeSInt = i64; pub struct Parser<'a> { /// Bits set according to the [`Extensions`] enum. pub exts: Extensions, src: &'a str, cursor: ParserCursor, prev_cursor: ParserCursor, } #[derive(Copy, Clone)] // GRCOV_EXCL_LINE pub struct ParserCursor { cursor: usize, pre_ws_cursor: usize, last_ws_len: usize, } const WS_CURSOR_UNCLOSED_LINE: usize = usize::MAX; impl PartialEq for ParserCursor { fn eq(&self, other: &Self) -> bool { self.cursor == other.cursor } } impl PartialOrd for ParserCursor { fn partial_cmp(&self, other: &Self) -> Option { self.cursor.partial_cmp(&other.cursor) } } /// constructor and parsing utilities impl<'a> Parser<'a> { pub fn new(src: &'a str) -> SpannedResult { let mut parser = Parser { exts: Extensions::empty(), src, cursor: ParserCursor { cursor: 0, pre_ws_cursor: 0, last_ws_len: 0, }, prev_cursor: ParserCursor { cursor: 0, pre_ws_cursor: 0, last_ws_len: 0, }, }; parser.skip_ws().map_err(|e| parser.span_error(e))?; // Loop over all extensions attributes loop { let attribute = parser.extensions().map_err(|e| parser.span_error(e))?; if attribute.is_empty() { break; } parser.exts |= attribute; parser.skip_ws().map_err(|e| parser.span_error(e))?; } Ok(parser) } fn set_cursor(&mut self, cursor: ParserCursor) { self.cursor = cursor; } pub fn span_error(&self, code: Error) -> SpannedError { SpannedError { code, span: Span { start: Position::from_src_end(&self.src[..self.prev_cursor.cursor]), end: Position::from_src_end(&self.src[..self.cursor.cursor]), }, } } pub fn advance_bytes(&mut self, bytes: usize) { self.prev_cursor = self.cursor; self.cursor.cursor += bytes; } pub fn next_char(&mut self) -> Result { let c = self.peek_char_or_eof()?; self.cursor.cursor += c.len_utf8(); Ok(c) } pub fn skip_next_char(&mut self) { core::mem::drop(self.next_char()); } pub fn peek_char(&self) -> Option { self.src().chars().next() } pub fn peek_char_or_eof(&self) -> Result { self.peek_char().ok_or(Error::Eof) } pub fn check_char(&self, c: char) -> bool { self.src().starts_with(c) } pub fn check_str(&self, s: &str) -> bool { self.src().starts_with(s) } pub fn src(&self) -> &'a str { &self.src[self.cursor.cursor..] } pub fn pre_ws_src(&self) -> &'a str { &self.src[self.cursor.pre_ws_cursor..] } pub fn consume_str(&mut self, s: &str) -> bool { if self.check_str(s) { self.advance_bytes(s.len()); true } else { false } } pub fn consume_char(&mut self, c: char) -> bool { if self.check_char(c) { self.advance_bytes(c.len_utf8()); true } else { false } } fn consume_all(&mut self, all: &[&str]) -> Result { all.iter() .map(|elem| { if self.consume_str(elem) { self.skip_ws()?; Ok(true) } else { Ok(false) } }) .try_fold(true, |acc, x| x.map(|x| x && acc)) } pub fn expect_char(&mut self, expected: char, error: Error) -> Result<()> { if self.consume_char(expected) { Ok(()) } else { Err(error) } } #[must_use] pub fn next_chars_while_len(&self, condition: fn(char) -> bool) -> usize { self.next_chars_while_from_len(0, condition) } #[must_use] pub fn next_chars_while_from_len(&self, from: usize, condition: fn(char) -> bool) -> usize { self.src()[from..] .find(|c| !condition(c)) .unwrap_or(self.src().len() - from) } } /// actual parsing of ron tokens impl<'a> Parser<'a> { fn parse_integer_digits( &mut self, s: &str, base: u8, f: fn(&mut T, u8) -> bool, ) -> Result { let mut num_acc = T::from_u8(0); for (i, c) in s.char_indices() { if c == '_' { continue; } if num_acc.checked_mul_ext(base) { self.advance_bytes(s.len()); return Err(Error::IntegerOutOfBounds); } let digit = Self::decode_hex(c)?; if digit >= base { self.advance_bytes(i); return Err(Error::InvalidIntegerDigit { digit: c, base }); } if f(&mut num_acc, digit) { self.advance_bytes(s.len()); return Err(Error::IntegerOutOfBounds); } } self.advance_bytes(s.len()); Ok(num_acc) } fn parse_integer(&mut self, sign: i8) -> Result { let base = match () { () if self.consume_str("0b") => 2, () if self.consume_str("0o") => 8, () if self.consume_str("0x") => 16, () => 10, }; let num_bytes = self.next_chars_while_len(is_int_char); if num_bytes == 0 { return Err(Error::ExpectedInteger); } if self.check_char('_') { return Err(Error::UnderscoreAtBeginning); } let s = &self.src()[..num_bytes]; if sign > 0 { self.parse_integer_digits(s, base, T::checked_add_ext) } else { self.parse_integer_digits(s, base, T::checked_sub_ext) } } #[allow(clippy::too_many_lines)] pub fn integer(&mut self) -> Result { let src_backup = self.src(); let is_negative = match self.peek_char_or_eof()? { '+' => { self.skip_next_char(); false } '-' => { self.skip_next_char(); true } 'b' if self.consume_str("b'") => { // Parse a byte literal let byte = match self.next_char()? { '\\' => match self.parse_escape(EscapeEncoding::Binary, true)? { // we know that this byte is an ASCII character EscapeCharacter::Ascii(b) => b, EscapeCharacter::Utf8(_) => { return Err(Error::InvalidEscape( "Unexpected Unicode escape in byte literal", )) } }, b if b.is_ascii() => b as u8, _ => return Err(Error::ExpectedByteLiteral), }; if !self.consume_char('\'') { return Err(Error::ExpectedByteLiteral); } let bytes_ron = &src_backup[..src_backup.len() - self.src().len()]; return T::try_from_parsed_integer(ParsedInteger::U8(byte), bytes_ron); } _ => false, }; let sign = if is_negative { -1 } else { 1 }; let num_bytes = self.next_chars_while_len(is_int_char); if self.src()[num_bytes..].starts_with(['i', 'u']) { let int_cursor = self.cursor; self.advance_bytes(num_bytes); #[allow(clippy::never_loop)] loop { let (res, suffix_bytes) = if self.consume_ident("i8") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( self.parse_integer::(sign).map(ParsedInteger::I8), suffix_bytes, ) } else if self.consume_ident("i16") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( self.parse_integer::(sign).map(ParsedInteger::I16), suffix_bytes, ) } else if self.consume_ident("i32") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( self.parse_integer::(sign).map(ParsedInteger::I32), suffix_bytes, ) } else if self.consume_ident("i64") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( self.parse_integer::(sign).map(ParsedInteger::I64), suffix_bytes, ) } else if self.consume_ident("u8") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( self.parse_integer::(sign).map(ParsedInteger::U8), suffix_bytes, ) } else if self.consume_ident("u16") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( self.parse_integer::(sign).map(ParsedInteger::U16), suffix_bytes, ) } else if self.consume_ident("u32") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( self.parse_integer::(sign).map(ParsedInteger::U32), suffix_bytes, ) } else if self.consume_ident("u64") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( self.parse_integer::(sign).map(ParsedInteger::U64), suffix_bytes, ) } else { #[cfg(feature = "integer128")] if self.consume_ident("i128") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( self.parse_integer::(sign).map(ParsedInteger::I128), suffix_bytes, ) } else if self.consume_ident("u128") { let suffix_bytes = self.src(); self.set_cursor(int_cursor); ( self.parse_integer::(sign).map(ParsedInteger::U128), suffix_bytes, ) } else { break; } #[cfg(not(feature = "integer128"))] { break; } }; if !matches!( &res, Err(Error::UnderscoreAtBeginning | Error::InvalidIntegerDigit { .. }) ) { // Advance past the number suffix self.skip_identifier(); } let integer_ron = &src_backup[..src_backup.len() - suffix_bytes.len()]; return res.and_then(|parsed| T::try_from_parsed_integer(parsed, integer_ron)); } self.set_cursor(int_cursor); } T::parse(self, sign) } pub fn any_number(&mut self) -> Result { if self.next_bytes_is_float() { return match self.float::()? { ParsedFloat::F32(v) => Ok(Number::F32(v.into())), ParsedFloat::F64(v) => Ok(Number::F64(v.into())), }; } let backup_cursor = self.cursor; let (integer_err, integer_cursor) = match self.integer::() { Ok(integer) => { return match integer { ParsedInteger::I8(v) => Ok(Number::I8(v)), ParsedInteger::I16(v) => Ok(Number::I16(v)), ParsedInteger::I32(v) => Ok(Number::I32(v)), ParsedInteger::I64(v) => Ok(Number::I64(v)), #[cfg(feature = "integer128")] ParsedInteger::I128(v) => Ok(Number::I128(v)), ParsedInteger::U8(v) => Ok(Number::U8(v)), ParsedInteger::U16(v) => Ok(Number::U16(v)), ParsedInteger::U32(v) => Ok(Number::U32(v)), ParsedInteger::U64(v) => Ok(Number::U64(v)), #[cfg(feature = "integer128")] ParsedInteger::U128(v) => Ok(Number::U128(v)), } } Err(err) => (err, self.cursor), }; self.set_cursor(backup_cursor); // Fall-back to parse an out-of-range integer as a float match self.float::() { Ok(ParsedFloat::F32(v)) if self.cursor >= integer_cursor => Ok(Number::F32(v.into())), Ok(ParsedFloat::F64(v)) if self.cursor >= integer_cursor => Ok(Number::F64(v.into())), _ => { // Return the more precise integer error self.set_cursor(integer_cursor); Err(integer_err) } } } pub fn bool(&mut self) -> Result { if self.consume_ident("true") { Ok(true) } else if self.consume_ident("false") { Ok(false) } else { Err(Error::ExpectedBoolean) } } pub fn char(&mut self) -> Result { self.expect_char('\'', Error::ExpectedChar)?; let c = self.next_char()?; let c = if c == '\\' { match self.parse_escape(EscapeEncoding::Utf8, true)? { // we know that this byte is an ASCII character EscapeCharacter::Ascii(b) => char::from(b), EscapeCharacter::Utf8(c) => c, } } else { c }; self.expect_char('\'', Error::ExpectedChar)?; Ok(c) } pub fn comma(&mut self) -> Result { self.skip_ws()?; if self.consume_char(',') { self.skip_ws()?; Ok(true) } else { Ok(false) } } /// Only returns true if the char after `ident` cannot belong /// to an identifier. pub fn check_ident(&mut self, ident: &str) -> bool { self.check_str(ident) && !self.check_ident_other_char(ident.len()) } fn check_ident_other_char(&self, index: usize) -> bool { self.src()[index..] .chars() .next() .map_or(false, is_xid_continue) } /// Check which type of struct we are currently parsing. The parsing state /// is only changed in case of an error, to provide a better position. /// /// [`NewtypeMode::NoParensMeanUnit`] detects (tuple) structs by a leading /// opening bracket and reports a unit struct otherwise. /// [`NewtypeMode::InsideNewtype`] skips an initial check for unit structs, /// and means that any leading opening bracket is not considered to open /// a (tuple) struct but to be part of the structs inner contents. /// /// [`TupleMode::ImpreciseTupleOrNewtype`] only performs a cheap, O(1), /// single-identifier lookahead check to distinguish tuple structs from /// non-tuple structs. /// [`TupleMode::DifferentiateNewtype`] performs an expensive, O(N), look- /// ahead over the entire next value tree, which can span the entirety of /// the remaining document in the worst case. pub fn check_struct_type( &mut self, newtype: NewtypeMode, tuple: TupleMode, ) -> Result { fn check_struct_type_inner( parser: &mut Parser, newtype: NewtypeMode, tuple: TupleMode, ) -> Result { if matches!(newtype, NewtypeMode::NoParensMeanUnit) && !parser.consume_char('(') { return Ok(StructType::Unit); } parser.skip_ws()?; // Check for `Ident()`, which could be // - a zero-field struct or tuple (variant) // - an unwrapped newtype around a unit if matches!(newtype, NewtypeMode::NoParensMeanUnit) && parser.check_char(')') { return Ok(StructType::EmptyTuple); } if parser.skip_identifier().is_some() { parser.skip_ws()?; match parser.peek_char() { // Definitely a struct with named fields Some(':') => return Ok(StructType::Named), // Definitely a tuple-like struct with fields Some(',') => { parser.skip_next_char(); parser.skip_ws()?; if parser.check_char(')') { // A one-element tuple could be a newtype return Ok(StructType::NewtypeTuple); } // Definitely a tuple struct with more than one field return Ok(StructType::NonNewtypeTuple); } // Either a newtype or a tuple struct Some(')') => return Ok(StructType::NewtypeTuple), // Something else, let's investigate further Some(_) | None => (), }; } if matches!(tuple, TupleMode::ImpreciseTupleOrNewtype) { return Ok(StructType::AnyTuple); } let mut braces = 1_usize; let mut more_than_one = false; // Skip ahead to see if the value is followed by another value while braces > 0 { // Skip spurious braces in comments, strings, and characters parser.skip_ws()?; let cursor_backup = parser.cursor; if parser.char().is_err() { parser.set_cursor(cursor_backup); } let cursor_backup = parser.cursor; match parser.string() { Ok(_) => (), // prevent quadratic complexity backtracking for unterminated string Err(err @ (Error::ExpectedStringEnd | Error::Eof)) => return Err(err), Err(_) => parser.set_cursor(cursor_backup), } let cursor_backup = parser.cursor; // we have already checked for strings, which subsume base64 byte strings match parser.byte_string_no_base64() { Ok(_) => (), // prevent quadratic complexity backtracking for unterminated byte string Err(err @ (Error::ExpectedStringEnd | Error::Eof)) => return Err(err), Err(_) => parser.set_cursor(cursor_backup), } let c = parser.next_char()?; if matches!(c, '(' | '[' | '{') { braces += 1; } else if matches!(c, ')' | ']' | '}') { braces -= 1; } else if c == ',' && braces == 1 { parser.skip_ws()?; more_than_one = !parser.check_char(')'); break; } } if more_than_one { Ok(StructType::NonNewtypeTuple) } else { Ok(StructType::NewtypeTuple) } } // Create a temporary working copy let backup_cursor = self.cursor; let result = check_struct_type_inner(self, newtype, tuple); if result.is_ok() { // Revert the parser to before the struct type check self.set_cursor(backup_cursor); } result } /// Only returns true if the char after `ident` cannot belong /// to an identifier. pub fn consume_ident(&mut self, ident: &str) -> bool { if self.check_ident(ident) { self.advance_bytes(ident.len()); true } else { false } } pub fn consume_struct_name(&mut self, ident: &'static str) -> Result { if self.check_ident("") { if self.exts.contains(Extensions::EXPLICIT_STRUCT_NAMES) { return Err(Error::ExpectedStructName(ident.to_string())); } return Ok(false); } let found_ident = match self.identifier() { Ok(maybe_ident) => maybe_ident, Err(Error::SuggestRawIdentifier(found_ident)) if found_ident == ident => { return Err(Error::SuggestRawIdentifier(found_ident)) } Err(_) => return Err(Error::ExpectedNamedStructLike(ident)), }; if ident.is_empty() { return Err(Error::ExpectedNamedStructLike(ident)); } if found_ident != ident { return Err(Error::ExpectedDifferentStructName { expected: ident, found: String::from(found_ident), }); } Ok(true) } /// Returns the extensions bit mask. fn extensions(&mut self) -> Result { if !self.check_char('#') { return Ok(Extensions::empty()); } if !self.consume_all(&["#", "!", "[", "enable", "("])? { return Err(Error::ExpectedAttribute); } self.skip_ws()?; let mut extensions = Extensions::empty(); loop { let ident = self.identifier()?; let extension = Extensions::from_ident(ident) .ok_or_else(|| Error::NoSuchExtension(ident.into()))?; extensions |= extension; let comma = self.comma()?; // If we have no comma but another item, return an error if !comma && self.check_ident_other_char(0) { return Err(Error::ExpectedComma); } // If there's no comma, assume the list ended. // If there is, it might be a trailing one, thus we only // continue the loop if we get an ident char. if !comma || !self.check_ident_other_char(0) { break; } } self.skip_ws()?; if self.consume_all(&[")", "]"])? { Ok(extensions) } else { Err(Error::ExpectedAttributeEnd) } } pub fn float(&mut self) -> Result { const F32_SUFFIX: &str = "f32"; const F64_SUFFIX: &str = "f64"; for (literal, value_f32, value_f64) in &[ ("inf", f32::INFINITY, f64::INFINITY), ("+inf", f32::INFINITY, f64::INFINITY), ("-inf", f32::NEG_INFINITY, f64::NEG_INFINITY), ("NaN", f32::NAN, f64::NAN), ("+NaN", f32::NAN, f64::NAN), ("-NaN", -f32::NAN, -f64::NAN), ] { if self.consume_ident(literal) { return T::parse(literal); } if let Some(suffix) = self.src().strip_prefix(literal) { if let Some(post_suffix) = suffix.strip_prefix(F32_SUFFIX) { if !post_suffix.chars().next().map_or(false, is_xid_continue) { let float_ron = &self.src()[..literal.len() + F32_SUFFIX.len()]; self.advance_bytes(literal.len() + F32_SUFFIX.len()); return T::try_from_parsed_float(ParsedFloat::F32(*value_f32), float_ron); } } if let Some(post_suffix) = suffix.strip_prefix(F64_SUFFIX) { if !post_suffix.chars().next().map_or(false, is_xid_continue) { let float_ron = &self.src()[..literal.len() + F64_SUFFIX.len()]; self.advance_bytes(literal.len() + F64_SUFFIX.len()); return T::try_from_parsed_float(ParsedFloat::F64(*value_f64), float_ron); } } } } let num_bytes = self.next_chars_while_len(is_float_char); if num_bytes == 0 { return Err(Error::ExpectedFloat); } if self.check_char('_') { return Err(Error::UnderscoreAtBeginning); } let mut f = String::with_capacity(num_bytes); let mut allow_underscore = false; for (i, c) in self.src()[..num_bytes].char_indices() { match c { '_' if allow_underscore => continue, '_' => { self.advance_bytes(i); return Err(Error::FloatUnderscore); } '0'..='9' | 'e' | 'E' => allow_underscore = true, '.' => allow_underscore = false, _ => (), } // we know that the byte is an ASCII character here f.push(c); } if self.src()[num_bytes..].starts_with('f') { let backup_cursor = self.cursor; self.advance_bytes(num_bytes); #[allow(clippy::never_loop)] loop { let res = if self.consume_ident(F32_SUFFIX) { f32::from_str(&f).map(ParsedFloat::F32) } else if self.consume_ident(F64_SUFFIX) { f64::from_str(&f).map(ParsedFloat::F64) } else { break; }; let parsed = if let Ok(parsed) = res { parsed } else { self.set_cursor(backup_cursor); return Err(Error::ExpectedFloat); }; let float_ron = &self.src[backup_cursor.cursor..self.cursor.cursor]; return T::try_from_parsed_float(parsed, float_ron); } self.set_cursor(backup_cursor); } let value = T::parse(&f)?; self.advance_bytes(num_bytes); Ok(value) } pub fn skip_identifier(&mut self) -> Option<&'a str> { #[allow(clippy::nonminimal_bool)] if self.check_str("b\"") // byte string || self.check_str("b'") // byte literal || self.check_str("br#") // raw byte string || self.check_str("br\"") // raw byte string || self.check_str("r\"") // raw string || self.check_str("r#\"") // raw string || self.check_str("r##") // raw string || false { return None; } if self.check_str("r#") { // maybe a raw identifier let len = self.next_chars_while_from_len(2, is_ident_raw_char); if len > 0 { let ident = &self.src()[2..2 + len]; self.advance_bytes(2 + len); return Some(ident); } return None; } if let Some(c) = self.peek_char() { // maybe a normal identifier if is_ident_first_char(c) { let len = c.len_utf8() + self.next_chars_while_from_len(c.len_utf8(), is_xid_continue); let ident = &self.src()[..len]; self.advance_bytes(len); return Some(ident); } } None } pub fn identifier(&mut self) -> Result<&'a str> { let first = self.peek_char_or_eof()?; if !is_ident_first_char(first) { if is_ident_raw_char(first) { let ident_bytes = self.next_chars_while_len(is_ident_raw_char); return Err(Error::SuggestRawIdentifier( self.src()[..ident_bytes].into(), )); } return Err(Error::ExpectedIdentifier); } // If the next 2-3 bytes signify the start of a (raw) (byte) string // literal, return an error. #[allow(clippy::nonminimal_bool)] if self.check_str("b\"") // byte string || self.check_str("b'") // byte literal || self.check_str("br#") // raw byte string || self.check_str("br\"") // raw byte string || self.check_str("r\"") // raw string || self.check_str("r#\"") // raw string || self.check_str("r##") // raw string || false { return Err(Error::ExpectedIdentifier); } let length = if self.check_str("r#") { let cursor_backup = self.cursor; self.advance_bytes(2); // Note: it's important to check this before advancing forward, so that // the value-type deserializer can fall back to parsing it differently. if !matches!(self.peek_char(), Some(c) if is_ident_raw_char(c)) { self.set_cursor(cursor_backup); return Err(Error::ExpectedIdentifier); } self.next_chars_while_len(is_ident_raw_char) } else if first == 'r' { let std_ident_length = self.next_chars_while_len(is_xid_continue); let raw_ident_length = self.next_chars_while_len(is_ident_raw_char); if raw_ident_length > std_ident_length { return Err(Error::SuggestRawIdentifier( self.src()[..raw_ident_length].into(), )); } std_ident_length } else { let std_ident_length = first.len_utf8() + self.next_chars_while_from_len(first.len_utf8(), is_xid_continue); let raw_ident_length = self.next_chars_while_len(is_ident_raw_char); if raw_ident_length > std_ident_length { return Err(Error::SuggestRawIdentifier( self.src()[..raw_ident_length].into(), )); } std_ident_length }; let ident = &self.src()[..length]; self.advance_bytes(length); Ok(ident) } pub fn next_bytes_is_float(&mut self) -> bool { if let Some(c) = self.peek_char() { let skip = match c { '+' | '-' => 1, _ => 0, }; let valid_float_len = self.next_chars_while_from_len(skip, is_float_char); let valid_int_len = self.next_chars_while_from_len(skip, is_int_char); valid_float_len > valid_int_len } else { false } } pub fn skip_ws(&mut self) -> Result<()> { if (self.cursor.last_ws_len != WS_CURSOR_UNCLOSED_LINE) && ((self.cursor.pre_ws_cursor + self.cursor.last_ws_len) < self.cursor.cursor) { // the last whitespace is disjoint from this one, we need to track a new one self.cursor.pre_ws_cursor = self.cursor.cursor; } if self.src().is_empty() { return Ok(()); } loop { self.advance_bytes(self.next_chars_while_len(is_whitespace_char)); match self.skip_comment()? { None => break, Some(Comment::UnclosedLine) => { self.cursor.last_ws_len = WS_CURSOR_UNCLOSED_LINE; return Ok(()); } Some(Comment::ClosedLine | Comment::Block) => continue, } } self.cursor.last_ws_len = self.cursor.cursor - self.cursor.pre_ws_cursor; Ok(()) } pub fn has_unclosed_line_comment(&self) -> bool { self.src().is_empty() && self.cursor.last_ws_len == WS_CURSOR_UNCLOSED_LINE } pub fn byte_string(&mut self) -> Result> { fn expected_byte_string_found_base64( base64_str: &ParsedStr, byte_str: &ParsedByteStr, ) -> Error { let byte_str = match &byte_str { ParsedByteStr::Allocated(b) => b.as_slice(), ParsedByteStr::Slice(b) => b, } .iter() .flat_map(|c| core::ascii::escape_default(*c)) .map(char::from) .collect::(); let base64_str = match &base64_str { ParsedStr::Allocated(s) => s.as_str(), ParsedStr::Slice(s) => s, }; Error::InvalidValueForType { expected: format!("the Rusty byte string b\"{}\"", byte_str), found: format!("the ambiguous base64 string {:?}", base64_str), } } // FIXME @juntyr: remove in v0.13, since only byte_string_no_base64 will // be used if self.consume_char('"') { let base64_str = self.escaped_string()?; let base64_result = ParsedByteStr::try_from_base64(&base64_str); match base64_result { Some(byte_str) => Err(expected_byte_string_found_base64(&base64_str, &byte_str)), None => Err(Error::ExpectedByteString), } } else if self.consume_char('r') { let base64_str = self.raw_string()?; let base64_result = ParsedByteStr::try_from_base64(&base64_str); match base64_result { Some(byte_str) => Err(expected_byte_string_found_base64(&base64_str, &byte_str)), None => Err(Error::ExpectedByteString), } } else { self.byte_string_no_base64() } } pub fn byte_string_no_base64(&mut self) -> Result> { if self.consume_str("b\"") { self.escaped_byte_string() } else if self.consume_str("br") { self.raw_byte_string() } else { Err(Error::ExpectedByteString) } } fn escaped_byte_string(&mut self) -> Result> { match self.escaped_byte_buf(EscapeEncoding::Binary) { Ok((bytes, advance)) => { self.advance_bytes(advance); Ok(bytes) } Err(err) => Err(err), } } fn raw_byte_string(&mut self) -> Result> { match self.raw_byte_buf() { Ok((bytes, advance)) => { self.advance_bytes(advance); Ok(bytes) } Err(Error::ExpectedString) => Err(Error::ExpectedByteString), Err(err) => Err(err), } } pub fn string(&mut self) -> Result> { if self.consume_char('"') { self.escaped_string() } else if self.consume_char('r') { self.raw_string() } else { Err(Error::ExpectedString) } } fn escaped_string(&mut self) -> Result> { match self.escaped_byte_buf(EscapeEncoding::Utf8) { Ok((bytes, advance)) => { let string = ParsedStr::try_from_bytes(bytes).map_err(Error::from)?; self.advance_bytes(advance); Ok(string) } Err(err) => Err(err), } } fn raw_string(&mut self) -> Result> { match self.raw_byte_buf() { Ok((bytes, advance)) => { let string = ParsedStr::try_from_bytes(bytes).map_err(Error::from)?; self.advance_bytes(advance); Ok(string) } Err(err) => Err(err), } } fn escaped_byte_buf(&mut self, encoding: EscapeEncoding) -> Result<(ParsedByteStr<'a>, usize)> { // Checking for '"' and '\\' separately is faster than searching for both at the same time let str_end = self.src().find('"').ok_or(Error::ExpectedStringEnd)?; let escape = self.src()[..str_end].find('\\'); if let Some(escape) = escape { // Now check if escaping is used inside the string let mut i = escape; let mut s = self.src().as_bytes()[..i].to_vec(); loop { self.advance_bytes(i + 1); match self.parse_escape(encoding, false)? { EscapeCharacter::Ascii(c) => s.push(c), EscapeCharacter::Utf8(c) => match c.len_utf8() { 1 => s.push(c as u8), len => { let start = s.len(); s.extend(core::iter::repeat(0).take(len)); c.encode_utf8(&mut s[start..]); } }, } // Checking for '"' and '\\' separately is faster than searching for both at the same time let new_str_end = self.src().find('"').ok_or(Error::ExpectedStringEnd)?; let new_escape = self.src()[..new_str_end].find('\\'); if let Some(new_escape) = new_escape { s.extend_from_slice(&self.src().as_bytes()[..new_escape]); i = new_escape; } else { s.extend_from_slice(&self.src().as_bytes()[..new_str_end]); // Advance to the end of the string + 1 for the `"`. break Ok((ParsedByteStr::Allocated(s), new_str_end + 1)); } } } else { let s = &self.src().as_bytes()[..str_end]; // Advance by the number of bytes of the string + 1 for the `"`. Ok((ParsedByteStr::Slice(s), str_end + 1)) } } fn raw_byte_buf(&mut self) -> Result<(ParsedByteStr<'a>, usize)> { let num_hashes = self.next_chars_while_len(|c| c == '#'); let hashes = &self.src()[..num_hashes]; self.advance_bytes(num_hashes); self.expect_char('"', Error::ExpectedString)?; let ending = ["\"", hashes].concat(); let i = self.src().find(&ending).ok_or(Error::ExpectedStringEnd)?; let s = &self.src().as_bytes()[..i]; // Advance by the number of bytes of the byte string // + `num_hashes` + 1 for the `"`. Ok((ParsedByteStr::Slice(s), i + num_hashes + 1)) } fn decode_ascii_escape(&mut self) -> Result { let mut n = 0; for _ in 0..2 { n <<= 4; let byte = self.next_char()?; let decoded = Self::decode_hex(byte)?; n |= decoded; } Ok(n) } #[inline] fn decode_hex(c: char) -> Result { if !c.is_ascii() { return Err(Error::InvalidEscape("Non-hex digit found")); } // c is an ASCII character that can be losslessly cast to u8 match c as u8 { c @ b'0'..=b'9' => Ok(c - b'0'), c @ b'a'..=b'f' => Ok(10 + c - b'a'), c @ b'A'..=b'F' => Ok(10 + c - b'A'), _ => Err(Error::InvalidEscape("Non-hex digit found")), } } fn parse_escape(&mut self, encoding: EscapeEncoding, is_char: bool) -> Result { let c = match self.next_char()? { '\'' => EscapeCharacter::Ascii(b'\''), '"' => EscapeCharacter::Ascii(b'"'), '\\' => EscapeCharacter::Ascii(b'\\'), 'n' => EscapeCharacter::Ascii(b'\n'), 'r' => EscapeCharacter::Ascii(b'\r'), 't' => EscapeCharacter::Ascii(b'\t'), '0' => EscapeCharacter::Ascii(b'\0'), 'x' => { // Fast exit for ascii escape in byte string let b: u8 = self.decode_ascii_escape()?; if let EscapeEncoding::Binary = encoding { return Ok(EscapeCharacter::Ascii(b)); } // Fast exit for ascii character in UTF-8 string let mut bytes = [b, 0, 0, 0]; if let Ok(Some(c)) = from_utf8(&bytes[..=0]).map(|s| s.chars().next()) { return Ok(EscapeCharacter::Utf8(c)); } if is_char { // Character literals are not allowed to use multiple byte // escapes to build a unicode character return Err(Error::InvalidEscape( "Not a valid byte-escaped Unicode character", )); } // UTF-8 character needs up to four bytes and we have already // consumed one, so at most three to go for i in 1..4 { if !self.consume_str(r"\x") { return Err(Error::InvalidEscape( "Not a valid byte-escaped Unicode character", )); } bytes[i] = self.decode_ascii_escape()?; // Check if we now have a valid UTF-8 character if let Ok(Some(c)) = from_utf8(&bytes[..=i]).map(|s| s.chars().next()) { return Ok(EscapeCharacter::Utf8(c)); } } return Err(Error::InvalidEscape( "Not a valid byte-escaped Unicode character", )); } 'u' => { self.expect_char('{', Error::InvalidEscape("Missing { in Unicode escape"))?; let mut bytes: u32 = 0; let mut num_digits = 0; while num_digits < 6 { let byte = self.peek_char_or_eof()?; if byte == '}' { break; } self.skip_next_char(); num_digits += 1; let byte = Self::decode_hex(byte)?; bytes <<= 4; bytes |= u32::from(byte); } if num_digits == 0 { return Err(Error::InvalidEscape( "Expected 1-6 digits, got 0 digits in Unicode escape", )); } self.expect_char( '}', Error::InvalidEscape("No } at the end of Unicode escape"), )?; let c = char_from_u32(bytes).ok_or(Error::InvalidEscape( "Not a valid Unicode-escaped character", ))?; EscapeCharacter::Utf8(c) } _ => return Err(Error::InvalidEscape("Unknown escape character")), }; Ok(c) } fn skip_comment(&mut self) -> Result> { if self.consume_char('/') { match self.next_char()? { '/' => { let bytes = self.next_chars_while_len(|c| c != '\n'); self.advance_bytes(bytes); if self.src().is_empty() { Ok(Some(Comment::UnclosedLine)) } else { Ok(Some(Comment::ClosedLine)) } } '*' => { let mut level = 1; while level > 0 { let bytes = self.next_chars_while_len(|c| !matches!(c, '/' | '*')); if self.src().is_empty() { return Err(Error::UnclosedBlockComment); } self.advance_bytes(bytes); // check whether / or * and take action if self.consume_str("/*") { level += 1; } else if self.consume_str("*/") { level -= 1; } else { self.next_char().map_err(|_| Error::UnclosedBlockComment)?; } } Ok(Some(Comment::Block)) } c => Err(Error::UnexpectedChar(c)), } } else { Ok(None) } } } enum Comment { ClosedLine, UnclosedLine, Block, } pub trait Num { fn from_u8(x: u8) -> Self; /// Returns `true` on overflow fn checked_mul_ext(&mut self, x: u8) -> bool; /// Returns `true` on overflow fn checked_add_ext(&mut self, x: u8) -> bool; /// Returns `true` on overflow fn checked_sub_ext(&mut self, x: u8) -> bool; } macro_rules! impl_num { ($ty:ty) => { impl Num for $ty { fn from_u8(x: u8) -> Self { x as $ty } fn checked_mul_ext(&mut self, x: u8) -> bool { match self.checked_mul(Self::from_u8(x)) { Some(n) => { *self = n; false } None => true, } } fn checked_add_ext(&mut self, x: u8) -> bool { match self.checked_add(Self::from_u8(x)) { Some(n) => { *self = n; false } None => true, } } fn checked_sub_ext(&mut self, x: u8) -> bool { match self.checked_sub(Self::from_u8(x)) { Some(n) => { *self = n; false } None => true, } } } }; ($($tys:ty)*) => { $( impl_num!($tys); )* }; } impl_num! { i8 i16 i32 i64 u8 u16 u32 u64 } #[cfg(feature = "integer128")] impl_num! { i128 u128 } pub trait Integer: Sized { fn parse(parser: &mut Parser, sign: i8) -> Result; fn try_from_parsed_integer(parsed: ParsedInteger, ron: &str) -> Result; } macro_rules! impl_integer { ($wrap:ident($ty:ty)) => { impl Integer for $ty { fn parse(parser: &mut Parser, sign: i8) -> Result { parser.parse_integer(sign) } fn try_from_parsed_integer(parsed: ParsedInteger, ron: &str) -> Result { match parsed { ParsedInteger::$wrap(v) => Ok(v), _ => Err(Error::InvalidValueForType { expected: format!( "a{} {}-bit {}signed integer", if <$ty>::BITS == 8 { "n" } else { "n" }, <$ty>::BITS, if <$ty>::MIN == 0 { "un" } else { "" }, ), found: String::from(ron), }), } } } }; ($($wraps:ident($tys:ty))*) => { $( impl_integer!($wraps($tys)); )* }; } impl_integer! { I8(i8) I16(i16) I32(i32) I64(i64) U8(u8) U16(u16) U32(u32) U64(u64) } #[cfg(feature = "integer128")] impl_integer! { I128(i128) U128(u128) } pub enum ParsedInteger { I8(i8), I16(i16), I32(i32), I64(i64), #[cfg(feature = "integer128")] I128(i128), U8(u8), U16(u16), U32(u32), U64(u64), #[cfg(feature = "integer128")] U128(u128), } impl Integer for ParsedInteger { fn parse(parser: &mut Parser, sign: i8) -> Result { if sign < 0 { let signed = parser.parse_integer::(-1)?; return if let Ok(x) = i8::try_from(signed) { Ok(ParsedInteger::I8(x)) } else if let Ok(x) = i16::try_from(signed) { Ok(ParsedInteger::I16(x)) } else if let Ok(x) = i32::try_from(signed) { Ok(ParsedInteger::I32(x)) } else { #[cfg(not(feature = "integer128"))] { Ok(ParsedInteger::I64(signed)) } #[cfg(feature = "integer128")] if let Ok(x) = i64::try_from(signed) { Ok(ParsedInteger::I64(x)) } else { Ok(ParsedInteger::I128(signed)) } }; } let unsigned = parser.parse_integer::(1)?; if let Ok(x) = u8::try_from(unsigned) { Ok(ParsedInteger::U8(x)) } else if let Ok(x) = u16::try_from(unsigned) { Ok(ParsedInteger::U16(x)) } else if let Ok(x) = u32::try_from(unsigned) { Ok(ParsedInteger::U32(x)) } else { #[cfg(not(feature = "integer128"))] { Ok(ParsedInteger::U64(unsigned)) } #[cfg(feature = "integer128")] if let Ok(x) = u64::try_from(unsigned) { Ok(ParsedInteger::U64(x)) } else { Ok(ParsedInteger::U128(unsigned)) } } } fn try_from_parsed_integer(parsed: ParsedInteger, _ron: &str) -> Result { Ok(parsed) } } pub trait Float: Sized { fn parse(float: &str) -> Result; fn try_from_parsed_float(parsed: ParsedFloat, ron: &str) -> Result; } macro_rules! impl_float { ($wrap:ident($ty:ty: $bits:expr)) => { impl Float for $ty { fn parse(float: &str) -> Result { <$ty>::from_str(float).map_err(|_| Error::ExpectedFloat) } fn try_from_parsed_float(parsed: ParsedFloat, ron: &str) -> Result { match parsed { ParsedFloat::$wrap(v) => Ok(v), _ => Err(Error::InvalidValueForType { expected: format!( "a {}-bit floating point number", $bits, ), found: String::from(ron), }), } } } }; ($($wraps:ident($tys:ty: $bits:expr))*) => { $( impl_float!($wraps($tys: $bits)); )* }; } impl_float! { F32(f32: 32) F64(f64: 64) } pub enum ParsedFloat { F32(f32), F64(f64), } impl Float for ParsedFloat { fn parse(float: &str) -> Result { let value = f64::from_str(float).map_err(|_| Error::ExpectedFloat)?; #[allow(clippy::cast_possible_truncation)] if value.total_cmp(&f64::from(value as f32)).is_eq() { Ok(ParsedFloat::F32(value as f32)) } else { Ok(ParsedFloat::F64(value)) } } fn try_from_parsed_float(parsed: ParsedFloat, _ron: &str) -> Result { Ok(parsed) } } pub enum StructType { AnyTuple, EmptyTuple, NewtypeTuple, NonNewtypeTuple, Named, Unit, } #[derive(Copy, Clone)] // GRCOV_EXCL_LINE pub enum NewtypeMode { NoParensMeanUnit, InsideNewtype, } #[derive(Copy, Clone)] // GRCOV_EXCL_LINE pub enum TupleMode { ImpreciseTupleOrNewtype, DifferentiateNewtype, } pub enum ParsedStr<'a> { Allocated(String), Slice(&'a str), } pub enum ParsedByteStr<'a> { Allocated(Vec), Slice(&'a [u8]), } impl<'a> ParsedStr<'a> { pub fn try_from_bytes(bytes: ParsedByteStr<'a>) -> Result { match bytes { ParsedByteStr::Allocated(byte_buf) => Ok(ParsedStr::Allocated( String::from_utf8(byte_buf).map_err(|e| e.utf8_error())?, )), ParsedByteStr::Slice(bytes) => Ok(ParsedStr::Slice(from_utf8(bytes)?)), } } } impl<'a> ParsedByteStr<'a> { pub fn try_from_base64(str: &ParsedStr<'a>) -> Option { // Adapted from MIT licensed Jenin Sutradhar's base 64 decoder // https://github.com/JeninSutradhar/base64-Rust-Encoder-Decoder/blob/ee1fb08cbb78024ec8cf5e786815acb239169f02/src/lib.rs#L84-L128 fn try_decode_base64(str: &str) -> Option> { const CHARSET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const PADDING: u8 = b'='; // fast reject for missing padding if (str.len() % 4) != 0 { return None; } let bstr_no_padding = str.trim_end_matches(char::from(PADDING)).as_bytes(); // fast reject for excessive padding if (str.len() - bstr_no_padding.len()) > 2 { return None; } // fast reject for extraneous bytes after padding if bstr_no_padding.contains(&PADDING) { return None; } // fast reject for non-ASCII if !str.is_ascii() { return None; } let mut collected_bits = 0_u8; let mut byte_buffer = 0_u16; let mut bytes = bstr_no_padding.iter().copied(); let mut binary = Vec::new(); 'decodeloop: loop { while collected_bits < 8 { if let Some(nextbyte) = bytes.next() { #[allow(clippy::cast_possible_truncation)] if let Some(idx) = CHARSET.iter().position(|&x| x == nextbyte) { byte_buffer |= ((idx & 0b0011_1111) as u16) << (10 - collected_bits); collected_bits += 6; } else { return None; } } else { break 'decodeloop; } } binary.push(((0b1111_1111_0000_0000 & byte_buffer) >> 8) as u8); byte_buffer &= 0b0000_0000_1111_1111; byte_buffer <<= 8; collected_bits -= 8; } if usize::from(collected_bits) != ((str.len() - bstr_no_padding.len()) * 2) { return None; } Some(binary) } let base64_str = match str { ParsedStr::Allocated(string) => string.as_str(), ParsedStr::Slice(str) => str, }; try_decode_base64(base64_str).map(ParsedByteStr::Allocated) } } #[derive(Copy, Clone)] // GRCOV_EXCL_LINE enum EscapeEncoding { Binary, Utf8, } enum EscapeCharacter { Ascii(u8), Utf8(char), } #[cfg(test)] mod tests { use super::*; #[test] fn decode_x10() { let mut bytes = Parser::new("10").unwrap(); assert_eq!(bytes.decode_ascii_escape(), Ok(b'\x10')); } #[test] fn track_prior_ws() { const SOURCE: &str = " /*hey*/ 42 /*bye*/ 24 "; let mut bytes = Parser::new(SOURCE).unwrap(); assert_eq!(bytes.src(), "42 /*bye*/ 24 "); assert_eq!(bytes.pre_ws_src(), SOURCE); bytes.skip_ws().unwrap(); assert_eq!(bytes.src(), "42 /*bye*/ 24 "); assert_eq!(bytes.pre_ws_src(), SOURCE); assert_eq!(bytes.integer::().unwrap(), 42); assert_eq!(bytes.src(), " /*bye*/ 24 "); assert_eq!(bytes.pre_ws_src(), SOURCE); bytes.skip_ws().unwrap(); bytes.skip_ws().unwrap(); assert_eq!(bytes.src(), "24 "); assert_eq!(bytes.pre_ws_src(), " /*bye*/ 24 "); let mut bytes = Parser::new("42").unwrap(); bytes.skip_ws().unwrap(); bytes.skip_ws().unwrap(); assert_eq!(bytes.src(), "42"); assert_eq!(bytes.pre_ws_src(), "42"); assert_eq!(bytes.integer::().unwrap(), 42); bytes.skip_ws().unwrap(); bytes.skip_ws().unwrap(); assert_eq!(bytes.src(), ""); assert_eq!(bytes.pre_ws_src(), ""); let mut bytes = Parser::new(" 42 ").unwrap(); bytes.skip_ws().unwrap(); bytes.skip_ws().unwrap(); assert_eq!(bytes.src(), "42 "); assert_eq!(bytes.pre_ws_src(), " 42 "); assert_eq!(bytes.integer::().unwrap(), 42); bytes.skip_ws().unwrap(); bytes.skip_ws().unwrap(); assert_eq!(bytes.src(), ""); assert_eq!(bytes.pre_ws_src(), " "); let mut bytes = Parser::new(" 42 //").unwrap(); bytes.skip_ws().unwrap(); bytes.skip_ws().unwrap(); assert_eq!(bytes.src(), "42 //"); assert_eq!(bytes.pre_ws_src(), " 42 //"); assert_eq!(bytes.integer::().unwrap(), 42); bytes.skip_ws().unwrap(); bytes.skip_ws().unwrap(); assert_eq!(bytes.src(), ""); assert_eq!(bytes.pre_ws_src(), " //"); } #[test] fn parser_cursor_eq_cmp() { assert!( ParserCursor { cursor: 42, pre_ws_cursor: 42, last_ws_len: 42 } == ParserCursor { cursor: 42, pre_ws_cursor: 24, last_ws_len: 24 } ); assert!( ParserCursor { cursor: 42, pre_ws_cursor: 42, last_ws_len: 42 } != ParserCursor { cursor: 24, pre_ws_cursor: 42, last_ws_len: 42 } ); assert!( ParserCursor { cursor: 42, pre_ws_cursor: 42, last_ws_len: 42 } < ParserCursor { cursor: 43, pre_ws_cursor: 24, last_ws_len: 24 } ); assert!( ParserCursor { cursor: 42, pre_ws_cursor: 42, last_ws_len: 42 } > ParserCursor { cursor: 41, pre_ws_cursor: 24, last_ws_len: 24 } ); } #[test] fn empty_src_is_not_a_float() { assert!(!Parser::new("").unwrap().next_bytes_is_float()); } #[test] fn base64_deprecation_error() { let err = crate::from_str::("\"SGVsbG8gcm9uIQ==\"").unwrap_err(); assert_eq!( err, SpannedError { code: Error::InvalidValueForType { expected: String::from("the Rusty byte string b\"Hello ron!\""), found: String::from("the ambiguous base64 string \"SGVsbG8gcm9uIQ==\"") }, span: Span { start: Position { line: 1, col: 2 }, end: Position { line: 1, col: 19 }, } } ); let err = crate::from_str::("r\"SGVsbG8gcm9uIQ==\"").unwrap_err(); assert_eq!(format!("{}", err.code), "Expected the Rusty byte string b\"Hello ron!\" but found the ambiguous base64 string \"SGVsbG8gcm9uIQ==\" instead"); assert_eq!( crate::from_str::("\"invalid=\"").unwrap_err(), SpannedError { code: Error::InvalidValueForType { expected: String::from("the Rusty byte string b\"\\x8a{\\xda\\x96\\'\""), found: String::from("the ambiguous base64 string \"invalid=\"") }, span: Span { start: Position { line: 1, col: 2 }, end: Position { line: 1, col: 11 }, } } ); assert_eq!( crate::from_str::("r\"invalid=\"").unwrap_err(), SpannedError { code: Error::InvalidValueForType { expected: String::from("the Rusty byte string b\"\\x8a{\\xda\\x96\\'\""), found: String::from("the ambiguous base64 string \"invalid=\"") }, span: Span { start: Position { line: 1, col: 3 }, end: Position { line: 1, col: 12 }, } } ); assert_eq!( crate::from_str::("r\"invalid\"").unwrap_err(), SpannedError { code: Error::ExpectedByteString, span: Span { start: Position { line: 1, col: 3 }, end: Position { line: 1, col: 11 }, } } ); } } ron-0.12.0/src/ser/mod.rs000064400000000000000000001203061046102023000132110ustar 00000000000000use alloc::{borrow::Cow, string::String}; use core::fmt; use serde::{ser, ser::Serialize}; use serde_derive::{Deserialize, Serialize}; use unicode_ident::is_xid_continue; use crate::{ error::{Error, Result}, extensions::Extensions, options::Options, parse::{is_ident_first_char, is_ident_raw_char, is_whitespace_char, LargeSInt, LargeUInt}, }; pub mod path_meta; mod raw; #[cfg(test)] mod tests; mod value; /// Serializes `value` into `writer`. /// /// This function does not generate any newlines or nice formatting; /// if you want that, you can use [`to_writer_pretty`] instead. pub fn to_writer(writer: W, value: &T) -> Result<()> where W: fmt::Write, T: ?Sized + Serialize, { Options::default().to_writer(writer, value) } /// Serializes `value` into `writer` in a pretty way. pub fn to_writer_pretty(writer: W, value: &T, config: PrettyConfig) -> Result<()> where W: fmt::Write, T: ?Sized + Serialize, { Options::default().to_writer_pretty(writer, value, config) } /// Serializes `value` and returns it as string. /// /// This function does not generate any newlines or nice formatting; /// if you want that, you can use [`to_string_pretty`] instead. pub fn to_string(value: &T) -> Result where T: ?Sized + Serialize, { Options::default().to_string(value) } /// Serializes `value` in the recommended RON layout in a pretty way. pub fn to_string_pretty(value: &T, config: PrettyConfig) -> Result where T: ?Sized + Serialize, { Options::default().to_string_pretty(value, config) } /// Pretty serializer state struct Pretty { indent: usize, } /// Pretty serializer configuration. /// /// # Examples /// /// ``` /// use ron::ser::PrettyConfig; /// /// let my_config = PrettyConfig::new() /// .depth_limit(4) /// // definitely superior (okay, just joking) /// .indentor("\t"); /// ``` #[allow(clippy::struct_excessive_bools)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(default)] #[non_exhaustive] pub struct PrettyConfig { /// Limit the pretty-ness up to the given depth. pub depth_limit: usize, /// New line string pub new_line: Cow<'static, str>, /// Indentation string pub indentor: Cow<'static, str>, /// Separator string pub separator: Cow<'static, str>, // Whether to emit struct names pub struct_names: bool, /// Separate tuple members with indentation pub separate_tuple_members: bool, /// Enumerate array items in comments pub enumerate_arrays: bool, /// Enable extensions. Only configures `implicit_some`, /// `unwrap_newtypes`, and `unwrap_variant_newtypes` for now. pub extensions: Extensions, /// Enable compact arrays, which do not insert new lines and indentation /// between the elements of an array pub compact_arrays: bool, /// Whether to serialize strings as escaped strings, /// or fall back onto raw strings if necessary. pub escape_strings: bool, /// Enable compact structs, which do not insert new lines and indentation /// between the fields of a struct pub compact_structs: bool, /// Enable compact maps, which do not insert new lines and indentation /// between the entries of a struct pub compact_maps: bool, /// Enable explicit number type suffixes like `1u16` pub number_suffixes: bool, /// Additional path-based field metadata to serialize pub path_meta: Option, } impl PrettyConfig { /// Creates a default [`PrettyConfig`]. #[must_use] pub fn new() -> Self { Self::default() } /// Limits the pretty-formatting based on the number of indentations. /// I.e., with a depth limit of 5, starting with an element of depth /// (indentation level) 6, everything will be put into the same line, /// without pretty formatting. /// /// Default: [`usize::MAX`] #[must_use] pub fn depth_limit(mut self, depth_limit: usize) -> Self { self.depth_limit = depth_limit; self } /// Configures the newlines used for serialization. /// /// Default: `\r\n` on Windows, `\n` otherwise #[must_use] pub fn new_line(mut self, new_line: impl Into>) -> Self { self.new_line = new_line.into(); self } /// Configures the string sequence used for indentation. /// /// Default: 4 spaces #[must_use] pub fn indentor(mut self, indentor: impl Into>) -> Self { self.indentor = indentor.into(); self } /// Configures the string sequence used to separate items inline. /// /// Default: 1 space #[must_use] pub fn separator(mut self, separator: impl Into>) -> Self { self.separator = separator.into(); self } /// Configures whether to emit struct names. /// /// See also [`Extensions::EXPLICIT_STRUCT_NAMES`] for the extension equivalent. /// /// Default: `false` #[must_use] pub fn struct_names(mut self, struct_names: bool) -> Self { self.struct_names = struct_names; self } /// Configures whether tuples are single- or multi-line. /// If set to `true`, tuples will have their fields indented and in new /// lines. If set to `false`, tuples will be serialized without any /// newlines or indentations. /// /// Default: `false` #[must_use] pub fn separate_tuple_members(mut self, separate_tuple_members: bool) -> Self { self.separate_tuple_members = separate_tuple_members; self } /// Configures whether a comment shall be added to every array element, /// indicating the index. /// /// Default: `false` #[must_use] pub fn enumerate_arrays(mut self, enumerate_arrays: bool) -> Self { self.enumerate_arrays = enumerate_arrays; self } /// Configures whether every array should be a single line (`true`) /// or a multi line one (`false`). /// /// When `false`, `["a","b"]` will serialize to /// ``` /// [ /// "a", /// "b", /// ] /// # ; /// ``` /// When `true`, `["a","b"]` will instead serialize to /// ``` /// ["a","b"] /// # ; /// ``` /// /// Default: `false` #[must_use] pub fn compact_arrays(mut self, compact_arrays: bool) -> Self { self.compact_arrays = compact_arrays; self } /// Configures extensions /// /// Default: [`Extensions::empty()`] #[must_use] pub fn extensions(mut self, extensions: Extensions) -> Self { self.extensions = extensions; self } /// Configures whether strings should be serialized using escapes (true) /// or fall back to raw strings if the string contains a `"` (false). /// /// When `true`, `"a\nb"` will serialize to /// ``` /// "a\nb" /// # ; /// ``` /// When `false`, `"a\nb"` will instead serialize to /// ``` /// "a /// b" /// # ; /// ``` /// /// Default: `true` #[must_use] pub fn escape_strings(mut self, escape_strings: bool) -> Self { self.escape_strings = escape_strings; self } /// Configures whether every struct should be a single line (`true`) /// or a multi line one (`false`). /// /// When `false`, `Struct { a: 4, b: 2 }` will serialize to /// ```ignore /// Struct( /// a: 4, /// b: 2, /// ) /// # ; /// ``` /// When `true`, `Struct { a: 4, b: 2 }` will instead serialize to /// ```ignore /// Struct(a: 4, b: 2) /// # ; /// ``` /// /// Default: `false` #[must_use] pub fn compact_structs(mut self, compact_structs: bool) -> Self { self.compact_structs = compact_structs; self } /// Configures whether every map should be a single line (`true`) /// or a multi line one (`false`). /// /// When `false`, a map with entries `{ "a": 4, "b": 2 }` will serialize to /// ```ignore /// { /// "a": 4, /// "b": 2, /// } /// # ; /// ``` /// When `true`, a map with entries `{ "a": 4, "b": 2 }` will instead /// serialize to /// ```ignore /// {"a": 4, "b": 2} /// # ; /// ``` /// /// Default: `false` #[must_use] pub fn compact_maps(mut self, compact_maps: bool) -> Self { self.compact_maps = compact_maps; self } /// Configures whether numbers should be printed without (`false`) or /// with (`true`) their explicit type suffixes. /// /// When `false`, the integer `12345u16` will serialize to /// ```ignore /// 12345 /// # ; /// ``` /// and the float `12345.6789f64` will serialize to /// ```ignore /// 12345.6789 /// # ; /// ``` /// When `true`, the integer `12345u16` will serialize to /// ```ignore /// 12345u16 /// # ; /// ``` /// and the float `12345.6789f64` will serialize to /// ```ignore /// 12345.6789f64 /// # ; /// ``` /// /// Default: `false` #[must_use] pub fn number_suffixes(mut self, number_suffixes: bool) -> Self { self.number_suffixes = number_suffixes; self } } impl Default for PrettyConfig { fn default() -> Self { PrettyConfig { depth_limit: usize::MAX, new_line: if cfg!(not(target_os = "windows")) { Cow::Borrowed("\n") } else { Cow::Borrowed("\r\n") // GRCOV_EXCL_LINE }, indentor: Cow::Borrowed(" "), separator: Cow::Borrowed(" "), struct_names: false, separate_tuple_members: false, enumerate_arrays: false, extensions: Extensions::empty(), compact_arrays: false, escape_strings: true, compact_structs: false, compact_maps: false, number_suffixes: false, path_meta: None, } } } /// The RON serializer. /// /// You can just use [`to_string`] for deserializing a value. /// If you want it pretty-printed, take a look at [`to_string_pretty`]. pub struct Serializer { output: W, pretty: Option<(PrettyConfig, Pretty)>, default_extensions: Extensions, is_empty: Option, newtype_variant: bool, recursion_limit: Option, // Tracks the number of opened implicit `Some`s, set to 0 on backtracking implicit_some_depth: usize, } fn indent(output: &mut W, config: &PrettyConfig, pretty: &Pretty) -> fmt::Result { if pretty.indent <= config.depth_limit { for _ in 0..pretty.indent { output.write_str(&config.indentor)?; } } Ok(()) } impl Serializer { /// Creates a new [`Serializer`]. /// /// Most of the time you can just use [`to_string`] or /// [`to_string_pretty`]. pub fn new(writer: W, config: Option) -> Result { Self::with_options(writer, config, &Options::default()) } /// Creates a new [`Serializer`]. /// /// Most of the time you can just use [`to_string`] or /// [`to_string_pretty`]. pub fn with_options( mut writer: W, config: Option, options: &Options, ) -> Result { if let Some(conf) = &config { if !conf.new_line.chars().all(is_whitespace_char) { return Err(Error::Message(String::from( "Invalid non-whitespace `PrettyConfig::new_line`", ))); } if !conf.indentor.chars().all(is_whitespace_char) { return Err(Error::Message(String::from( "Invalid non-whitespace `PrettyConfig::indentor`", ))); } if !conf.separator.chars().all(is_whitespace_char) { return Err(Error::Message(String::from( "Invalid non-whitespace `PrettyConfig::separator`", ))); } let non_default_extensions = !options.default_extensions; for (extension_name, _) in (non_default_extensions & conf.extensions).iter_names() { write!(writer, "#![enable({})]", extension_name.to_lowercase())?; writer.write_str(&conf.new_line)?; } }; Ok(Serializer { output: writer, pretty: config.map(|conf| (conf, Pretty { indent: 0 })), default_extensions: options.default_extensions, is_empty: None, newtype_variant: false, recursion_limit: options.recursion_limit, implicit_some_depth: 0, }) } /// Unwrap the `Writer` from the `Serializer`. #[inline] pub fn into_inner(self) -> W { self.output } fn separate_tuple_members(&self) -> bool { self.pretty .as_ref() .map_or(false, |(ref config, _)| config.separate_tuple_members) } fn compact_arrays(&self) -> bool { self.pretty .as_ref() .map_or(false, |(ref config, _)| config.compact_arrays) } fn compact_structs(&self) -> bool { self.pretty .as_ref() .map_or(false, |(ref config, _)| config.compact_structs) } fn compact_maps(&self) -> bool { self.pretty .as_ref() .map_or(false, |(ref config, _)| config.compact_maps) } fn number_suffixes(&self) -> bool { self.pretty .as_ref() .map_or(false, |(ref config, _)| config.number_suffixes) } fn extensions(&self) -> Extensions { self.default_extensions | self .pretty .as_ref() .map_or(Extensions::empty(), |(ref config, _)| config.extensions) } fn escape_strings(&self) -> bool { self.pretty .as_ref() .map_or(true, |(ref config, _)| config.escape_strings) } fn start_indent(&mut self) -> Result<()> { if let Some((ref config, ref mut pretty)) = self.pretty { pretty.indent += 1; if pretty.indent <= config.depth_limit { let is_empty = self.is_empty.unwrap_or(false); if !is_empty { self.output.write_str(&config.new_line)?; } } } Ok(()) } fn indent(&mut self) -> fmt::Result { if let Some((ref config, ref pretty)) = self.pretty { indent(&mut self.output, config, pretty)?; } Ok(()) } fn end_indent(&mut self) -> fmt::Result { if let Some((ref config, ref mut pretty)) = self.pretty { if pretty.indent <= config.depth_limit { let is_empty = self.is_empty.unwrap_or(false); if !is_empty { for _ in 1..pretty.indent { self.output.write_str(&config.indentor)?; } } } pretty.indent -= 1; self.is_empty = None; } Ok(()) } fn serialize_escaped_str(&mut self, value: &str) -> fmt::Result { self.output.write_char('"')?; let mut scalar = [0u8; 4]; for c in value.chars().flat_map(char::escape_debug) { self.output.write_str(c.encode_utf8(&mut scalar))?; } self.output.write_char('"')?; Ok(()) } fn serialize_unescaped_or_raw_str(&mut self, value: &str) -> fmt::Result { if value.contains('"') || value.contains('\\') { let (_, num_consecutive_hashes) = value.chars().fold((0, 0), |(count, max), c| match c { '#' => (count + 1, max.max(count + 1)), _ => (0_usize, max), }); let hashes: String = "#".repeat(num_consecutive_hashes + 1); self.output.write_char('r')?; self.output.write_str(&hashes)?; self.output.write_char('"')?; self.output.write_str(value)?; self.output.write_char('"')?; self.output.write_str(&hashes)?; } else { self.output.write_char('"')?; self.output.write_str(value)?; self.output.write_char('"')?; } Ok(()) } fn serialize_escaped_byte_str(&mut self, value: &[u8]) -> fmt::Result { self.output.write_str("b\"")?; for c in value.iter().flat_map(|c| core::ascii::escape_default(*c)) { self.output.write_char(char::from(c))?; } self.output.write_char('"')?; Ok(()) } fn serialize_unescaped_or_raw_byte_str(&mut self, value: &str) -> fmt::Result { if value.contains('"') || value.contains('\\') { let (_, num_consecutive_hashes) = value.chars().fold((0, 0), |(count, max), c| match c { '#' => (count + 1, max.max(count + 1)), _ => (0_usize, max), }); let hashes: String = "#".repeat(num_consecutive_hashes + 1); self.output.write_str("br")?; self.output.write_str(&hashes)?; self.output.write_char('"')?; self.output.write_str(value)?; self.output.write_char('"')?; self.output.write_str(&hashes)?; } else { self.output.write_str("b\"")?; self.output.write_str(value)?; self.output.write_char('"')?; } Ok(()) } fn serialize_sint(&mut self, value: impl Into, suffix: &str) -> Result<()> { // TODO optimize write!(self.output, "{}", value.into())?; if self.number_suffixes() { write!(self.output, "{}", suffix)?; } Ok(()) } fn serialize_uint(&mut self, value: impl Into, suffix: &str) -> Result<()> { // TODO optimize write!(self.output, "{}", value.into())?; if self.number_suffixes() { write!(self.output, "{}", suffix)?; } Ok(()) } fn write_identifier(&mut self, name: &str) -> Result<()> { self.validate_identifier(name)?; let mut chars = name.chars(); if !chars.next().map_or(false, is_ident_first_char) || !chars.all(is_xid_continue) || [ "true", "false", "Some", "None", "inf", "inff32", "inff64", "NaN", "NaNf32", "NaNf64", ] .contains(&name) { self.output.write_str("r#")?; } self.output.write_str(name)?; Ok(()) } #[allow(clippy::unused_self)] fn validate_identifier(&self, name: &str) -> Result<()> { if name.is_empty() || !name.chars().all(is_ident_raw_char) { return Err(Error::InvalidIdentifier(name.into())); } Ok(()) } /// Checks if struct names should be emitted /// /// Note that when using the `explicit_struct_names` extension, this method will use an OR operation on the extension and the [`PrettyConfig::struct_names`] option. See also [`Extensions::EXPLICIT_STRUCT_NAMES`] for the extension equivalent. fn struct_names(&self) -> bool { self.extensions() .contains(Extensions::EXPLICIT_STRUCT_NAMES) || self .pretty .as_ref() .map_or(false, |(pc, _)| pc.struct_names) } } macro_rules! guard_recursion { ($self:expr => $expr:expr) => {{ if let Some(limit) = &mut $self.recursion_limit { if let Some(new_limit) = limit.checked_sub(1) { *limit = new_limit; } else { return Err(Error::ExceededRecursionLimit); } } let result = $expr; if let Some(limit) = &mut $self.recursion_limit { *limit = limit.saturating_add(1); } result }}; } impl<'a, W: fmt::Write> ser::Serializer for &'a mut Serializer { type Error = Error; type Ok = (); type SerializeMap = Compound<'a, W>; type SerializeSeq = Compound<'a, W>; type SerializeStruct = Compound<'a, W>; type SerializeStructVariant = Compound<'a, W>; type SerializeTuple = Compound<'a, W>; type SerializeTupleStruct = Compound<'a, W>; type SerializeTupleVariant = Compound<'a, W>; fn serialize_bool(self, v: bool) -> Result<()> { self.output.write_str(if v { "true" } else { "false" })?; Ok(()) } fn serialize_i8(self, v: i8) -> Result<()> { self.serialize_sint(v, "i8") } fn serialize_i16(self, v: i16) -> Result<()> { self.serialize_sint(v, "i16") } fn serialize_i32(self, v: i32) -> Result<()> { self.serialize_sint(v, "i32") } fn serialize_i64(self, v: i64) -> Result<()> { self.serialize_sint(v, "i64") } #[cfg(feature = "integer128")] fn serialize_i128(self, v: i128) -> Result<()> { self.serialize_sint(v, "i128") } fn serialize_u8(self, v: u8) -> Result<()> { self.serialize_uint(v, "u8") } fn serialize_u16(self, v: u16) -> Result<()> { self.serialize_uint(v, "u16") } fn serialize_u32(self, v: u32) -> Result<()> { self.serialize_uint(v, "u32") } fn serialize_u64(self, v: u64) -> Result<()> { self.serialize_uint(v, "u64") } #[cfg(feature = "integer128")] fn serialize_u128(self, v: u128) -> Result<()> { self.serialize_uint(v, "u128") } fn serialize_f32(self, v: f32) -> Result<()> { if v.is_nan() && v.is_sign_negative() { write!(self.output, "-")?; } write!(self.output, "{}", v)?; // Equivalent to v.fract() == 0.0 // See: https://docs.rs/num-traits/0.2.19/src/num_traits/float.rs.html#459-465 if v % 1. == 0.0 { write!(self.output, ".0")?; } if self.number_suffixes() { write!(self.output, "f32")?; } Ok(()) } fn serialize_f64(self, v: f64) -> Result<()> { if v.is_nan() && v.is_sign_negative() { write!(self.output, "-")?; } write!(self.output, "{}", v)?; // Equivalent to v.fract() == 0.0 // See: https://docs.rs/num-traits/0.2.19/src/num_traits/float.rs.html#459-465 if v % 1. == 0.0 { write!(self.output, ".0")?; } if self.number_suffixes() { write!(self.output, "f64")?; } Ok(()) } fn serialize_char(self, v: char) -> Result<()> { self.output.write_char('\'')?; if v == '\\' || v == '\'' { self.output.write_char('\\')?; } write!(self.output, "{}", v)?; self.output.write_char('\'')?; Ok(()) } fn serialize_str(self, v: &str) -> Result<()> { if self.escape_strings() { self.serialize_escaped_str(v)?; } else { self.serialize_unescaped_or_raw_str(v)?; } Ok(()) } fn serialize_bytes(self, v: &[u8]) -> Result<()> { // We need to fall back to escaping if the byte string would be invalid UTF-8 if !self.escape_strings() { if let Ok(v) = core::str::from_utf8(v) { return self .serialize_unescaped_or_raw_byte_str(v) .map_err(Error::from); } } self.serialize_escaped_byte_str(v)?; Ok(()) } fn serialize_none(self) -> Result<()> { // We no longer need to keep track of the depth let implicit_some_depth = self.implicit_some_depth; self.implicit_some_depth = 0; for _ in 0..implicit_some_depth { self.output.write_str("Some(")?; } self.output.write_str("None")?; for _ in 0..implicit_some_depth { self.output.write_char(')')?; } Ok(()) } fn serialize_some(self, value: &T) -> Result<()> where T: ?Sized + Serialize, { let implicit_some = self.extensions().contains(Extensions::IMPLICIT_SOME); if implicit_some { self.implicit_some_depth += 1; } else { self.newtype_variant = self .extensions() .contains(Extensions::UNWRAP_VARIANT_NEWTYPES); self.output.write_str("Some(")?; } guard_recursion! { self => value.serialize(&mut *self)? }; if implicit_some { self.implicit_some_depth = 0; } else { self.output.write_char(')')?; self.newtype_variant = false; } Ok(()) } fn serialize_unit(self) -> Result<()> { if !self.newtype_variant { self.output.write_str("()")?; } Ok(()) } fn serialize_unit_struct(self, name: &'static str) -> Result<()> { if self.struct_names() && !self.newtype_variant { self.write_identifier(name)?; Ok(()) } else { self.validate_identifier(name)?; self.serialize_unit() } } fn serialize_unit_variant( self, name: &'static str, _variant_index: u32, variant: &'static str, ) -> Result<()> { self.validate_identifier(name)?; self.write_identifier(variant)?; Ok(()) } fn serialize_newtype_struct(self, name: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { if name == crate::value::raw::RAW_VALUE_TOKEN { let implicit_some_depth = self.implicit_some_depth; self.implicit_some_depth = 0; for _ in 0..implicit_some_depth { self.output.write_str("Some(")?; } guard_recursion! { self => value.serialize(raw::Serializer::new(self)) }?; for _ in 0..implicit_some_depth { self.output.write_char(')')?; } return Ok(()); } if self.extensions().contains(Extensions::UNWRAP_NEWTYPES) || self.newtype_variant { self.newtype_variant = false; self.validate_identifier(name)?; return guard_recursion! { self => value.serialize(&mut *self) }; } if self.struct_names() { self.write_identifier(name)?; } else { self.validate_identifier(name)?; } self.implicit_some_depth = 0; self.output.write_char('(')?; guard_recursion! { self => value.serialize(&mut *self)? }; self.output.write_char(')')?; Ok(()) } fn serialize_newtype_variant( self, name: &'static str, _variant_index: u32, variant: &'static str, value: &T, ) -> Result<()> where T: ?Sized + Serialize, { self.validate_identifier(name)?; self.write_identifier(variant)?; self.output.write_char('(')?; self.newtype_variant = self .extensions() .contains(Extensions::UNWRAP_VARIANT_NEWTYPES); self.implicit_some_depth = 0; guard_recursion! { self => value.serialize(&mut *self)? }; self.newtype_variant = false; self.output.write_char(')')?; Ok(()) } fn serialize_seq(self, len: Option) -> Result { self.newtype_variant = false; self.implicit_some_depth = 0; self.output.write_char('[')?; if !self.compact_arrays() { if let Some(len) = len { self.is_empty = Some(len == 0); } self.start_indent()?; } Ok(Compound::new(self, false)) } fn serialize_tuple(self, len: usize) -> Result { let old_newtype_variant = self.newtype_variant; self.newtype_variant = false; self.implicit_some_depth = 0; if !old_newtype_variant { self.output.write_char('(')?; } if self.separate_tuple_members() { self.is_empty = Some(len == 0); self.start_indent()?; } Ok(Compound::new(self, old_newtype_variant)) } fn serialize_tuple_struct( self, name: &'static str, len: usize, ) -> Result { if self.struct_names() && !self.newtype_variant { self.write_identifier(name)?; } else { self.validate_identifier(name)?; } self.serialize_tuple(len) } fn serialize_tuple_variant( self, name: &'static str, _variant_index: u32, variant: &'static str, len: usize, ) -> Result { self.newtype_variant = false; self.implicit_some_depth = 0; self.validate_identifier(name)?; self.write_identifier(variant)?; self.output.write_char('(')?; if self.separate_tuple_members() { self.is_empty = Some(len == 0); self.start_indent()?; } Ok(Compound::new(self, false)) } fn serialize_map(self, len: Option) -> Result { self.newtype_variant = false; self.implicit_some_depth = 0; self.output.write_char('{')?; if !self.compact_maps() { if let Some(len) = len { self.is_empty = Some(len == 0); } self.start_indent()?; } Ok(Compound::new(self, false)) } fn serialize_struct(self, name: &'static str, len: usize) -> Result { let old_newtype_variant = self.newtype_variant; self.newtype_variant = false; self.implicit_some_depth = 0; if old_newtype_variant { self.validate_identifier(name)?; } else { if self.struct_names() { self.write_identifier(name)?; } else { self.validate_identifier(name)?; } self.output.write_char('(')?; } if !self.compact_structs() { self.is_empty = Some(len == 0); self.start_indent()?; } Ok(Compound::new(self, old_newtype_variant)) } fn serialize_struct_variant( self, name: &'static str, _variant_index: u32, variant: &'static str, len: usize, ) -> Result { self.newtype_variant = false; self.implicit_some_depth = 0; self.validate_identifier(name)?; self.write_identifier(variant)?; self.output.write_char('(')?; if !self.compact_structs() { self.is_empty = Some(len == 0); self.start_indent()?; } Ok(Compound::new(self, false)) } } enum State { First, Rest, } #[doc(hidden)] pub struct Compound<'a, W: fmt::Write> { ser: &'a mut Serializer, state: State, newtype_variant: bool, sequence_index: usize, } impl<'a, W: fmt::Write> Compound<'a, W> { fn new(ser: &'a mut Serializer, newtype_variant: bool) -> Self { Compound { ser, state: State::First, newtype_variant, sequence_index: 0, } } } impl<'a, W: fmt::Write> Drop for Compound<'a, W> { fn drop(&mut self) { if let Some(limit) = &mut self.ser.recursion_limit { *limit = limit.saturating_add(1); } } } impl<'a, W: fmt::Write> ser::SerializeSeq for Compound<'a, W> { type Error = Error; type Ok = (); fn serialize_element(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { if let State::First = self.state { self.state = State::Rest; } else { self.ser.output.write_char(',')?; if let Some((ref config, ref mut pretty)) = self.ser.pretty { if pretty.indent <= config.depth_limit && !config.compact_arrays { self.ser.output.write_str(&config.new_line)?; } else { self.ser.output.write_str(&config.separator)?; } } } if !self.ser.compact_arrays() { self.ser.indent()?; } if let Some((ref mut config, ref mut pretty)) = self.ser.pretty { if pretty.indent <= config.depth_limit && config.enumerate_arrays { write!(self.ser.output, "/*[{}]*/ ", self.sequence_index)?; self.sequence_index += 1; } } guard_recursion! { self.ser => value.serialize(&mut *self.ser)? }; Ok(()) } fn end(self) -> Result<()> { if let State::Rest = self.state { if let Some((ref config, ref mut pretty)) = self.ser.pretty { if pretty.indent <= config.depth_limit && !config.compact_arrays { self.ser.output.write_char(',')?; self.ser.output.write_str(&config.new_line)?; } } } if !self.ser.compact_arrays() { self.ser.end_indent()?; } // seq always disables `self.newtype_variant` self.ser.output.write_char(']')?; Ok(()) } } impl<'a, W: fmt::Write> ser::SerializeTuple for Compound<'a, W> { type Error = Error; type Ok = (); fn serialize_element(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { if let State::First = self.state { self.state = State::Rest; } else { self.ser.output.write_char(',')?; if let Some((ref config, ref pretty)) = self.ser.pretty { if pretty.indent <= config.depth_limit && self.ser.separate_tuple_members() { self.ser.output.write_str(&config.new_line)?; } else { self.ser.output.write_str(&config.separator)?; } } } if self.ser.separate_tuple_members() { self.ser.indent()?; } guard_recursion! { self.ser => value.serialize(&mut *self.ser)? }; Ok(()) } fn end(self) -> Result<()> { if let State::Rest = self.state { if let Some((ref config, ref pretty)) = self.ser.pretty { if self.ser.separate_tuple_members() && pretty.indent <= config.depth_limit { self.ser.output.write_char(',')?; self.ser.output.write_str(&config.new_line)?; } } } if self.ser.separate_tuple_members() { self.ser.end_indent()?; } if !self.newtype_variant { self.ser.output.write_char(')')?; } Ok(()) } } // Same thing but for tuple structs. impl<'a, W: fmt::Write> ser::SerializeTupleStruct for Compound<'a, W> { type Error = Error; type Ok = (); fn serialize_field(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { ser::SerializeTuple::serialize_element(self, value) } fn end(self) -> Result<()> { ser::SerializeTuple::end(self) } } impl<'a, W: fmt::Write> ser::SerializeTupleVariant for Compound<'a, W> { type Error = Error; type Ok = (); fn serialize_field(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { ser::SerializeTuple::serialize_element(self, value) } fn end(self) -> Result<()> { ser::SerializeTuple::end(self) } } impl<'a, W: fmt::Write> ser::SerializeMap for Compound<'a, W> { type Error = Error; type Ok = (); fn serialize_key(&mut self, key: &T) -> Result<()> where T: ?Sized + Serialize, { if let State::First = self.state { self.state = State::Rest; } else { self.ser.output.write_char(',')?; if let Some((ref config, ref pretty)) = self.ser.pretty { if pretty.indent <= config.depth_limit && !config.compact_maps { self.ser.output.write_str(&config.new_line)?; } else { self.ser.output.write_str(&config.separator)?; } } } if !self.ser.compact_maps() { self.ser.indent()?; } guard_recursion! { self.ser => key.serialize(&mut *self.ser) } } fn serialize_value(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { self.ser.output.write_char(':')?; if let Some((ref config, _)) = self.ser.pretty { self.ser.output.write_str(&config.separator)?; } guard_recursion! { self.ser => value.serialize(&mut *self.ser)? }; Ok(()) } fn end(self) -> Result<()> { if let State::Rest = self.state { if let Some((ref config, ref pretty)) = self.ser.pretty { if pretty.indent <= config.depth_limit && !config.compact_maps { self.ser.output.write_char(',')?; self.ser.output.write_str(&config.new_line)?; } } } if !self.ser.compact_maps() { self.ser.end_indent()?; } // map always disables `self.newtype_variant` self.ser.output.write_char('}')?; Ok(()) } } impl<'a, W: fmt::Write> ser::SerializeStruct for Compound<'a, W> { type Error = Error; type Ok = (); fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { let mut restore_field = self.ser.pretty.as_mut().and_then(|(config, _)| { config.path_meta.take().map(|mut field| { if let Some(fields) = field.fields_mut() { config.path_meta = fields.remove(key); } field }) }); if let State::First = self.state { self.state = State::Rest; } else { self.ser.output.write_char(',')?; if let Some((ref config, ref pretty)) = self.ser.pretty { if pretty.indent <= config.depth_limit && !config.compact_structs { self.ser.output.write_str(&config.new_line)?; } else { self.ser.output.write_str(&config.separator)?; } } } if !self.ser.compact_structs() { if let Some((ref config, ref pretty)) = self.ser.pretty { indent(&mut self.ser.output, config, pretty)?; if let Some(ref field) = config.path_meta { for doc_line in field.doc().lines() { self.ser.output.write_str("/// ")?; self.ser.output.write_str(doc_line)?; self.ser.output.write_char('\n')?; indent(&mut self.ser.output, config, pretty)?; } } } } self.ser.write_identifier(key)?; self.ser.output.write_char(':')?; if let Some((ref config, _)) = self.ser.pretty { self.ser.output.write_str(&config.separator)?; } guard_recursion! { self.ser => value.serialize(&mut *self.ser)? }; if let Some((ref mut config, _)) = self.ser.pretty { core::mem::swap(&mut config.path_meta, &mut restore_field); if let Some(ref mut field) = config.path_meta { if let Some(fields) = field.fields_mut() { if let Some(restore_field) = restore_field { fields.insert(key, restore_field); } } } }; Ok(()) } fn end(self) -> Result<()> { if let State::Rest = self.state { if let Some((ref config, ref pretty)) = self.ser.pretty { if pretty.indent <= config.depth_limit && !config.compact_structs { self.ser.output.write_char(',')?; self.ser.output.write_str(&config.new_line)?; } } } if !self.ser.compact_structs() { self.ser.end_indent()?; } if !self.newtype_variant { self.ser.output.write_char(')')?; } Ok(()) } } impl<'a, W: fmt::Write> ser::SerializeStructVariant for Compound<'a, W> { type Error = Error; type Ok = (); fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { ser::SerializeStruct::serialize_field(self, key, value) } fn end(self) -> Result<()> { ser::SerializeStruct::end(self) } } ron-0.12.0/src/ser/path_meta.rs000064400000000000000000000237461046102023000144060ustar 00000000000000//! Path-based metadata to serialize with a value. //! //! Path-based in this context means that the metadata is linked //! to the data in a relative and hierarchical fashion by tracking //! the current absolute path of the field being serialized. //! //! # Example //! //! ``` //! # use ron::ser::{PrettyConfig, path_meta::Field}; //! //! #[derive(serde::Serialize)] //! struct Creature { //! seconds_since_existing: usize, //! linked: Option>, //! } //! //! let mut config = PrettyConfig::default(); //! //! config //! .path_meta //! // The path meta defaults to no root structure, //! // so we either provide a prebuilt one or initialize //! // an empty one to build. //! .get_or_insert_with(Field::empty) //! .build_fields(|fields| { //! fields //! // Get or insert the named field //! .field("seconds_since_existing") //! .with_doc("Outer seconds_since_existing"); //! fields //! .field("linked") //! // Doc metadata is serialized preceded by three forward slashes and a space for each line //! .with_doc("Optional.\nProvide another creature to be wrapped.") //! // Even though it's another Creature, the fields have different paths, so they are addressed separately. //! .build_fields(|fields| { //! fields //! .field("seconds_since_existing") //! .with_doc("Inner seconds_since_existing"); //! }); //! }); //! //! let value = Creature { //! seconds_since_existing: 0, //! linked: Some(Box::new(Creature { //! seconds_since_existing: 0, //! linked: None, //! })), //! }; //! //! let s = ron::ser::to_string_pretty(&value, config).unwrap(); //! //! assert_eq!(s, r#"( //! /// Outer seconds_since_existing //! seconds_since_existing: 0, //! /// Optional. //! /// Provide another creature to be wrapped. //! linked: Some(( //! /// Inner seconds_since_existing //! seconds_since_existing: 0, //! linked: None, //! )), //! )"#); //! ``` //! //! # Identical paths //! //! Especially in enums and tuples it's possible for fields //! to share a path, thus being unable to be addressed separately. //! //! ```no_run //! enum Kind { //! A { //! field: (), //! }, // ^ //! // cannot be addressed separately because they have the same path //! B { // v //! field: (), //! }, //! } //! ``` //! //! ```no_run //! struct A { //! field: (), //! } //! //! struct B { //! field: (), //! } //! //! type Value = ( //! A, //! // ^ //! // These are different types, but they share the path `field` //! // v //! B, //! ); //! ``` use alloc::string::String; use serde_derive::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::collections::HashMap as FieldsInner; #[cfg(not(feature = "std"))] use alloc::collections::BTreeMap as FieldsInner; /// The metadata and inner [`Fields`] of a field. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct Field { doc: String, fields: Option, } impl Field { /// Create a new empty field metadata. #[must_use] pub const fn empty() -> Self { Self { doc: String::new(), fields: None, } } /// Create a new field metadata from parts. pub fn new(doc: impl Into, fields: Option) -> Self { Self { doc: doc.into(), fields, } } /// Get a shared reference to the documentation metadata of this field. #[inline] #[must_use] pub fn doc(&self) -> &str { self.doc.as_str() } /// Get a mutable reference to the documentation metadata of this field. #[inline] #[must_use] pub fn doc_mut(&mut self) -> &mut String { &mut self.doc } /// Set the documentation metadata of this field. /// /// ``` /// # use ron::ser::path_meta::Field; /// /// let mut field = Field::empty(); /// /// assert_eq!(field.doc(), ""); /// /// field.with_doc("some meta"); /// /// assert_eq!(field.doc(), "some meta"); /// ``` pub fn with_doc(&mut self, doc: impl Into) -> &mut Self { self.doc = doc.into(); self } /// Get a shared reference to the inner fields of this field, if it has any. #[must_use] pub fn fields(&self) -> Option<&Fields> { self.fields.as_ref() } /// Get a mutable reference to the inner fields of this field, if it has any. pub fn fields_mut(&mut self) -> Option<&mut Fields> { self.fields.as_mut() } /// Return whether this field has inner fields. /// /// ``` /// # use ron::ser::path_meta::{Field, Fields}; /// /// let mut field = Field::empty(); /// /// assert!(!field.has_fields()); /// /// field.with_fields(Some(Fields::default())); /// /// assert!(field.has_fields()); /// ``` #[must_use] pub fn has_fields(&self) -> bool { self.fields.is_some() } /// Set the inner fields of this field. /// /// ``` /// # use ron::ser::path_meta::{Field, Fields}; /// /// let mut field = Field::empty(); /// /// assert!(!field.has_fields()); /// /// field.with_fields(Some(Fields::default())); /// /// assert!(field.has_fields()); /// /// field.with_fields(None); /// /// assert!(!field.has_fields()); /// ``` pub fn with_fields(&mut self, fields: Option) -> &mut Self { self.fields = fields; self } /// Ergonomic shortcut for building some inner fields. /// /// ``` /// # use ron::ser::path_meta::Field; /// /// let mut field = Field::empty(); /// /// field.build_fields(|fields| { /// fields.field("inner field"); /// }); /// /// assert_eq!(field.fields().map(|fields| fields.contains("inner field")), Some(true)); /// ``` pub fn build_fields(&mut self, builder: impl FnOnce(&mut Fields)) -> &mut Self { let mut fields = Fields::default(); builder(&mut fields); self.with_fields(Some(fields)); self } } /// Mapping of names to [`Field`]s. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct Fields { fields: FieldsInner, } impl Fields { /// Return a new, empty metadata field map. #[must_use] pub fn new() -> Self { Self::default() } /// Return whether this field map contains no fields. /// /// ``` /// # use ron::ser::path_meta::{Fields, Field}; /// /// let mut fields = Fields::default(); /// /// assert!(fields.is_empty()); /// /// fields.insert("", Field::empty()); /// /// assert!(!fields.is_empty()); /// ``` #[must_use] pub fn is_empty(&self) -> bool { self.fields.is_empty() } /// Return whether this field map contains a field with the given name. /// /// ``` /// # use ron::ser::path_meta::{Fields, Field}; /// /// let fields: Fields = [("a thing", Field::empty())].into_iter().collect(); /// /// assert!(fields.contains("a thing")); /// assert!(!fields.contains("not a thing")); /// ``` pub fn contains(&self, name: impl AsRef) -> bool { self.fields.contains_key(name.as_ref()) } /// Get a reference to the field with the provided `name`, if it exists. /// /// ``` /// # use ron::ser::path_meta::{Fields, Field}; /// /// let fields: Fields = [("a thing", Field::empty())].into_iter().collect(); /// /// assert!(fields.get("a thing").is_some()); /// assert!(fields.get("not a thing").is_none()); /// ``` pub fn get(&self, name: impl AsRef) -> Option<&Field> { self.fields.get(name.as_ref()) } /// Get a mutable reference to the field with the provided `name`, if it exists. /// /// ``` /// # use ron::ser::path_meta::{Fields, Field}; /// /// let mut fields: Fields = [("a thing", Field::empty())].into_iter().collect(); /// /// assert!(fields.get_mut("a thing").is_some()); /// assert!(fields.get_mut("not a thing").is_none()); /// ``` pub fn get_mut(&mut self, name: impl AsRef) -> Option<&mut Field> { self.fields.get_mut(name.as_ref()) } /// Insert a field with the given name into the map. /// /// ``` /// # use ron::ser::path_meta::{Fields, Field}; /// /// let mut fields = Fields::default(); /// /// assert!(fields.insert("field", Field::empty()).is_none()); /// assert!(fields.insert("field", Field::empty()).is_some()); /// ``` pub fn insert(&mut self, name: impl Into, field: Field) -> Option { self.fields.insert(name.into(), field) } /// Remove a field with the given name from the map. /// /// ``` /// # use ron::ser::path_meta::{Fields, Field}; /// /// let mut fields: Fields = [("a", Field::empty())].into_iter().collect(); /// /// assert_eq!(fields.remove("a"), Some(Field::empty())); /// assert_eq!(fields.remove("a"), None); /// ``` pub fn remove(&mut self, name: impl AsRef) -> Option { self.fields.remove(name.as_ref()) } /// Get a mutable reference to the field with the provided `name`, /// inserting an empty [`Field`] if it didn't exist. /// /// ``` /// # use ron::ser::path_meta::Fields; /// /// let mut fields = Fields::default(); /// /// assert!(!fields.contains("thing")); /// /// fields.field("thing"); /// /// assert!(fields.contains("thing")); /// ``` pub fn field(&mut self, name: impl Into) -> &mut Field { self.fields.entry(name.into()).or_insert_with(Field::empty) } } impl> FromIterator<(K, Field)> for Fields { fn from_iter>(iter: T) -> Self { Self { fields: iter.into_iter().map(|(k, v)| (k.into(), v)).collect(), } } } ron-0.12.0/src/ser/raw.rs000064400000000000000000000166441046102023000132340ustar 00000000000000use core::fmt; use serde::{ser, Serialize}; use super::{Error, Result}; pub struct Serializer<'a, W: fmt::Write> { ser: &'a mut super::Serializer, } impl<'a, W: fmt::Write> Serializer<'a, W> { pub fn new(ser: &'a mut super::Serializer) -> Self { Self { ser } } } impl<'a, W: fmt::Write> ser::Serializer for Serializer<'a, W> { type Error = Error; type Ok = (); type SerializeMap = ser::Impossible<(), Error>; type SerializeSeq = ser::Impossible<(), Error>; type SerializeStruct = ser::Impossible<(), Error>; type SerializeStructVariant = ser::Impossible<(), Error>; type SerializeTuple = ser::Impossible<(), Error>; type SerializeTupleStruct = ser::Impossible<(), Error>; type SerializeTupleVariant = ser::Impossible<(), Error>; fn serialize_bool(self, _: bool) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_i8(self, _: i8) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_i16(self, _: i16) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_i32(self, _: i32) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_i64(self, _: i64) -> Result<()> { Err(Error::ExpectedRawValue) } #[cfg(feature = "integer128")] fn serialize_i128(self, _: i128) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_u8(self, _: u8) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_u16(self, _: u16) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_u32(self, _: u32) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_u64(self, _: u64) -> Result<()> { Err(Error::ExpectedRawValue) } #[cfg(feature = "integer128")] fn serialize_u128(self, _: u128) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_f32(self, _: f32) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_f64(self, _: f64) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_char(self, _: char) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_str(self, ron: &str) -> Result<()> { self.ser.output.write_str(ron)?; Ok(()) } fn serialize_bytes(self, _: &[u8]) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_none(self) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_some(self, _: &T) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_unit(self) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_unit_struct(self, _: &'static str) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_unit_variant(self, _: &'static str, _: u32, _: &'static str) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_newtype_struct(self, _: &'static str, _: &T) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_newtype_variant( self, _: &'static str, _: u32, _: &'static str, _: &T, ) -> Result<()> { Err(Error::ExpectedRawValue) } fn serialize_seq(self, _: Option) -> Result { Err(Error::ExpectedRawValue) } fn serialize_tuple(self, _: usize) -> Result { Err(Error::ExpectedRawValue) } fn serialize_tuple_struct( self, _: &'static str, _: usize, ) -> Result { Err(Error::ExpectedRawValue) } fn serialize_tuple_variant( self, _: &'static str, _: u32, _: &'static str, _: usize, ) -> Result { Err(Error::ExpectedRawValue) } fn serialize_map(self, _: Option) -> Result { Err(Error::ExpectedRawValue) } fn serialize_struct(self, _: &'static str, _: usize) -> Result { Err(Error::ExpectedRawValue) } fn serialize_struct_variant( self, _: &'static str, _: u32, _: &'static str, _: usize, ) -> Result { Err(Error::ExpectedRawValue) } } #[cfg(test)] mod tests { macro_rules! test_non_raw_value { ($test_name:ident => $serialize_method:ident($($serialize_param:expr),*)) => { #[test] fn $test_name() { use serde::{Serialize, Serializer}; struct Inner; impl Serialize for Inner { fn serialize(&self, serializer: S) -> Result { serializer.$serialize_method($($serialize_param),*).map(|_| unreachable!()) } } #[derive(Debug)] struct Newtype; impl Serialize for Newtype { fn serialize(&self, serializer: S) -> Result { serializer.serialize_newtype_struct( crate::value::raw::RAW_VALUE_TOKEN, &Inner, ) } } assert_eq!( crate::to_string(&Newtype).unwrap_err(), crate::Error::ExpectedRawValue ) } }; } test_non_raw_value! { test_bool => serialize_bool(false) } test_non_raw_value! { test_i8 => serialize_i8(0) } test_non_raw_value! { test_i16 => serialize_i16(0) } test_non_raw_value! { test_i32 => serialize_i32(0) } test_non_raw_value! { test_i64 => serialize_i64(0) } #[cfg(feature = "integer128")] test_non_raw_value! { test_i128 => serialize_i128(0) } test_non_raw_value! { test_u8 => serialize_u8(0) } test_non_raw_value! { test_u16 => serialize_u16(0) } test_non_raw_value! { test_u32 => serialize_u32(0) } test_non_raw_value! { test_u64 => serialize_u64(0) } #[cfg(feature = "integer128")] test_non_raw_value! { test_u128 => serialize_u128(0) } test_non_raw_value! { test_f32 => serialize_f32(0.0) } test_non_raw_value! { test_f64 => serialize_f64(0.0) } test_non_raw_value! { test_char => serialize_char('\0') } test_non_raw_value! { test_bytes => serialize_bytes(b"") } test_non_raw_value! { test_none => serialize_none() } test_non_raw_value! { test_some => serialize_some(&()) } test_non_raw_value! { test_unit => serialize_unit() } test_non_raw_value! { test_unit_struct => serialize_unit_struct("U") } test_non_raw_value! { test_unit_variant => serialize_unit_variant("E", 0, "U") } test_non_raw_value! { test_newtype_struct => serialize_newtype_struct("N", &()) } test_non_raw_value! { test_newtype_variant => serialize_newtype_variant("E", 0, "N", &()) } test_non_raw_value! { test_seq => serialize_seq(None) } test_non_raw_value! { test_tuple => serialize_tuple(0) } test_non_raw_value! { test_tuple_struct => serialize_tuple_struct("T", 0) } test_non_raw_value! { test_tuple_variant => serialize_tuple_variant("E", 0, "T", 0) } test_non_raw_value! { test_map => serialize_map(None) } test_non_raw_value! { test_struct => serialize_struct("S", 0) } test_non_raw_value! { test_struct_variant => serialize_struct_variant("E", 0, "S", 0) } } ron-0.12.0/src/ser/tests.rs000064400000000000000000000205021046102023000135710ustar 00000000000000use alloc::{format, vec}; use serde_derive::Serialize; use crate::Number; #[derive(Serialize)] struct EmptyStruct1; #[derive(Serialize)] struct EmptyStruct2 {} #[derive(Serialize)] struct NewType(i32); #[derive(Serialize)] struct TupleStruct(f32, f32); #[derive(Serialize)] struct MyStruct { x: f32, y: f32, } #[derive(Serialize)] enum MyEnum { A, B(bool), C(bool, f32), D { a: i32, b: i32 }, } #[test] fn test_empty_struct() { check_to_string_writer(&EmptyStruct1, "()", "EmptyStruct1"); check_to_string_writer(&EmptyStruct2 {}, "()", "EmptyStruct2()"); } #[test] fn test_struct() { let my_struct = MyStruct { x: 4.0, y: 7.0 }; check_to_string_writer(&my_struct, "(x:4.0,y:7.0)", "MyStruct(x: 4.0, y: 7.0)"); check_to_string_writer(&NewType(42), "(42)", "NewType(42)"); check_to_string_writer(&TupleStruct(2.0, 5.0), "(2.0,5.0)", "TupleStruct(2.0, 5.0)"); } #[test] fn test_option() { check_to_string_writer(&Some(1u8), "Some(1)", "Some(1)"); check_to_string_writer(&None::, "None", "None"); } #[test] fn test_enum() { check_to_string_writer(&MyEnum::A, "A", "A"); check_to_string_writer(&MyEnum::B(true), "B(true)", "B(true)"); check_to_string_writer(&MyEnum::C(true, 3.5), "C(true,3.5)", "C(true, 3.5)"); check_to_string_writer(&MyEnum::D { a: 2, b: 3 }, "D(a:2,b:3)", "D(a: 2, b: 3)"); } #[test] fn test_array() { let empty: [i32; 0] = []; check_to_string_writer(&empty, "()", "()"); let empty_ref: &[i32] = ∅ check_to_string_writer(&empty_ref, "[]", "[]"); check_to_string_writer(&[2, 3, 4i32], "(2,3,4)", "(2, 3, 4)"); check_to_string_writer( &(&[2, 3, 4i32] as &[i32]), "[2,3,4]", "[\n 2,\n 3,\n 4,\n]", ); } #[test] fn test_slice() { check_to_string_writer( &[0, 1, 2, 3, 4, 5][..], "[0,1,2,3,4,5]", "[\n 0,\n 1,\n 2,\n 3,\n 4,\n 5,\n]", ); check_to_string_writer( &[0, 1, 2, 3, 4, 5][1..4], "[1,2,3]", "[\n 1,\n 2,\n 3,\n]", ); } #[test] fn test_vec() { check_to_string_writer( &vec![0, 1, 2, 3, 4, 5], "[0,1,2,3,4,5]", "[\n 0,\n 1,\n 2,\n 3,\n 4,\n 5,\n]", ); } #[test] fn test_map() { use alloc::collections::BTreeMap; let mut map = BTreeMap::new(); map.insert((true, false), 4); map.insert((false, false), 123); check_to_string_writer( &map, "{(false,false):123,(true,false):4}", "{\n (false, false): 123,\n (true, false): 4,\n}", ); } #[test] fn test_string() { check_to_string_writer(&"Some string", "\"Some string\"", "\"Some string\""); } #[test] fn test_char() { check_to_string_writer(&'c', "'c'", "'c'"); } #[test] fn test_escape() { check_to_string_writer(&r#""Quoted""#, r#""\"Quoted\"""#, r#""\"Quoted\"""#); } #[test] fn test_byte_stream() { use serde_bytes; let small: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; check_to_string_writer( &small, "(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)", "(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)", ); let large = vec![0x01, 0x02, 0x03, 0x04]; let large = serde_bytes::Bytes::new(&large); check_to_string_writer( &large, "b\"\\x01\\x02\\x03\\x04\"", "b\"\\x01\\x02\\x03\\x04\"", ); let large = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06]; let large = serde_bytes::Bytes::new(&large); check_to_string_writer( &large, "b\"\\x01\\x02\\x03\\x04\\x05\\x06\"", "b\"\\x01\\x02\\x03\\x04\\x05\\x06\"", ); let large = vec![255u8; 64]; let large = serde_bytes::Bytes::new(&large); check_to_string_writer( &large, concat!( "b\"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff", "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff", "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff", "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff", "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\"" ), concat!( "b\"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff", "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff", "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff", "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff", "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\"" ), ); } #[test] fn rename() { #[derive(Serialize, Debug, PartialEq)] enum Foo { #[serde(rename = "2d")] D2, #[serde(rename = "triangle-list")] TriangleList, } check_to_string_writer(&Foo::D2, "r#2d", "r#2d"); check_to_string_writer(&Foo::TriangleList, "r#triangle-list", "r#triangle-list"); } #[test] fn test_any_number_precision() { check_ser_any_number(1_u8); check_ser_any_number(-1_i8); check_ser_any_number(1_f32); check_ser_any_number(-1_f32); check_ser_any_number(0.3_f64); check_to_string_writer(&Number::new(f32::NAN), "NaN", "NaN"); check_to_string_writer(&f32::NAN, "NaN", "NaN"); check_to_string_writer(&Number::new(-f32::NAN), "-NaN", "-NaN"); check_to_string_writer(&(-f32::NAN), "-NaN", "-NaN"); check_to_string_writer(&Number::new(f32::INFINITY), "inf", "inf"); check_to_string_writer(&f32::INFINITY, "inf", "inf"); check_to_string_writer(&Number::new(f32::NEG_INFINITY), "-inf", "-inf"); check_to_string_writer(&f32::NEG_INFINITY, "-inf", "-inf"); macro_rules! test_min_max { ($ty:ty) => { check_ser_any_number(<$ty>::MIN); check_ser_any_number(<$ty>::MAX); }; ($($ty:ty),*) => { $(test_min_max! { $ty })* }; } test_min_max! { i8, i16, i32, i64, u8, u16, u32, u64, f32, f64 } #[cfg(feature = "integer128")] test_min_max! { i128, u128 } } fn check_ser_any_number + core::fmt::Display + serde::Serialize>(n: T) { let mut fmt = format!("{}", n); if !fmt.contains('.') && core::any::type_name::().contains('f') { fmt.push_str(".0"); } check_to_string_writer(&n.into(), &fmt, &fmt); check_to_string_writer(&n, &fmt, &fmt); } #[test] fn recursion_limit() { assert_eq!( crate::Options::default() .with_recursion_limit(0) .to_string(&[42]), Err(crate::Error::ExceededRecursionLimit), ); assert_eq!( crate::Options::default() .with_recursion_limit(1) .to_string(&[42]) .as_deref(), Ok("(42)"), ); assert_eq!( crate::Options::default() .without_recursion_limit() .to_string(&[42]) .as_deref(), Ok("(42)"), ); assert_eq!( crate::Options::default() .with_recursion_limit(1) .to_string(&[[42]]), Err(crate::Error::ExceededRecursionLimit), ); assert_eq!( crate::Options::default() .with_recursion_limit(2) .to_string(&[[42]]) .as_deref(), Ok("((42))"), ); assert_eq!( crate::Options::default() .without_recursion_limit() .to_string(&[[42]]) .as_deref(), Ok("((42))"), ); } fn check_to_string_writer(val: &T, check: &str, check_pretty: &str) { let ron_str = super::to_string(val).unwrap(); assert_eq!(ron_str, check); let ron_str_pretty = super::to_string_pretty( val, super::PrettyConfig::default() .struct_names(true) .compact_structs(true), ) .unwrap(); assert_eq!(ron_str_pretty, check_pretty); #[cfg(feature = "std")] { let mut ron_writer = std::ffi::OsString::new(); super::to_writer(&mut ron_writer, val).unwrap(); assert_eq!(ron_writer, check); let mut ron_writer_pretty = std::ffi::OsString::new(); super::to_writer_pretty( &mut ron_writer_pretty, val, super::PrettyConfig::default() .struct_names(true) .compact_structs(true), ) .unwrap(); assert_eq!(ron_writer_pretty, check_pretty); } } ron-0.12.0/src/ser/value.rs000064400000000000000000000016261046102023000135510ustar 00000000000000use serde::ser::{Serialize, Serializer}; use crate::value::Value; impl Serialize for Value { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { Value::Bool(b) => serializer.serialize_bool(b), Value::Char(c) => serializer.serialize_char(c), Value::Map(ref m) => Serialize::serialize(m, serializer), Value::Number(ref number) => Serialize::serialize(number, serializer), Value::Option(Some(ref o)) => serializer.serialize_some(o.as_ref()), Value::Option(None) => serializer.serialize_none(), Value::String(ref s) => serializer.serialize_str(s), Value::Bytes(ref b) => serializer.serialize_bytes(b), Value::Seq(ref s) => Serialize::serialize(s, serializer), Value::Unit => serializer.serialize_unit(), } } } ron-0.12.0/src/util/mod.rs000064400000000000000000000001111046102023000133640ustar 00000000000000#[cfg(feature = "internal-span-substring-test")] pub mod span_substring; ron-0.12.0/src/util/span_substring.rs000064400000000000000000000240641046102023000156630ustar 00000000000000use crate::{ de::{Position, Span}, error::SpannedResult, }; use alloc::string::String; impl Position { /// Given a Position and a string, return the 0-indexed grapheme index into the /// string at that position, or [None] if the Position is out of bounds of the string. #[must_use] pub fn grapheme_index(&self, s: &str) -> Option { use unicode_segmentation::UnicodeSegmentation; let mut line_no = 1; let mut col_no = 1; if (self.line, self.col) == (1, 1) { return Some(0); } let mut i = 0; // Slightly non-intuitive arithmetic: a zero-length string at line 1, col 1 -> 0 if (line_no, col_no) == (self.line, self.col) { return Some(i); } for ch in s.graphemes(true) { if (line_no, col_no) == (self.line, self.col) { return Some(i); } // "\n" and "\r\n" each come through the iterator as a single grapheme if matches!(ch, "\n" | "\r\n") { line_no += 1; col_no = 1; } else { col_no += 1; } i += 1; } // ...and a string of length 7 at line 1, col 8 -> 7 if (line_no, col_no) == (self.line, self.col) { return Some(i); } None } } impl Span { /// Given a `Span` and a string, form the resulting string selected exclusively (as in `[start..end`]) by the `Span` /// or [`None`] if the span is out of bounds of the string at either end. #[must_use] pub fn substring_exclusive(&self, s: &str) -> Option { use alloc::vec::Vec; use unicode_segmentation::UnicodeSegmentation; if let (Some(start), Some(end)) = (self.start.grapheme_index(s), self.end.grapheme_index(s)) { Some(s.graphemes(true).collect::>()[start..end].concat()) } else { None } } /// Given a `Span` and a string, form the resulting string selected inclusively (as in `[start..=end]`) by the `Span` /// or [`None`] if the span is out of bounds of the string at either end. #[must_use] pub fn substring_inclusive(&self, s: &str) -> Option { use alloc::vec::Vec; use unicode_segmentation::UnicodeSegmentation; if let (Some(start), Some(end)) = (self.start.grapheme_index(s), self.end.grapheme_index(s)) { Some(s.graphemes(true).collect::>()[start..=end].concat()) } else { None } } } /// Given a string `ron`, a [`SpannedResult`], and a substring, verify that trying to parse `ron` results in an error /// equal to the [`SpannedResult`] with a Span that exclusively (as in `[start..end]`) selects that substring. /// Note that there are two versions of this helper, inclusive and exclusive. This is because while the parser cursor /// arithmetic that computes span positions always produces exclusive spans (as in `[start..end]`), /// when doing validation against a target substring, the inclusive check including the final grapheme that triggered /// the error is often a more intuitive target to check against. /// Meanwhile, if the parser threw an EOF, for example, there is no final grapheme to check, and so /// only the exclusive check would produce a meaningful result. #[allow(clippy::unwrap_used)] #[allow(clippy::missing_panics_doc)] pub fn check_error_span_exclusive( ron: &str, check: SpannedResult, substr: &str, ) { let res_str = crate::de::from_str::(ron); assert_eq!(res_str, check); let res_bytes = crate::de::from_bytes::(ron.as_bytes()); assert_eq!(res_bytes, check); #[cfg(feature = "std")] { let res_reader = crate::de::from_reader::<&[u8], T>(ron.as_bytes()); assert_eq!(res_reader, check); } assert_eq!( check.unwrap_err().span.substring_exclusive(ron).unwrap(), substr ); } /// Given a string `ron`, a [`SpannedResult`], and a substring, verify that trying to parse `ron` results in an error /// equal to the [`SpannedResult`] with a Span that inclusively (as in `[start..=end`]) selects that substring. /// See [`check_error_span_exclusive`] for the rationale behind both versions of this helper. #[allow(clippy::unwrap_used)] #[allow(clippy::missing_panics_doc)] pub fn check_error_span_inclusive( ron: &str, check: SpannedResult, substr: &str, ) { let res_str = crate::de::from_str::(ron); assert_eq!(res_str, check); let res_bytes = crate::de::from_bytes::(ron.as_bytes()); assert_eq!(res_bytes, check); #[cfg(feature = "std")] { let res_reader = crate::de::from_reader::<&[u8], T>(ron.as_bytes()); assert_eq!(res_reader, check); } assert_eq!( check.unwrap_err().span.substring_inclusive(ron).unwrap(), substr ); } #[cfg(test)] mod tests { use crate::de::{Position, Span}; fn span(start: Position, end: Position) -> Span { Span { start, end } } fn pos(line: usize, col: usize) -> Position { Position { line, col } } #[test] fn ascii_basics() { let text = "hello\nworld"; // first char / first col assert_eq!(pos(1, 1).grapheme_index(text), Some(0)); // last char on first line ('o') assert_eq!(pos(1, 5).grapheme_index(text), Some(4)); // start of second line ('w') assert_eq!(pos(2, 1).grapheme_index(text), Some(6)); // span across the `\n` assert_eq!( span(pos(1, 4), pos(2, 2)) .substring_exclusive(text) .unwrap(), "lo\nw" ); } #[test] fn multibyte_greek() { let text = "αβγ\ndeux\n三四五\r\nend"; // Beta assert_eq!(pos(1, 2).grapheme_index(text), Some(1)); // 三 assert_eq!(pos(3, 1).grapheme_index(text), Some(9)); // e assert_eq!(pos(4, 1).grapheme_index(text), Some(13)); // span from α to start of “deux” assert_eq!( span(pos(1, 1), pos(2, 1)) .substring_exclusive(text) .unwrap(), "αβγ\n" ); } #[test] fn combining_mark_cluster() { // é == [0x65, 0xCC, 0x81] in UTF-8 let text = "e\u{0301}x\n"; // grapheme #1 (“é”) assert_eq!(pos(1, 1).grapheme_index(text), Some(0)); // grapheme #2 (“x”) assert_eq!(pos(1, 2).grapheme_index(text), Some(1)); // column 4 is past EOL assert_eq!(pos(1, 4).grapheme_index(text), None); // full span assert_eq!( span(pos(1, 1), pos(1, 2)) .substring_exclusive(text) .unwrap(), "e\u{0301}" ); } #[test] fn zwj_emoji_cluster() { let text = "👩‍👩‍👧‍👧 and 👨‍👩‍👦"; // The family emoji is the first grapheme on the line. assert_eq!(pos(1, 1).grapheme_index(text), Some(0)); assert_eq!(pos(1, 2).grapheme_index(text), Some(1)); // Span selecting only the first emoji assert_eq!( span(pos(1, 1), pos(1, 2)) .substring_exclusive(text) .unwrap(), "👩‍👩‍👧‍👧" ); // Span selecting only the second emoji assert_eq!( span(pos(1, 7), pos(1, 8)) .substring_exclusive(text) .unwrap(), "👨‍👩‍👦" ); } #[test] fn mixed_newlines() { let text = "one\r\ntwo\nthree\r\n"; // start of “two” (line numbers are 1-based) assert_eq!(pos(2, 1).grapheme_index(text), Some(4)); // “three” assert_eq!(pos(3, 1).grapheme_index(text), Some(8)); // span “two\n” assert_eq!( span(pos(2, 1), pos(3, 1)) .substring_exclusive(text) .unwrap(), "two\n" ); // span “two\nthree” assert_eq!( span(pos(2, 1), pos(3, 6)) .substring_exclusive(text) .unwrap(), "two\nthree" ); } #[test] fn oob_and_error_paths() { let text = "short"; // line past EOF assert_eq!(pos(2, 1).grapheme_index(text), None); // column past EOL assert_eq!(pos(1, 10).grapheme_index(text), None); // span with either endpoint oob → None assert_eq!(span(pos(1, 1), pos(2, 1)).substring_exclusive(text), None); } #[test] fn whole_text_span() { let text = "αβγ\nδεζ"; let all = span(pos(1, 1), pos(2, 4)); assert_eq!(&all.substring_exclusive(text).unwrap(), text); } #[test] fn span_substring_helper() { assert_eq!( Span { start: Position { line: 1, col: 1 }, end: Position { line: 2, col: 1 }, } .substring_exclusive( "In the first place, there are two sorts of bets, or toh.11 There is the single axial bet in the center between the principals (toh ketengah), and there is the cloud of peripheral ones around the ring between members of the audience (toh kesasi). ", ) .unwrap(), "In the first place, there are two sorts of bets, or toh.11 There is the\n" ); assert_eq!( Span { start: Position { line: 2, col: 1 }, end: Position { line: 3, col: 1 }, } .substring_exclusive( "In the first place, there are two sorts of bets, or toh.11 There is the single axial bet in the center between the principals (toh ketengah), and there is the cloud of peripheral ones around the ring between members of the audience (toh kesasi). ", ) .unwrap(), "single axial bet in the center between the principals (toh ketengah), and\n" ); } } ron-0.12.0/src/value/map.rs000064400000000000000000000177571046102023000135510ustar 00000000000000use core::{ cmp::{Eq, Ordering}, hash::{Hash, Hasher}, iter::FromIterator, ops::{Index, IndexMut}, }; use serde_derive::{Deserialize, Serialize}; use super::Value; /// A [`Value`] to [`Value`] map. /// /// This structure either uses a [`BTreeMap`](alloc::collections::BTreeMap) or the /// [`IndexMap`](indexmap::IndexMap) internally. /// The latter can be used by enabling the `indexmap` feature. This can be used /// to preserve the order of the parsed map. #[derive(Clone, Debug, Default, Deserialize, Serialize)] #[serde(transparent)] pub struct Map(pub(crate) MapInner); #[cfg(not(feature = "indexmap"))] type MapInner = alloc::collections::BTreeMap; #[cfg(feature = "indexmap")] type MapInner = indexmap::IndexMap; impl Map { /// Creates a new, empty [`Map`]. #[must_use] pub fn new() -> Self { Self::default() } /// Returns the number of elements in the map. #[must_use] pub fn len(&self) -> usize { self.0.len() } /// Returns `true` if `self.len() == 0`, `false` otherwise. #[must_use] pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Immutably looks up an element by its `key`. #[must_use] pub fn get(&self, key: &Value) -> Option<&Value> { self.0.get(key) } /// Mutably looks up an element by its `key`. pub fn get_mut(&mut self, key: &Value) -> Option<&mut Value> { self.0.get_mut(key) } /// Inserts a new element, returning the previous element with this `key` if /// there was any. pub fn insert(&mut self, key: impl Into, value: impl Into) -> Option { self.0.insert(key.into(), value.into()) } /// Removes an element by its `key`. pub fn remove(&mut self, key: &Value) -> Option { #[cfg(feature = "indexmap")] { self.0.shift_remove(key) } #[cfg(not(feature = "indexmap"))] { self.0.remove(key) } } /// Iterate all key-value pairs. #[must_use] pub fn iter(&self) -> impl DoubleEndedIterator { self.0.iter() } /// Iterate all key-value pairs mutably. #[must_use] pub fn iter_mut(&mut self) -> impl DoubleEndedIterator { self.0.iter_mut() } /// Iterate all keys. #[must_use] pub fn keys(&self) -> impl DoubleEndedIterator { self.0.keys() } /// Iterate all values. #[must_use] pub fn values(&self) -> impl DoubleEndedIterator { self.0.values() } /// Iterate all values mutably. #[must_use] pub fn values_mut(&mut self) -> impl DoubleEndedIterator { self.0.values_mut() } /// Retains only the elements specified by the `keep` predicate. /// /// In other words, remove all pairs `(k, v)` for which `keep(&k, &mut v)` /// returns `false`. /// /// The elements are visited in iteration order. pub fn retain(&mut self, keep: F) where F: FnMut(&Value, &mut Value) -> bool, { self.0.retain(keep); } } impl Index<&Value> for Map { type Output = Value; #[allow(clippy::expect_used)] fn index(&self, index: &Value) -> &Self::Output { self.get(index).expect("no entry found for key") } } impl IndexMut<&Value> for Map { #[allow(clippy::expect_used)] fn index_mut(&mut self, index: &Value) -> &mut Self::Output { self.get_mut(index).expect("no entry found for key") } } impl IntoIterator for Map { type Item = (Value, Value); type IntoIter = ::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } impl, V: Into> FromIterator<(K, V)> for Map { fn from_iter>(iter: T) -> Self { Map(iter .into_iter() .map(|(key, value)| (key.into(), value.into())) .collect()) } } /// Note: equality is only given if both values and order of values match impl PartialEq for Map { fn eq(&self, other: &Map) -> bool { self.cmp(other).is_eq() } } /// Note: equality is only given if both values and order of values match impl Eq for Map {} impl PartialOrd for Map { fn partial_cmp(&self, other: &Map) -> Option { Some(self.cmp(other)) } } impl Ord for Map { fn cmp(&self, other: &Map) -> Ordering { self.iter().cmp(other.iter()) } } impl Hash for Map { fn hash(&self, state: &mut H) { self.iter().for_each(|x| x.hash(state)); } } #[cfg(test)] mod tests { use alloc::{vec, vec::Vec}; use super::{Map, Value}; #[test] fn map_usage() { let mut map = Map::new(); assert_eq!(map.len(), 0); assert!(map.is_empty()); map.insert("a", 42); assert_eq!(map.len(), 1); assert!(!map.is_empty()); assert_eq!(map.keys().collect::>(), vec![&Value::from("a")]); assert_eq!(map.values().collect::>(), vec![&Value::from(42)]); assert_eq!( map.iter().collect::>(), vec![(&Value::from("a"), &Value::from(42))] ); assert_eq!(map.get(&Value::from("a")), Some(&Value::from(42))); assert_eq!(map.get(&Value::from("b")), None); assert_eq!(map.get_mut(&Value::from("a")), Some(&mut Value::from(42))); assert_eq!(map.get_mut(&Value::from("b")), None); map[&Value::from("a")] = Value::from(24); assert_eq!(&map[&Value::from("a")], &Value::from(24)); for (key, value) in map.iter_mut() { if key == &Value::from("a") { *value = Value::from(42); } } assert_eq!(&map[&Value::from("a")], &Value::from(42)); map.values_mut().for_each(|value| *value = Value::from(24)); assert_eq!(&map[&Value::from("a")], &Value::from(24)); map.insert("b", 42); assert_eq!(map.len(), 2); assert!(!map.is_empty()); assert_eq!(map.get(&Value::from("a")), Some(&Value::from(24))); assert_eq!(map.get(&Value::from("b")), Some(&Value::from(42))); map.retain(|key, value| { if key == &Value::from("a") { *value = Value::from(42); true } else { false } }); assert_eq!(map.len(), 1); assert_eq!(map.get(&Value::from("a")), Some(&Value::from(42))); assert_eq!(map.get(&Value::from("b")), None); assert_eq!(map.remove(&Value::from("b")), None); assert_eq!(map.remove(&Value::from("a")), Some(Value::from(42))); assert_eq!(map.remove(&Value::from("a")), None); } #[cfg(feature = "std")] #[test] fn map_hash() { assert_same_hash(&Map::new(), &Map::new()); assert_same_hash( &[("a", 42)].into_iter().collect(), &[("a", 42)].into_iter().collect(), ); assert_same_hash( &[("b", 24), ("c", 42)].into_iter().collect(), &[("b", 24), ("c", 42)].into_iter().collect(), ); } #[cfg(feature = "std")] fn assert_same_hash(a: &Map, b: &Map) { use core::hash::{Hash, Hasher}; use std::collections::hash_map::DefaultHasher; assert_eq!(a, b); assert!(a.cmp(b).is_eq()); assert_eq!(a.partial_cmp(b), Some(core::cmp::Ordering::Equal)); let mut hasher = DefaultHasher::new(); a.hash(&mut hasher); let h1 = hasher.finish(); let mut hasher = DefaultHasher::new(); b.hash(&mut hasher); let h2 = hasher.finish(); assert_eq!(h1, h2); } #[test] #[should_panic(expected = "no entry found for key")] fn map_index_panic() { let _ = &Map::new()[&Value::Unit]; } #[test] #[should_panic(expected = "no entry found for key")] fn map_index_mut_panic() { let _ = &mut Map::new()[&Value::Unit]; } } ron-0.12.0/src/value/mod.rs000064400000000000000000000331551046102023000135410ustar 00000000000000//! Value module. use alloc::{borrow::Cow, boxed::Box, format, string::String, vec::Vec}; use core::{cmp::Eq, hash::Hash}; use serde::{ de::{DeserializeOwned, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor}, forward_to_deserialize_any, }; use crate::{de::Error, error::Result}; mod map; mod number; pub(crate) mod raw; pub use map::Map; pub use number::{Number, F32, F64}; #[allow(clippy::useless_attribute, clippy::module_name_repetitions)] pub use raw::RawValue; #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum Value { Bool(bool), Char(char), Map(Map), Number(Number), Option(Option>), String(String), Bytes(Vec), Seq(Vec), Unit, } impl From for Value { fn from(value: bool) -> Self { Self::Bool(value) } } impl From for Value { fn from(value: char) -> Self { Self::Char(value) } } impl, V: Into> FromIterator<(K, V)> for Value { fn from_iter>(iter: T) -> Self { Self::Map(iter.into_iter().collect()) } } impl From for Value { fn from(value: Map) -> Self { Self::Map(value) } } impl> From for Value { fn from(value: T) -> Self { Self::Number(value.into()) } } impl> From> for Value { fn from(value: Option) -> Self { Self::Option(value.map(Into::into).map(Box::new)) } } impl<'a> From<&'a str> for Value { fn from(value: &'a str) -> Self { String::from(value).into() } } impl<'a> From> for Value { fn from(value: Cow<'a, str>) -> Self { String::from(value).into() } } impl From for Value { fn from(value: String) -> Self { Self::String(value) } } /// Special case to allow `Value::from(b"byte string")` impl From<&'static [u8; N]> for Value { fn from(value: &'static [u8; N]) -> Self { Self::Bytes(Vec::from(*value)) } } impl> FromIterator for Value { fn from_iter>(iter: I) -> Self { Self::Seq(iter.into_iter().map(Into::into).collect()) } } impl<'a, T: Clone + Into> From<&'a [T]> for Value { fn from(value: &'a [T]) -> Self { value.iter().map(Clone::clone).map(Into::into).collect() } } impl> From> for Value { fn from(value: Vec) -> Self { value.into_iter().collect() } } impl From<()> for Value { fn from(_value: ()) -> Self { Value::Unit } } impl Value { /// Tries to deserialize this [`Value`] into `T`. pub fn into_rust(self) -> Result where T: DeserializeOwned, { T::deserialize(self) } } /// Deserializer implementation for RON [`Value`]. /// This does not support enums (because [`Value`] does not store them). impl<'de> Deserializer<'de> for Value { type Error = Error; forward_to_deserialize_any! { bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } #[cfg(feature = "integer128")] forward_to_deserialize_any! { i128 u128 } fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { match self { Value::Bool(b) => visitor.visit_bool(b), Value::Char(c) => visitor.visit_char(c), Value::Map(m) => { let old_len = m.len(); let mut items: Vec<(Value, Value)> = m.into_iter().collect(); items.reverse(); let value = visitor.visit_map(MapAccessor { items: &mut items, value: None, })?; if items.is_empty() { Ok(value) } else { Err(Error::ExpectedDifferentLength { expected: format!("a map of length {}", old_len - items.len()), found: old_len, }) } } Value::Number(number) => number.visit(visitor), Value::Option(Some(o)) => visitor.visit_some(*o), Value::Option(None) => visitor.visit_none(), Value::String(s) => visitor.visit_string(s), Value::Bytes(b) => visitor.visit_byte_buf(b), Value::Seq(mut seq) => { let old_len = seq.len(); seq.reverse(); let value = visitor.visit_seq(SeqAccessor { seq: &mut seq })?; if seq.is_empty() { Ok(value) } else { Err(Error::ExpectedDifferentLength { expected: format!("a sequence of length {}", old_len - seq.len()), found: old_len, }) } } Value::Unit => visitor.visit_unit(), } } } struct SeqAccessor<'a> { seq: &'a mut Vec, } impl<'a, 'de> SeqAccess<'de> for SeqAccessor<'a> { type Error = Error; fn next_element_seed(&mut self, seed: T) -> Result> where T: DeserializeSeed<'de>, { // The `Vec` is reversed, so we can pop to get the originally first element self.seq .pop() .map_or(Ok(None), |v| seed.deserialize(v).map(Some)) } fn size_hint(&self) -> Option { Some(self.seq.len()) } } struct MapAccessor<'a> { items: &'a mut Vec<(Value, Value)>, value: Option, } impl<'a, 'de> MapAccess<'de> for MapAccessor<'a> { type Error = Error; fn next_key_seed(&mut self, seed: K) -> Result> where K: DeserializeSeed<'de>, { // The `Vec` is reversed, so we can pop to get the originally first element match self.items.pop() { Some((key, value)) => { self.value = Some(value); seed.deserialize(key).map(Some) } None => Ok(None), } } #[allow(clippy::panic)] fn next_value_seed(&mut self, seed: V) -> Result where V: DeserializeSeed<'de>, { match self.value.take() { Some(value) => seed.deserialize(value), None => panic!("Contract violation: value before key"), } } fn size_hint(&self) -> Option { Some(self.items.len()) } } #[cfg(test)] mod tests { use alloc::{collections::BTreeMap, vec}; use core::fmt::Debug; use serde::Deserialize; use super::*; fn assert_same<'de, T>(s: &'de str) where T: Debug + Deserialize<'de> + PartialEq, { use crate::de::from_str; let direct: T = from_str(s).unwrap(); let value: Value = from_str(s).unwrap(); let de = T::deserialize(value.clone()).unwrap(); assert_eq!(direct, de, "Deserialization for {:?} is not the same", s); let value_roundtrip = Value::deserialize(value.clone()).unwrap(); assert_eq!(value_roundtrip, value); } fn assert_same_bytes<'de, T>(s: &'de [u8]) where T: Debug + Deserialize<'de> + PartialEq, { use crate::de::from_bytes; let direct: T = from_bytes(s).unwrap(); let value: Value = from_bytes(s).unwrap(); let de = T::deserialize(value.clone()).unwrap(); assert_eq!(direct, de, "Deserialization for {:?} is not the same", s); let value_roundtrip = Value::deserialize(value.clone()).unwrap(); assert_eq!(value_roundtrip, value); } #[test] fn boolean() { assert_same::("true"); assert_same::("false"); assert_eq!(Value::from(true), Value::Bool(true)); assert_eq!(Value::from(false), Value::Bool(false)); } #[test] fn float() { assert_same::("0.123"); assert_same::("-4.19"); assert_eq!( Value::from(42_f32), Value::Number(Number::F32(42_f32.into())) ); assert_eq!( Value::from(42_f64), Value::Number(Number::F64(42_f64.into())) ); } #[test] fn int() { assert_same::("626"); assert_same::("-50"); assert_eq!(Value::from(0_i8), Value::Number(Number::I8(0))); assert_eq!(Value::from(0_i16), Value::Number(Number::I16(0))); assert_eq!(Value::from(0_i32), Value::Number(Number::I32(0))); assert_eq!(Value::from(0_i64), Value::Number(Number::I64(0))); #[cfg(feature = "integer128")] assert_eq!(Value::from(0_i128), Value::Number(Number::I128(0))); assert_eq!(Value::from(0_u8), Value::Number(Number::U8(0))); assert_eq!(Value::from(0_u16), Value::Number(Number::U16(0))); assert_eq!(Value::from(0_u32), Value::Number(Number::U32(0))); assert_eq!(Value::from(0_u64), Value::Number(Number::U64(0))); #[cfg(feature = "integer128")] assert_eq!(Value::from(0_u128), Value::Number(Number::U128(0))); } #[test] fn char() { assert_same::("'4'"); assert_same::("'c'"); assert_eq!(Value::from('🦀'), Value::Char('🦀')); } #[test] fn string() { assert_same::(r#""hello world""#); assert_same::(r#""this is a Rusty 🦀 string""#); assert_same::(r#""this is now valid UTF-8 \xf0\x9f\xa6\x80""#); assert_eq!(Value::from("slice"), Value::String(String::from("slice"))); assert_eq!( Value::from(String::from("string")), Value::String(String::from("string")) ); assert_eq!( Value::from(Cow::Borrowed("cow")), Value::String(String::from("cow")) ); } #[test] fn bytes() { assert_same_bytes::(br#"b"hello world""#); assert_same_bytes::( br#"b"this is not valid UTF-8 \xf8\xa1\xa1\xa1\xa1""#, ); assert_eq!(Value::from(b"bytes"), Value::Bytes(Vec::from(*b"bytes"))); } #[test] fn map() { assert_same::>( "{ 'a': \"Hello\", 'b': \"Bye\", }", ); assert_eq!(Value::from(Map::new()), Value::Map(Map::new())); assert_eq!( Value::from_iter([("a", 42)]), Value::Map({ let mut map = Map::new(); map.insert(Value::from("a"), Value::from(42)); map }) ); } #[test] fn option() { assert_same::>("Some('a')"); assert_same::>("None"); assert_eq!(Value::from(Option::::None), Value::Option(None)); assert_eq!( Value::from(Some(false)), Value::Option(Some(Box::new(Value::Bool(false)))) ); assert_eq!( Value::from(Some(Option::::None)), Value::Option(Some(Box::new(Value::Option(None)))) ); } #[test] fn seq() { assert_same::>("[1.0, 2.0, 3.0, 4.0]"); assert_eq!( Value::from([-1_i8, 2, -3].as_slice()), Value::Seq(vec![ Value::from(-1_i8), Value::from(2_i8), Value::from(-3_i8) ]) ); assert_eq!( Value::from(vec![-1_i8, 2, -3]), Value::Seq(vec![ Value::from(-1_i8), Value::from(2_i8), Value::from(-3_i8) ]) ); assert_eq!( Value::from_iter([-1_i8, 2, -3]), Value::Seq(vec![ Value::from(-1_i8), Value::from(2_i8), Value::from(-3_i8) ]) ); } #[test] fn unit() { assert_same::<()>("()"); assert_eq!(Value::from(()), Value::Unit); } #[test] #[should_panic(expected = "Contract violation: value before key")] fn map_access_contract_violation() { struct BadVisitor; impl<'de> Visitor<'de> for BadVisitor { type Value = (); // GRCOV_EXCL_START fn expecting(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { fmt.write_str("a map") } // GRCOV_EXCL_STOP fn visit_map>( self, mut map: A, ) -> Result { map.next_value::<()>() } } let value = Value::Map([("a", 42)].into_iter().collect()); let _ = value.deserialize_map(BadVisitor); } #[test] fn transparent_value_newtype() { struct NewtypeDeserializer; impl<'de> Deserializer<'de> for NewtypeDeserializer { type Error = Error; fn deserialize_any>(self, visitor: V) -> Result { visitor.visit_newtype_struct(serde::de::value::CharDeserializer::new('🦀')) } // GRCOV_EXCL_START forward_to_deserialize_any! { bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } #[cfg(feature = "integer128")] forward_to_deserialize_any! { i128 u128 } // GRCOV_EXCL_STOP } assert_eq!( Value::deserialize(NewtypeDeserializer).unwrap(), Value::from('🦀') ); } } ron-0.12.0/src/value/number.rs000064400000000000000000000312571046102023000142530ustar 00000000000000use core::{ cmp::{Eq, Ordering}, hash::{Hash, Hasher}, }; use serde::{de::Visitor, Serialize, Serializer}; /// A wrapper for any numeric primitive type in Rust. /// /// Some varints of the `Number` enum are enabled by features: /// - `Number::I128` and `Number::U128` by the `integer128` feature /// /// To ensure that feature unification does not break `match`ing over `Number`, /// the `Number` enum is non-exhaustive. /// ///
/// Exhaustively matching on Number in tests /// /// If you want to ensure that you exhaustively handle every variant, you can /// match on the hidden `Number::__NonExhaustive(x)` variant by using the /// `x.never() -> !` method. /// ///
/// Matching on this variant means that your code may break when RON is /// upgraded or when feature unification enables further variants in the /// Number enum than your code expects. ///
/// /// It is your responsibility to only *ever* match on `Number::__NonExhaustive` /// inside tests, e.g. by using `#[cfg(test)]` on the particular match arm, to /// ensure that only your tests break (e.g. in CI) when your code is not /// exhaustively matching on every variant, e.g. after a version upgrade or /// feature unification. ///
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Ord)] #[cfg_attr(doc, non_exhaustive)] pub enum Number { I8(i8), I16(i16), I32(i32), I64(i64), #[cfg(feature = "integer128")] I128(i128), U8(u8), U16(u16), U32(u32), U64(u64), #[cfg(feature = "integer128")] U128(u128), F32(F32), F64(F64), #[cfg(not(doc))] #[allow(private_interfaces)] __NonExhaustive(private::Never), } mod private { #[derive(Debug, PartialEq, PartialOrd, Eq, Hash, Ord)] enum _Never {} #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Ord)] pub struct Never { never: &'static _Never, } impl Never { pub fn never(self) -> ! { match *self.never {} } } #[cfg(not(feature = "integer128"))] /// ```compile_fail /// # use ron::Number; /// fn match_number(x: Number) { /// match x { /// Number::I8(v) => println!("i8: {}", v), /// Number::I16(v) => println!("i16: {}", v), /// Number::I32(v) => println!("i32: {}", v), /// Number::I64(v) => println!("i64: {}", v), /// Number::U8(v) => println!("u8: {}", v), /// Number::U16(v) => println!("u16: {}", v), /// Number::U32(v) => println!("u32: {}", v), /// Number::U64(v) => println!("u64: {}", v), /// Number::F32(v) => println!("f32: {}", v.0), /// Number::F64(v) => println!("f64: {}", v.0), /// } /// } /// ``` fn _assert_non_exhaustive_check_fails_not_integer128() {} #[cfg(feature = "integer128")] /// ```compile_fail /// # use ron::Number; /// fn match_number(x: Number) { /// match x { /// Number::I8(v) => println!("i8: {}", v), /// Number::I16(v) => println!("i16: {}", v), /// Number::I32(v) => println!("i32: {}", v), /// Number::I64(v) => println!("i64: {}", v), /// Number::I128(v) => println!("i128: {}", v), /// Number::U8(v) => println!("u8: {}", v), /// Number::U16(v) => println!("u16: {}", v), /// Number::U32(v) => println!("u32: {}", v), /// Number::U64(v) => println!("u64: {}", v), /// Number::U128(v) => println!("u128: {}", v), /// Number::F32(v) => println!("f32: {}", v.0), /// Number::F64(v) => println!("f64: {}", v.0), /// } /// } /// ``` fn _assert_non_exhaustive_check_fails_integer128() {} } impl Serialize for Number { fn serialize(&self, serializer: S) -> Result { match self { Self::I8(v) => serializer.serialize_i8(*v), Self::I16(v) => serializer.serialize_i16(*v), Self::I32(v) => serializer.serialize_i32(*v), Self::I64(v) => serializer.serialize_i64(*v), #[cfg(feature = "integer128")] Self::I128(v) => serializer.serialize_i128(*v), Self::U8(v) => serializer.serialize_u8(*v), Self::U16(v) => serializer.serialize_u16(*v), Self::U32(v) => serializer.serialize_u32(*v), Self::U64(v) => serializer.serialize_u64(*v), #[cfg(feature = "integer128")] Self::U128(v) => serializer.serialize_u128(*v), Self::F32(v) => serializer.serialize_f32(v.get()), Self::F64(v) => serializer.serialize_f64(v.get()), #[cfg(not(doc))] Self::__NonExhaustive(never) => never.never(), } } } impl Number { pub fn visit<'de, V: Visitor<'de>, E: serde::de::Error>( &self, visitor: V, ) -> Result { match self { Self::I8(v) => visitor.visit_i8(*v), Self::I16(v) => visitor.visit_i16(*v), Self::I32(v) => visitor.visit_i32(*v), Self::I64(v) => visitor.visit_i64(*v), #[cfg(feature = "integer128")] Self::I128(v) => visitor.visit_i128(*v), Self::U8(v) => visitor.visit_u8(*v), Self::U16(v) => visitor.visit_u16(*v), Self::U32(v) => visitor.visit_u32(*v), Self::U64(v) => visitor.visit_u64(*v), #[cfg(feature = "integer128")] Self::U128(v) => visitor.visit_u128(*v), Self::F32(v) => visitor.visit_f32(v.get()), Self::F64(v) => visitor.visit_f64(v.get()), #[cfg(not(doc))] Self::__NonExhaustive(never) => never.never(), } } } macro_rules! float_ty { ($ty:ident($float:ty)) => { #[doc = concat!( "A wrapper for [`", stringify!($float), "`], which implements [`Eq`], ", "[`Hash`] and [`Ord`] using [`", stringify!($float), "::total_cmp`] ", "for a total order comparison", )] #[derive(Copy, Clone, Debug)] // GRCOV_EXCL_LINE pub struct $ty(pub $float); impl $ty { #[doc = concat!("Construct a new [`", stringify!($ty), "`].")] #[must_use] pub fn new(v: $float) -> Self { Self(v) } #[doc = concat!("Returns the wrapped [`", stringify!($float), "`].")] #[must_use] pub fn get(self) -> $float { self.0 } } impl From<$float> for $ty { fn from(v: $float) -> Self { Self::new(v) } } /// Partial equality comparison /// #[doc = concat!( "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ", "floating values use [`", stringify!($float), "::total_cmp`] for a total ", "order comparison.", )] /// /// See the [`Ord`] implementation. impl PartialEq for $ty { fn eq(&self, other: &Self) -> bool { self.cmp(other).is_eq() } } /// Equality comparison /// #[doc = concat!( "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ", "floating values use [`", stringify!($float), "::total_cmp`] for a total ", "order comparison.", )] /// /// See the [`Ord`] implementation. impl Eq for $ty {} impl Hash for $ty { fn hash(&self, state: &mut H) { self.0.to_bits().hash(state); } } /// Partial ordering comparison /// #[doc = concat!( "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ", "floating values use [`", stringify!($float), "::total_cmp`] for a total ", "order comparison.", )] /// /// See the [`Ord`] implementation. impl PartialOrd for $ty { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } /// Ordering comparison /// #[doc = concat!( "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ", "floating values use [`", stringify!($float), "::total_cmp`] for a total ", "order comparison.", )] /// /// ``` #[doc = concat!("use ron::value::", stringify!($ty), ";")] #[doc = concat!( "assert!(", stringify!($ty), "::new(", stringify!($float), "::NAN) > ", stringify!($ty), "::new(", stringify!($float), "::INFINITY));", )] #[doc = concat!( "assert!(", stringify!($ty), "::new(-", stringify!($float), "::NAN) < ", stringify!($ty), "::new(", stringify!($float), "::NEG_INFINITY));", )] #[doc = concat!( "assert!(", stringify!($ty), "::new(", stringify!($float), "::NAN) == ", stringify!($ty), "::new(", stringify!($float), "::NAN));", )] /// ``` impl Ord for $ty { fn cmp(&self, other: &Self) -> Ordering { self.0.total_cmp(&other.0) } } }; } float_ty! { F32(f32) } float_ty! { F64(f64) } impl Number { /// Construct a new number. pub fn new(v: impl Into) -> Self { v.into() } /// Returns the [`f64`] representation of the [`Number`] regardless of /// whether the number is stored as a float or integer. /// /// # Example /// /// ``` /// # use ron::value::Number; /// let i = Number::new(5); /// let f = Number::new(2.0); /// assert_eq!(i.into_f64(), 5.0); /// assert_eq!(f.into_f64(), 2.0); /// ``` #[must_use] pub fn into_f64(self) -> f64 { #[allow(clippy::cast_precision_loss)] match self { Self::I8(v) => f64::from(v), Self::I16(v) => f64::from(v), Self::I32(v) => f64::from(v), Self::I64(v) => v as f64, #[cfg(feature = "integer128")] Self::I128(v) => v as f64, Self::U8(v) => f64::from(v), Self::U16(v) => f64::from(v), Self::U32(v) => f64::from(v), Self::U64(v) => v as f64, #[cfg(feature = "integer128")] Self::U128(v) => v as f64, Self::F32(v) => f64::from(v.get()), Self::F64(v) => v.get(), #[cfg(not(doc))] Self::__NonExhaustive(never) => never.never(), } } } macro_rules! number_from_impl { (Number::$variant:ident($wrap:ident($ty:ty))) => { impl From<$ty> for Number { fn from(v: $ty) -> Number { Number::$variant($wrap(v)) } } }; (Number::$variant:ident($ty:ty)) => { impl From<$ty> for Number { fn from(v: $ty) -> Number { Number::$variant(v) } } }; } number_from_impl! { Number::I8(i8) } number_from_impl! { Number::I16(i16) } number_from_impl! { Number::I32(i32) } number_from_impl! { Number::I64(i64) } #[cfg(feature = "integer128")] number_from_impl! { Number::I128(i128) } number_from_impl! { Number::U8(u8) } number_from_impl! { Number::U16(u16) } number_from_impl! { Number::U32(u32) } number_from_impl! { Number::U64(u64) } #[cfg(feature = "integer128")] number_from_impl! { Number::U128(u128) } number_from_impl! { Number::F32(F32(f32)) } number_from_impl! { Number::F64(F64(f64)) } #[cfg(test)] mod tests { use super::*; #[test] fn test_nan() { assert_eq!(F32(f32::NAN), F32(f32::NAN)); assert_eq!(F32(-f32::NAN), F32(-f32::NAN)); assert_ne!(F32(f32::NAN), F32(-f32::NAN)); } #[cfg(feature = "std")] #[test] fn test_nan_hash() { use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; fn hash(v: &T) -> u64 { let mut state = DefaultHasher::new(); v.hash(&mut state); state.finish() } assert_eq!(hash(&F32(f32::NAN)), hash(&F32(f32::NAN))); assert_eq!(hash(&F32(-f32::NAN)), hash(&F32(-f32::NAN))); assert_ne!(hash(&F32(f32::NAN)), hash(&F32(-f32::NAN))); } #[test] fn test_partial_ord() { assert!(F32(f32::NAN) > F32(f32::INFINITY)); assert!(F32(-f32::NAN) < F32(f32::NEG_INFINITY)); assert!(F32(f32::NAN) == F32(f32::NAN)); } } ron-0.12.0/src/value/raw.rs000064400000000000000000000204051046102023000135450ustar 00000000000000// Inspired by David Tolnay's serde-rs // https://github.com/serde-rs/json/blob/master/src/raw.rs // Licensed under either of Apache License, Version 2.0 or MIT license at your option. use alloc::{borrow::ToOwned, boxed::Box, format, string::String}; use core::{fmt, ops::Range}; use serde::{de, ser, Deserialize, Serialize}; use crate::{ error::{Error, SpannedResult}, options::Options, }; // NOTE: Keep synchronised with fuzz/arbitrary::typed_data::RAW_VALUE_TOKEN pub(crate) const RAW_VALUE_TOKEN: &str = "$ron::private::RawValue"; #[allow(clippy::module_name_repetitions)] #[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct RawValue { ron: str, } #[allow(unsafe_code)] impl RawValue { fn from_borrowed_str(ron: &str) -> &Self { // Safety: RawValue is a transparent newtype around str unsafe { &*(ron as *const str as *const RawValue) } } fn from_boxed_str(ron: Box) -> Box { // Safety: RawValue is a transparent newtype around str unsafe { core::mem::transmute::, Box>(ron) } } fn into_boxed_str(raw_value: Box) -> Box { // Safety: RawValue is a transparent newtype around str unsafe { core::mem::transmute::, Box>(raw_value) } } #[allow(clippy::expect_used)] fn trim_range(ron: &str) -> Range { fn trim_range_inner(ron: &str) -> Result, Error> { let mut deserializer = crate::Deserializer::from_str(ron).map_err(Error::from)?; deserializer.parser.skip_ws()?; let start_offset = ron.len() - deserializer.parser.src().len(); let _ = serde::de::IgnoredAny::deserialize(&mut deserializer)?; deserializer.parser.skip_ws()?; let end_offset = deserializer.parser.pre_ws_src().len(); Ok(start_offset..(ron.len() - end_offset)) } trim_range_inner(ron).expect("RawValue must contain valid ron") } } impl Clone for Box { fn clone(&self) -> Self { (**self).to_owned() } } impl ToOwned for RawValue { type Owned = Box; fn to_owned(&self) -> Self::Owned { RawValue::from_boxed_str(self.ron.to_owned().into_boxed_str()) } } impl fmt::Debug for RawValue { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("RawValue") .field(&format_args!("{}", &self.ron)) .finish() } } impl fmt::Display for RawValue { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(&self.ron) } } impl RawValue { /// Get the inner raw RON string, which is guaranteed to contain valid RON. #[must_use] pub fn get_ron(&self) -> &str { &self.ron } /// Helper function to validate a RON string and turn it into a /// [`RawValue`]. pub fn from_ron(ron: &str) -> SpannedResult<&Self> { let mut deserializer = crate::Deserializer::from_str(ron)?; // raw values can be used everywhere but extensions cannot if !deserializer.extensions().is_empty() { return Err(deserializer.span_error(Error::Message(String::from( "ron::value::RawValue cannot enable extensions", )))); } let _ = <&Self>::deserialize(&mut deserializer).map_err(|e| deserializer.span_error(e))?; deserializer.end().map_err(|e| deserializer.span_error(e))?; Ok(Self::from_borrowed_str(ron)) } /// Helper function to validate a RON string and turn it into a /// [`RawValue`]. pub fn from_boxed_ron(ron: Box) -> SpannedResult> { match Self::from_ron(&ron) { Ok(_) => Ok(Self::from_boxed_str(ron)), Err(err) => Err(err), } } /// Helper function to deserialize the inner RON string into `T`. pub fn into_rust<'de, T: Deserialize<'de>>(&'de self) -> SpannedResult { Options::default().from_str(&self.ron) } /// Helper function to serialize `value` into a RON string. pub fn from_rust(value: &T) -> Result, Error> { let ron = Options::default().to_string(value)?; Ok(RawValue::from_boxed_str(ron.into_boxed_str())) } } impl RawValue { #[must_use] /// Trims any leadning and trailing whitespace off the raw RON string, /// including whitespace characters and comments. pub fn trim(&self) -> &Self { Self::from_borrowed_str(&self.ron[RawValue::trim_range(&self.ron)]) } #[must_use] #[allow(unsafe_code)] /// Trims any leadning and trailing whitespace off the boxed raw RON string, /// including whitespace characters and comments. pub fn trim_boxed(self: Box) -> Box { let trim_range = RawValue::trim_range(&self.ron); let mut boxed_ron = RawValue::into_boxed_str(self).into_string(); // SAFETY: ron[trim_range] is a valid str, so draining everything // before and after leaves a valid str unsafe { boxed_ron.as_mut_vec().drain(trim_range.end..); boxed_ron.as_mut_vec().drain(0..trim_range.start); } RawValue::from_boxed_str(boxed_ron.into_boxed_str()) } } impl From> for Box { fn from(raw_value: Box) -> Self { RawValue::into_boxed_str(raw_value) } } impl Serialize for RawValue { fn serialize(&self, serializer: S) -> Result { serializer.serialize_newtype_struct(RAW_VALUE_TOKEN, &self.ron) } } impl<'de: 'a, 'a> Deserialize<'de> for &'a RawValue { fn deserialize>(deserializer: D) -> Result { struct ReferenceVisitor; impl<'de> de::Visitor<'de> for ReferenceVisitor { type Value = &'de RawValue; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { // This error message only shows up with foreign Deserializers write!(formatter, "any valid borrowed RON-value-string") } fn visit_borrowed_str(self, ron: &'de str) -> Result { match Options::default().from_str::(ron) { Ok(_) => Ok(RawValue::from_borrowed_str(ron)), Err(err) => Err(de::Error::custom(format!("invalid RON value at {}", err))), } } fn visit_newtype_struct>( self, deserializer: D, ) -> Result { deserializer.deserialize_str(self) } } deserializer.deserialize_newtype_struct(RAW_VALUE_TOKEN, ReferenceVisitor) } } impl<'de> Deserialize<'de> for Box { fn deserialize>(deserializer: D) -> Result { struct BoxedVisitor; impl<'de> de::Visitor<'de> for BoxedVisitor { type Value = Box; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { // This error message only shows up with foreign Deserializers write!(formatter, "any valid RON-value-string") } fn visit_str(self, ron: &str) -> Result { match Options::default().from_str::(ron) { Ok(_) => Ok(RawValue::from_boxed_str(ron.to_owned().into_boxed_str())), Err(err) => Err(de::Error::custom(format!("invalid RON value at {}", err))), } } fn visit_string(self, ron: String) -> Result { match Options::default().from_str::(&ron) { Ok(_) => Ok(RawValue::from_boxed_str(ron.into_boxed_str())), Err(err) => Err(de::Error::custom(format!("invalid RON value at {}", err))), } } fn visit_newtype_struct>( self, deserializer: D, ) -> Result { deserializer.deserialize_string(self) } } deserializer.deserialize_newtype_struct(RAW_VALUE_TOKEN, BoxedVisitor) } } ron-0.12.0/tests/115_minimal_flattening.rs000064400000000000000000000206331046102023000164450ustar 00000000000000use std::collections::HashMap; use ron::error::Span; use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize, PartialEq, Eq, Debug)] struct Main { #[serde(flatten)] required: Required, #[serde(flatten)] optional: Optional, some_other_field: u32, } #[derive(Deserialize, Serialize, PartialEq, Eq, Debug)] struct Required { first: u32, second: u32, } #[derive(Deserialize, Serialize, PartialEq, Eq, Debug)] struct Optional { third: Option, } #[derive(Deserialize, Serialize, PartialEq, Eq, Debug)] struct MyType { first: u32, second: u32, #[serde(flatten)] everything_else: HashMap, } #[derive(Deserialize, Serialize, PartialEq, Eq, Debug)] struct AllOptional { #[serde(flatten)] everything_else: HashMap, } #[derive(Deserialize, Serialize, PartialEq, Eq, Debug)] enum Newtype { Main(Main), MyType(MyType), AllOptional(AllOptional), } #[test] fn test_flatten_struct_into_struct() { let val = Main { required: Required { first: 1, second: 2, }, optional: Optional { third: Some(3) }, some_other_field: 1337, }; let ron = ron::ser::to_string_pretty(&val, ron::ser::PrettyConfig::default()).unwrap(); assert_eq!( ron, "{ \"first\": 1, \"second\": 2, \"third\": Some(3), \"some_other_field\": 1337, }" ); let de: Main = ron::from_str(&ron).unwrap(); assert_eq!(de, val); let val = Newtype::Main(Main { required: Required { first: 1, second: 2, }, optional: Optional { third: Some(3) }, some_other_field: 1337, }); let ron = ron::ser::to_string_pretty( &val, ron::ser::PrettyConfig::default() .extensions(ron::extensions::Extensions::UNWRAP_VARIANT_NEWTYPES), ) .unwrap(); assert_eq!( ron, "#![enable(unwrap_variant_newtypes)] Main({ \"first\": 1, \"second\": 2, \"third\": Some(3), \"some_other_field\": 1337, })" ); let ron = ron::ser::to_string_pretty(&val, ron::ser::PrettyConfig::default()).unwrap(); let de: Newtype = ron::from_str(&ron).unwrap(); assert_eq!(de, val); assert_eq!( ron::from_str::
( "{ first\": 1, \"second\": 2, \"third\": Some(3), \"some_other_field\": 1337, }" ), Err(ron::error::SpannedError { code: ron::error::Error::ExpectedString, span: Span { start: ron::error::Position { line: 1, col: 2 }, end: ron::error::Position { line: 2, col: 9 }, } }) ); assert_eq!( ron::from_str::
( "{ \"first\": 1, \"second: 2, \"third\": Some(3), \"some_other_field\": 1337, }" ), Err(ron::error::SpannedError { code: ron::error::Error::ExpectedMapColon, span: Span { start: ron::error::Position { line: 4, col: 10 }, end: ron::error::Position { line: 4, col: 10 }, } }) ); assert_eq!( ron::from_str::
( "{ \"first\": 1, \"second\": 2, third\": Some(3), \"some_other_field\": 1337, }" ), Err(ron::error::SpannedError { code: ron::error::Error::ExpectedString, span: Span { start: ron::error::Position { line: 4, col: 9 }, end: ron::error::Position { line: 4, col: 9 }, } }) ); assert_eq!( ron::from_str::
( "{ \"first\": 1, \"second\": 2, \"third\": Some(3), \"some_other_field: 1337, }" ), Err(ron::error::SpannedError { code: ron::error::Error::ExpectedStringEnd, span: Span { start: ron::error::Position { line: 5, col: 9 }, end: ron::error::Position { line: 5, col: 10 }, } }) ); } #[test] fn test_flatten_rest() { let val = MyType { first: 1, second: 2, everything_else: { let mut map = HashMap::new(); map.insert( String::from("third"), ron::Value::Number(ron::value::Number::U8(3)), ); map }, }; let ron = ron::ser::to_string_pretty(&val, ron::ser::PrettyConfig::default()).unwrap(); assert_eq!( ron, "{ \"first\": 1, \"second\": 2, \"third\": 3, }" ); let de: MyType = ron::from_str(&ron).unwrap(); assert_eq!(de, val); let val = Newtype::MyType(MyType { first: 1, second: 2, everything_else: { let mut map = HashMap::new(); map.insert( String::from("third"), ron::Value::Number(ron::value::Number::U8(3)), ); map }, }); let ron = ron::ser::to_string_pretty( &val, ron::ser::PrettyConfig::default() .extensions(ron::extensions::Extensions::UNWRAP_VARIANT_NEWTYPES), ) .unwrap(); assert_eq!( ron, "#![enable(unwrap_variant_newtypes)] MyType({ \"first\": 1, \"second\": 2, \"third\": 3, })" ); let de: Newtype = ron::from_str(&ron).unwrap(); assert_eq!(de, val); assert_eq!( ron::from_str::( "{ first\": 1, \"second\": 2, \"third\": 3, }" ), Err(ron::error::SpannedError { code: ron::error::Error::ExpectedString, span: Span { start: ron::error::Position { line: 1, col: 2 }, end: ron::error::Position { line: 2, col: 9 }, } }) ); assert_eq!( ron::from_str::( "{ \"first\": 1, \"second: 2, \"third\": 3, }" ), Err(ron::error::SpannedError { code: ron::error::Error::ExpectedMapColon, span: Span { start: ron::error::Position { line: 4, col: 10 }, end: ron::error::Position { line: 4, col: 10 }, } }) ); assert_eq!( ron::from_str::( "{ \"first\": 1, \"second\": 2, third\": 3, }" ), Err(ron::error::SpannedError { code: ron::error::Error::ExpectedString, span: Span { start: ron::error::Position { line: 4, col: 9 }, end: ron::error::Position { line: 4, col: 9 }, } }) ); assert_eq!( ron::from_str::( "{ \"first\": 1, \"second\": 2, \"third: 3, }" ), Err(ron::error::SpannedError { code: ron::error::Error::ExpectedStringEnd, span: Span { start: ron::error::Position { line: 4, col: 9 }, end: ron::error::Position { line: 4, col: 10 }, } }) ); } #[test] fn test_flatten_only_rest() { let val = AllOptional { everything_else: HashMap::new(), }; let ron = ron::ser::to_string(&val).unwrap(); assert_eq!(ron, "{}"); let de: AllOptional = ron::from_str(&ron).unwrap(); assert_eq!(de, val); let val = Newtype::AllOptional(AllOptional { everything_else: HashMap::new(), }); let ron = ron::ser::to_string_pretty( &val, ron::ser::PrettyConfig::default() .extensions(ron::extensions::Extensions::UNWRAP_VARIANT_NEWTYPES), ) .unwrap(); assert_eq!( ron, "#![enable(unwrap_variant_newtypes)] AllOptional({ })" ); let de: Newtype = ron::from_str(&ron).unwrap(); assert_eq!(de, val); } #[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)] #[serde(deny_unknown_fields)] pub struct AvailableCards { pub left: u8, pub right: u8, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] struct MapProperties { #[serde(flatten)] cards: AvailableCards, } #[test] fn test_issue_456() { let map_properties = MapProperties { cards: AvailableCards { ..Default::default() }, }; let ron = ron::to_string(&map_properties).unwrap(); let de: MapProperties = ron::from_str(&ron).unwrap(); assert_eq!(map_properties, de); } ron-0.12.0/tests/117_untagged_tuple_variant.rs000064400000000000000000000026201046102023000173350ustar 00000000000000use std::borrow::Cow; use ron::{de::from_str, ser::to_string}; use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct BuildSystem<'m> { version: Cow<'m, str>, flags: Vec>, } #[derive(Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(untagged)] pub enum Flag<'m> { Value(Cow<'m, str>), If(Cow<'m, str>, Vec>), } #[test] fn test_ebkalderon_case() { let file = r#"BuildSystem( version: "1.0.0", flags: [ "--enable-thing", "--enable-other-thing", ("some-conditional", ["--enable-third-thing"]), ] ) "#; assert_eq!( from_str::(file).unwrap(), BuildSystem { version: "1.0.0".into(), flags: vec![ Flag::Value("--enable-thing".into()), Flag::Value("--enable-other-thing".into()), Flag::If( "some-conditional".into(), vec!["--enable-third-thing".into()] ) ] }, ); } #[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] #[serde(untagged)] enum Foo { Bar(usize), } #[test] fn test_vessd_case() { let foo_vec = vec![Foo::Bar(0); 5]; let foo_str = to_string(&foo_vec).unwrap(); assert_eq!(foo_str.as_str(), "[0,0,0,0,0]"); assert_eq!(from_str::>(&foo_str).unwrap(), foo_vec); } ron-0.12.0/tests/123_enum_representation.rs000064400000000000000000000163161046102023000166740ustar 00000000000000use std::{cmp::PartialEq, fmt::Debug}; use ron::{de::from_str, ser::to_string}; use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Serialize, Deserialize)] struct Unit; #[derive(Debug, PartialEq, Serialize, Deserialize)] struct Tuple(u8, u8); #[derive(Debug, PartialEq, Serialize, Deserialize)] enum Inner { Foo, Bar, } #[derive(Debug, PartialEq, Serialize, Deserialize)] enum EnumStructExternally { VariantA { foo: u32, bar: Unit, #[serde(with = "ByteStr")] baz: Vec, different: Tuple, }, VariantB { foo: u32, bar: Unit, #[serde(with = "ByteStr")] baz: Vec, }, } #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(tag = "type")] enum EnumStructInternally { VariantA { foo: u32, bar: u32, different: u32 }, VariantB { foo: u32, bar: u32 }, } #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(tag = "type", content = "content")] enum EnumStructAdjacently { VariantA { foo: f64, bar: (), different: Inner }, VariantB { foo: f64, bar: () }, } #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(untagged)] enum EnumStructUntagged { VariantA { foo: u32, bar: u32, different: u32 }, VariantB { foo: u32, bar: u32 }, } fn test_ser(value: &T, expected: &str) { let actual = to_string(value).expect("Failed to serialize"); assert_eq!(actual, expected); } fn test_de(s: &str, expected: T) where T: for<'a> Deserialize<'a> + Debug + PartialEq, { let actual: Result = from_str(s); assert_eq!(actual, Ok(expected)); } fn test_roundtrip(value: T) where T: Serialize + for<'a> Deserialize<'a> + Debug + PartialEq, { let s = to_string(&value).expect("Failed to serialize"); let actual: Result = from_str(&s); assert_eq!(actual, Ok(value)); } #[test] fn test_externally_a_ser() { let v = EnumStructExternally::VariantA { foo: 1, bar: Unit, baz: vec![b'a'], different: Tuple(2, 3), }; let e = "VariantA(foo:1,bar:(),baz:b\"a\",different:(2,3))"; test_ser(&v, e); } #[test] fn test_externally_b_ser() { let v = EnumStructExternally::VariantB { foo: 1, bar: Unit, baz: vec![b'a'], }; let e = "VariantB(foo:1,bar:(),baz:b\"a\")"; test_ser(&v, e); } #[test] fn test_internally_a_ser() { let v = EnumStructInternally::VariantA { foo: 1, bar: 2, different: 3, }; let e = "(type:\"VariantA\",foo:1,bar:2,different:3)"; test_ser(&v, e); } #[test] fn test_internally_b_ser() { let v = EnumStructInternally::VariantB { foo: 1, bar: 2 }; let e = "(type:\"VariantB\",foo:1,bar:2)"; test_ser(&v, e); } #[test] fn test_adjacently_a_ser() { let v = EnumStructAdjacently::VariantA { foo: 1.0, bar: (), different: Inner::Foo, }; let e = "(type:VariantA,content:(foo:1.0,bar:(),different:Foo))"; test_ser(&v, e); } #[test] fn test_adjacently_b_ser() { let v = EnumStructAdjacently::VariantB { foo: 1.0, bar: () }; let e = "(type:VariantB,content:(foo:1.0,bar:()))"; test_ser(&v, e); } #[test] fn test_untagged_a_ser() { let v = EnumStructUntagged::VariantA { foo: 1, bar: 2, different: 3, }; let e = "(foo:1,bar:2,different:3)"; test_ser(&v, e); } #[test] fn test_untagged_b_ser() { let v = EnumStructUntagged::VariantB { foo: 1, bar: 2 }; let e = "(foo:1,bar:2)"; test_ser(&v, e); } #[test] fn test_externally_a_de() { let s = "VariantA(foo:1,bar:Unit,baz:b\"a\",different:Tuple(2,3))"; let e = EnumStructExternally::VariantA { foo: 1, bar: Unit, baz: vec![b'a'], different: Tuple(2, 3), }; test_de(s, e); } #[test] fn test_externally_b_de() { let s = "VariantB(foo:1,bar:Unit,baz:b\"a\")"; let e = EnumStructExternally::VariantB { foo: 1, bar: Unit, baz: vec![b'a'], }; test_de(s, e); } #[test] fn test_internally_a_de() { let s = "(type:\"VariantA\",foo:1,bar:2,different:3)"; let e = EnumStructInternally::VariantA { foo: 1, bar: 2, different: 3, }; test_de(s, e); } #[test] fn test_internally_b_de() { let s = "(type:\"VariantB\",foo:1,bar:2)"; let e = EnumStructInternally::VariantB { foo: 1, bar: 2 }; test_de(s, e); } #[test] fn test_adjacently_a_de() { let s = "(type:VariantA,content:(foo:1.0,bar:(),different:Foo))"; let e = EnumStructAdjacently::VariantA { foo: 1.0, bar: (), different: Inner::Foo, }; test_de(s, e); } #[test] fn test_adjacently_b_de() { let s = "(type:VariantB,content:(foo:1.0,bar:()))"; let e = EnumStructAdjacently::VariantB { foo: 1.0, bar: () }; test_de(s, e); } #[test] fn test_untagged_a_de() { let s = "(foo:1,bar:2,different:3)"; let e = EnumStructUntagged::VariantA { foo: 1, bar: 2, different: 3, }; test_de(s, e); } #[test] fn test_untagged_b_de() { let s = "(foo:1,bar:2)"; let e = EnumStructUntagged::VariantB { foo: 1, bar: 2 }; test_de(s, e); } #[test] fn test_externally_a_roundtrip() { let v = EnumStructExternally::VariantA { foo: 1, bar: Unit, baz: vec![b'a'], different: Tuple(2, 3), }; test_roundtrip(v); } #[test] fn test_externally_b_roundtrip() { let v = EnumStructExternally::VariantB { foo: 1, bar: Unit, baz: vec![b'a'], }; test_roundtrip(v); } #[test] fn test_internally_a_roundtrip() { let v = EnumStructInternally::VariantA { foo: 1, bar: 2, different: 3, }; test_roundtrip(v); } #[test] fn test_internally_b_roundtrip() { let v = EnumStructInternally::VariantB { foo: 1, bar: 2 }; test_roundtrip(v); } #[test] fn test_adjacently_a_roundtrip() { let v = EnumStructAdjacently::VariantA { foo: 1.0, bar: (), different: Inner::Foo, }; test_roundtrip(v); } #[test] fn test_adjacently_b_roundtrip() { let v = EnumStructAdjacently::VariantB { foo: 1.0, bar: () }; test_roundtrip(v); } #[test] fn test_untagged_a_roundtrip() { let v = EnumStructUntagged::VariantA { foo: 1, bar: 2, different: 3, }; test_roundtrip(v); } #[test] fn test_untagged_b_roundtrip() { let v = EnumStructUntagged::VariantB { foo: 1, bar: 2 }; test_roundtrip(v); } enum ByteStr {} impl ByteStr { fn serialize(data: &[u8], serializer: S) -> Result { serializer.serialize_bytes(data) } fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result, D::Error> { struct ByteStrVisitor; impl<'de> serde::de::Visitor<'de> for ByteStrVisitor { type Value = Vec; // GRCOV_EXCL_START fn expecting(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { fmt.write_str("a Rusty byte string") } // GRCOV_EXCL_STOP fn visit_bytes(self, bytes: &[u8]) -> Result { Ok(bytes.to_vec()) } } deserializer.deserialize_bytes(ByteStrVisitor) } } ron-0.12.0/tests/129_indexmap.rs000064400000000000000000000035121046102023000144130ustar 00000000000000#[cfg(feature = "indexmap")] use ron::{de::from_str, Value}; #[test] #[cfg(feature = "indexmap")] fn test_order_preserved() { let file = r#"( tasks: { "debug message": Dbg( msg: "test message. some text after it." ), "shell command": Shell( command: "ls", args: Some([ "-l", "-h", ]), ch_dir: Some("/"), ), }, ) "#; let value: Value = from_str(file).unwrap(); match value { Value::Map(map) => match &map[&Value::String("tasks".to_owned())] { Value::Map(map) => { assert_eq!( *map.keys().next().unwrap(), Value::String("debug message".to_string()) ); assert_eq!( *map.keys().nth(1).unwrap(), Value::String("shell command".to_string()) ); } _ => panic!(), // GRCOV_EXCL_LINE }, _ => panic!(), // GRCOV_EXCL_LINE } let file = r#"( tasks: { "shell command": Shell( command: "ls", args: Some([ "-l", "-h", ]), ch_dir: Some("/") ), "debug message": Dbg( msg: "test message. some text after it." ), } ) "#; let value: Value = from_str(file).unwrap(); match value { Value::Map(map) => match &map[&Value::String("tasks".to_owned())] { Value::Map(map) => { assert_eq!( *map.keys().next().unwrap(), Value::String("shell command".to_string()) ); assert_eq!( *map.keys().nth(1).unwrap(), Value::String("debug message".to_string()) ); } _ => panic!(), // GRCOV_EXCL_LINE }, _ => panic!(), // GRCOV_EXCL_LINE } } ron-0.12.0/tests/147_empty_sets_serialisation.rs000064400000000000000000000027441046102023000177360ustar 00000000000000use std::collections::HashMap; use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Deserialize, Serialize)] struct UnitStruct; #[derive(Debug, PartialEq, Deserialize, Serialize)] struct NewType(f32); #[derive(Debug, PartialEq, Deserialize, Serialize)] struct TupleStruct(UnitStruct, i8); #[derive(Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] struct Key(u32); #[derive(Debug, PartialEq, Deserialize, Serialize)] struct Struct { tuple: ((), NewType, TupleStruct), vec: Vec>, map: HashMap, deep_vec: HashMap>, deep_map: HashMap>, } #[test] fn empty_sets_arrays() { let value = Struct { tuple: ((), NewType(0.5), TupleStruct(UnitStruct, -5)), vec: vec![], map: vec![].into_iter().collect(), deep_vec: vec![(Key(0), vec![])].into_iter().collect(), deep_map: vec![(Key(0), vec![].into_iter().collect())] .into_iter() .collect(), }; let pretty = ron::ser::PrettyConfig::new() .enumerate_arrays(true) .new_line("\n"); let serial = ron::ser::to_string_pretty(&value, pretty).unwrap(); println!("Serialized: {}", serial); assert_eq!( "( tuple: ((), (0.5), ((), -5)), vec: [], map: {}, deep_vec: { (0): [], }, deep_map: { (0): {}, }, )", serial ); let deserial = ron::de::from_str(&serial); assert_eq!(Ok(value), deserial); } ron-0.12.0/tests/152_bitflags.rs000064400000000000000000000033511046102023000143760ustar 00000000000000use bitflags::bitflags; use option_set::option_set; // GRCOV_EXCL_START bitflags! { #[derive( Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, )] struct TestGood: u8 { const ONE = 1; const TWO = 1 << 1; const THREE = 1 << 2; } } // GRCOV_EXCL_STOP // GRCOV_EXCL_START option_set! { struct TestBad: UpperCamel + u8 { const ONE = 1; const TWO = 1 << 1; const THREE = 1 << 2; } } // GRCOV_EXCL_STOP #[test] fn test_bitflags() { // Test case provided by jaynus in // https://github.com/ron-rs/ron/issues/152#issue-421298302 let flag_good = TestGood::ONE | TestGood::TWO; let json_ser_good = serde_json::ser::to_string(&flag_good).unwrap(); let ron_ser_good = ron::ser::to_string(&flag_good).unwrap(); assert_eq!(json_ser_good, "\"ONE | TWO\""); assert_eq!(ron_ser_good, "(\"ONE | TWO\")"); let json_de_good: TestGood = serde_json::de::from_str(json_ser_good.as_str()).unwrap(); let ron_de_good: TestGood = ron::de::from_str(ron_ser_good.as_str()).unwrap(); assert_eq!(json_de_good, flag_good); assert_eq!(ron_de_good, flag_good); // option_set let flag_bad = TestBad::ONE | TestBad::TWO; let json_ser_bad = serde_json::ser::to_string(&flag_bad).unwrap(); let ron_ser_bad = ron::ser::to_string(&flag_bad).unwrap(); assert_eq!(json_ser_bad, "[\"One\",\"Two\"]"); assert_eq!(ron_ser_bad, "[\"One\",\"Two\"]"); let json_de_bad: TestBad = serde_json::de::from_str(json_ser_bad.as_str()).unwrap(); let ron_de_bad: TestBad = ron::de::from_str(ron_ser_bad.as_str()).unwrap(); assert_eq!(json_de_bad, flag_bad); assert_eq!(ron_de_bad, flag_bad); } ron-0.12.0/tests/203_error_positions.rs000064400000000000000000000117071046102023000160440ustar 00000000000000use std::num::NonZeroU32; use ron::error::{Error, Position, Span, SpannedError}; use serde::{ de::{Deserialize, Error as DeError, Unexpected}, Deserializer, }; #[cfg(feature = "internal-span-substring-test")] use ron::util::span_substring::check_error_span_inclusive; #[cfg(feature = "internal-span-substring-test")] use ron::util::span_substring::check_error_span_exclusive; #[derive(Debug, serde::Deserialize, PartialEq)] #[serde(deny_unknown_fields)] enum Test { TupleVariant(i32, String), StructVariant { a: bool, b: NonZeroU32, c: i32 }, } #[derive(Debug, PartialEq)] // GRCOV_EXCL_LINE struct TypeError; impl<'de> Deserialize<'de> for TypeError { fn deserialize>(_deserializer: D) -> Result { Err(D::Error::invalid_type(Unexpected::Unit, &"impossible")) } } #[test] fn test_error_positions() { let bogus_struct = " ()"; let expected_err = Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("impossible"), found: String::from("a unit value"), }, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 3 }, }, }); assert_eq!(ron::from_str::(bogus_struct), expected_err,); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::(bogus_struct, expected_err, " ("); let bogus_struct = "StructVariant(a: true, b: 0, c: -42)"; let expected_err = Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("a nonzero u32"), found: String::from("the unsigned integer `0`"), }, span: Span { start: Position { line: 1, col: 27 }, end: Position { line: 1, col: 28 }, }, }); assert_eq!(ron::from_str::(bogus_struct), expected_err); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::(bogus_struct, expected_err, "0,"); let bogus_struct = "TupleVariant(42)"; let expected_err = Err(SpannedError { code: Error::ExpectedDifferentLength { expected: String::from("tuple variant Test::TupleVariant with 2 elements"), found: 1, }, span: Span { start: Position { line: 1, col: 16 }, end: Position { line: 1, col: 16 }, }, }); assert_eq!(ron::from_str::(bogus_struct), expected_err); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::(bogus_struct, expected_err, ")"); let bogus_struct = "NotAVariant"; let expected_err = Err(SpannedError { code: Error::NoSuchEnumVariant { expected: &["TupleVariant", "StructVariant"], found: String::from("NotAVariant"), outer: Some(String::from("Test")), }, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 12 }, }, }); assert_eq!(ron::from_str::(bogus_struct), expected_err); #[cfg(feature = "internal-span-substring-test")] check_error_span_exclusive::(bogus_struct, expected_err, "NotAVariant"); let bogus_struct = "StructVariant(a: true, b: 1, c: -42, d: \"gotcha\")"; let expected_err = Err(SpannedError { code: Error::NoSuchStructField { expected: &["a", "b", "c"], found: String::from("d"), outer: Some(String::from("StructVariant")), }, span: Span { start: Position { line: 1, col: 38 }, end: Position { line: 1, col: 39 }, }, }); assert_eq!(ron::from_str::(bogus_struct), expected_err); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::(bogus_struct, expected_err, "d:"); let bogus_struct = "StructVariant(a: true, c: -42)"; let expected_err = Err(SpannedError { code: Error::MissingStructField { field: "b", outer: Some(String::from("StructVariant")), }, span: Span { start: Position { line: 1, col: 30 }, end: Position { line: 1, col: 30 }, }, }); assert_eq!(ron::from_str::(bogus_struct), expected_err); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::(bogus_struct, expected_err, ")"); let bogus_struct = "StructVariant(a: true, b: 1, a: false, c: -42)"; let expected_err = Err(SpannedError { code: Error::DuplicateStructField { field: "a", outer: Some(String::from("StructVariant")), }, span: Span { start: Position { line: 1, col: 30 }, end: Position { line: 1, col: 31 }, }, }); assert_eq!(ron::from_str::(bogus_struct), expected_err); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::(bogus_struct, expected_err, "a:"); } ron-0.12.0/tests/207_adjacently_tagged_enum.rs000064400000000000000000000010001046102023000172460ustar 00000000000000use ron::{de::from_str, ser::to_string}; use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(tag = "type", content = "data")] enum TestEnum { Name(String), Index(u32), } #[test] fn test_adjacently_tagged() { let source = TestEnum::Index(1); let ron_string = to_string(&source).unwrap(); assert_eq!(ron_string, "(type:Index,data:1)"); let deserialized = from_str::(&ron_string).unwrap(); assert_eq!(deserialized, source); } ron-0.12.0/tests/217_nested_untagged_enums.rs000064400000000000000000000047071046102023000171620ustar 00000000000000use serde::{Deserialize, Serialize}; #[test] fn nested_untagged_enum() { // Contributed by @caelunshun in https://github.com/ron-rs/ron/issues/217 #[derive(Debug, PartialEq, Deserialize)] struct Root { value: Either, } #[derive(Debug, PartialEq, Deserialize)] #[serde(transparent)] struct Newtype(Either); #[derive(Debug, PartialEq, Deserialize)] #[serde(untagged)] enum Either { One(One), Two(Two), } #[derive(Debug, PartialEq, Deserialize)] enum One { OneA, OneB, OneC, } #[derive(Debug, PartialEq, Deserialize)] enum Two { TwoA, TwoB, TwoC, } assert_eq!(ron::de::from_str("OneA"), Ok(One::OneA)); assert_eq!(ron::de::from_str("OneA"), Ok(Either::One(One::OneA))); assert_eq!( ron::de::from_str("(value: OneA)"), Ok(Root { value: Either::One(One::OneA) }) ); assert_eq!( ron::de::from_str("OneA"), Ok(Newtype(Either::One(One::OneA))) ); } #[test] fn untagged_enum_of_enum_list() { // Contributed by @joonazan in https://github.com/ron-rs/ron/issues/217 #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] pub enum UnitType { Explorer, } #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(untagged)] enum CardTextNumberFlat { JustNum(u8), RangeW(Range_), CountUnitW(CountUnit_), PowerCardsPlayedW(PowerCardsPlayed), } #[allow(non_snake_case)] #[derive(Serialize, Deserialize, Debug, PartialEq)] struct Range_ { Range: u8, } #[derive(Serialize, Deserialize, Debug, PartialEq)] #[allow(non_snake_case)] struct CountUnit_ { CountUnit: Vec, } #[derive(Serialize, Deserialize, Debug, PartialEq)] struct PowerCardsPlayed; let units = CardTextNumberFlat::CountUnitW(CountUnit_ { CountUnit: vec![UnitType::Explorer], }); let range = CardTextNumberFlat::RangeW(Range_ { Range: 1 }); let units_ron: String = ron::to_string(&units).unwrap(); let range_ron = ron::to_string(&range).unwrap(); assert_eq!(units_ron, "(CountUnit:[Explorer])"); assert_eq!(range_ron, "(Range:1)"); let units_de: CardTextNumberFlat = ron::from_str(&units_ron).unwrap(); let range_de: CardTextNumberFlat = ron::from_str(&range_ron).unwrap(); assert_eq!(units_de, units); assert_eq!(range_de, range); } ron-0.12.0/tests/238_array.rs000064400000000000000000000025321046102023000137260ustar 00000000000000use ron::{ error::{Error, Position, Span, SpannedError}, value::{Number, Value}, }; #[test] fn test_array() { let array: [i32; 3] = [1, 2, 3]; let ser = ron::to_string(&array).unwrap(); assert_eq!(ser, "(1,2,3)"); let de: [i32; 3] = ron::from_str(&ser).unwrap(); assert_eq!(de, array); let value: Value = ron::from_str(&ser).unwrap(); assert_eq!( value, Value::Seq(vec![ Value::Number(Number::U8(1)), Value::Number(Number::U8(2)), Value::Number(Number::U8(3)), ]) ); let ser = ron::to_string(&value).unwrap(); assert_eq!(ser, "[1,2,3]"); let de: [i32; 3] = value.into_rust().unwrap(); assert_eq!(de, array); // FIXME: fails and hence arrays do not roundtrip let de: SpannedError = ron::from_str::<[i32; 3]>(&ser).unwrap_err(); assert_eq!( de, SpannedError { code: Error::ExpectedStructLike, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 }, } } ); let value: Value = ron::from_str(&ser).unwrap(); assert_eq!( value, Value::Seq(vec![ Value::Number(Number::U8(1)), Value::Number(Number::U8(2)), Value::Number(Number::U8(3)), ]) ); } ron-0.12.0/tests/240_array_pretty.rs000064400000000000000000000017441046102023000153320ustar 00000000000000use ron::ser::{to_string_pretty, PrettyConfig}; #[test] fn small_array() { let arr = &[(), (), ()][..]; assert_eq!( to_string_pretty(&arr, PrettyConfig::new().new_line("\n")).unwrap(), "[ (), (), (), ]" ); assert_eq!( to_string_pretty( &arr, PrettyConfig::new().new_line("\n").compact_arrays(true) ) .unwrap(), "[(), (), ()]" ); assert_eq!( to_string_pretty( &arr, PrettyConfig::new() .new_line("\n") .compact_arrays(true) .separator("") ) .unwrap(), "[(),(),()]" ); assert_eq!( to_string_pretty( &vec![(1, 2), (3, 4)], PrettyConfig::new() .new_line("\n") .separate_tuple_members(true) .compact_arrays(true) ) .unwrap(), "[( 1, 2, ), ( 3, 4, )]" ); } ron-0.12.0/tests/250_variant_newtypes.rs000064400000000000000000000316301046102023000162050ustar 00000000000000use std::collections::HashMap; use ron::{ de::from_str, error::Error, extensions::Extensions, ser::{to_string_pretty, PrettyConfig}, }; use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, Serialize, Eq, PartialEq)] enum TestEnum { Unit, PrimitiveNewtype(String), Tuple(u32, bool), Struct { a: u32, b: bool }, TupleNewtypeUnit(Unit), TupleNewtypeNewtype(Newtype), TupleNewtypeTuple((u32, bool)), TupleNewtypeTupleStruct(TupleStruct), TupleNewtypeStruct(Struct), TupleNewtypeEnum(Enum), TupleNewtypeOption(Option), TupleNewtypeSeq(Vec), TupleNewtypeMap(HashMap), } #[derive(Debug, Deserialize, Serialize, Eq, PartialEq)] struct Unit; #[derive(Debug, Deserialize, Serialize, Eq, PartialEq)] struct Newtype(i32); #[derive(Debug, Deserialize, Serialize, Eq, PartialEq)] struct TupleStruct(u32, bool); #[derive(Debug, Deserialize, Serialize, Eq, PartialEq)] struct Struct { a: u32, b: bool, } #[derive(Debug, Deserialize, Serialize, Eq, PartialEq)] enum Enum { A, B(Struct), C(u32, bool), D { a: u32, b: bool }, } #[test] fn test_deserialise_non_newtypes() { assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] Unit"#).unwrap(), TestEnum::Unit, ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] PrimitiveNewtype("hi")"#) .unwrap(), TestEnum::PrimitiveNewtype(String::from("hi")), ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] Tuple(4, false)"#).unwrap(), TestEnum::Tuple(4, false), ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] Struct(a: 4, b: false)"#) .unwrap(), TestEnum::Struct { a: 4, b: false }, ); } #[test] fn test_deserialise_tuple_newtypes() { assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeUnit(Unit)"#) .unwrap_err() .code, Error::ExpectedStructLikeEnd, ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeUnit(())"#) .unwrap_err() .code, Error::ExpectedStructLikeEnd, ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeUnit()"#).unwrap(), TestEnum::TupleNewtypeUnit(Unit), ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeNewtype(Newtype(4))"# ) .unwrap_err() .code, Error::ExpectedInteger, ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeNewtype((4))"#) .unwrap_err() .code, Error::ExpectedInteger, ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeNewtype(4)"#) .unwrap(), TestEnum::TupleNewtypeNewtype(Newtype(4)), ); assert_eq!( from_str::(r#"#![enable(unwrap_newtypes)] TupleNewtypeNewtype(4)"#).unwrap(), TestEnum::TupleNewtypeNewtype(Newtype(4)), ); assert_eq!( from_str::(r#"#![enable(unwrap_newtypes)] #![enable(unwrap_variant_newtypes)] TupleNewtypeNewtype(4)"#).unwrap(), TestEnum::TupleNewtypeNewtype(Newtype(4)), ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeTuple((4, false))"# ) .unwrap_err() .code, Error::ExpectedInteger, ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeTuple(4, false)"#) .unwrap(), TestEnum::TupleNewtypeTuple((4, false)), ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeTupleStruct(TupleStruct(4, false))"# ) .unwrap_err() .code, Error::ExpectedInteger, ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeTupleStruct((4, false))"# ) .unwrap_err() .code, Error::ExpectedInteger, ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeTupleStruct(4, false)"# ) .unwrap(), TestEnum::TupleNewtypeTupleStruct(TupleStruct(4, false)), ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeStruct(Struct(a: 4, b: false))"# ) .unwrap_err() .code, Error::ExpectedMapColon, ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeStruct((a: 4, b: false))"# ) .unwrap_err() .code, Error::ExpectedIdentifier, ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeStruct(a: 4, b: false)"# ) .unwrap(), TestEnum::TupleNewtypeStruct(Struct { a: 4, b: false }), ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeEnum(A)"#).unwrap(), TestEnum::TupleNewtypeEnum(Enum::A), ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeEnum(B(a: 4, b: false))"# ) .unwrap(), TestEnum::TupleNewtypeEnum(Enum::B(Struct { a: 4, b: false })), ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeEnum(C 4, false)"#) .unwrap_err() .code, Error::ExpectedStructLike, ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeEnum(C(4, false))"# ) .unwrap(), TestEnum::TupleNewtypeEnum(Enum::C(4, false)), ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeEnum(D a: 4, b: false)"# ) .unwrap_err() .code, Error::ExpectedStructLike, ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeEnum(D(a: 4, b: false))"# ) .unwrap(), TestEnum::TupleNewtypeEnum(Enum::D { a: 4, b: false }), ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeOption(None)"#) .unwrap(), TestEnum::TupleNewtypeOption(None), ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeOption(Some(a: 4, b: false))"# ) .unwrap(), TestEnum::TupleNewtypeOption(Some(Struct { a: 4, b: false })), ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeOption(a: 4, b: false)"# ) .unwrap_err() .code, Error::ExpectedOption, ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes, implicit_some)] TupleNewtypeOption(a: 4, b: false)"#).unwrap(), TestEnum::TupleNewtypeOption(Some(Struct { a: 4, b: false })), ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeSeq([])"#).unwrap(), TestEnum::TupleNewtypeSeq(vec![]), ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeSeq([(a: 4, b: false)])"# ) .unwrap(), TestEnum::TupleNewtypeSeq(vec![Struct { a: 4, b: false }]), ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeSeq([Struct(a: 4, b: false)])"# ) .unwrap(), TestEnum::TupleNewtypeSeq(vec![Struct { a: 4, b: false }]), ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeMap({})"#).unwrap(), TestEnum::TupleNewtypeMap(vec![].into_iter().collect()), ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeMap({2: (a: 4, b: false)})"# ) .unwrap(), TestEnum::TupleNewtypeMap(vec![(2, Struct { a: 4, b: false })].into_iter().collect()), ); assert_eq!( from_str::( r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeMap({8: Struct(a: 4, b: false)})"# ) .unwrap(), TestEnum::TupleNewtypeMap(vec![(8, Struct { a: 4, b: false })].into_iter().collect()), ); } #[test] fn test_newtype_some() { assert_eq!( from_str::>(r#"Some((a: 4, b: false))"#).unwrap(), Some(Struct { a: 4, b: false }), ); assert_eq!( from_str::>(r#"Some(a: 4, b: false)"#) .unwrap_err() .code, Error::ExpectedDifferentStructName { expected: "Struct", found: String::from("a"), }, ); assert_eq!( from_str::>(r#"#![enable(unwrap_variant_newtypes)] Some(a: 4, b: false)"#) .unwrap(), Some(Struct { a: 4, b: false }), ); } #[test] fn test_serialise_non_newtypes() { assert_eq_serialize_roundtrip(TestEnum::Unit, Extensions::UNWRAP_VARIANT_NEWTYPES); assert_eq_serialize_roundtrip( TestEnum::PrimitiveNewtype(String::from("hi")), Extensions::UNWRAP_VARIANT_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::Tuple(4, false), Extensions::UNWRAP_VARIANT_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::Struct { a: 4, b: false }, Extensions::UNWRAP_VARIANT_NEWTYPES, ); } #[test] fn test_serialise_tuple_newtypes() { assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeUnit(Unit), Extensions::UNWRAP_VARIANT_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeNewtype(Newtype(4)), Extensions::UNWRAP_VARIANT_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeNewtype(Newtype(4)), Extensions::UNWRAP_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeNewtype(Newtype(4)), Extensions::UNWRAP_VARIANT_NEWTYPES | Extensions::UNWRAP_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeTuple((4, false)), Extensions::UNWRAP_VARIANT_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeTupleStruct(TupleStruct(4, false)), Extensions::UNWRAP_VARIANT_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeStruct(Struct { a: 4, b: false }), Extensions::UNWRAP_VARIANT_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeEnum(Enum::A), Extensions::UNWRAP_VARIANT_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeEnum(Enum::B(Struct { a: 4, b: false })), Extensions::UNWRAP_VARIANT_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeEnum(Enum::C(4, false)), Extensions::UNWRAP_VARIANT_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeEnum(Enum::D { a: 4, b: false }), Extensions::UNWRAP_VARIANT_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeOption(None), Extensions::UNWRAP_VARIANT_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeOption(Some(Struct { a: 4, b: false })), Extensions::UNWRAP_VARIANT_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeOption(Some(Struct { a: 4, b: false })), Extensions::IMPLICIT_SOME, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeOption(Some(Struct { a: 4, b: false })), Extensions::UNWRAP_VARIANT_NEWTYPES | Extensions::IMPLICIT_SOME, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeSeq(vec![]), Extensions::UNWRAP_VARIANT_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeSeq(vec![Struct { a: 4, b: false }]), Extensions::UNWRAP_VARIANT_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeMap(vec![].into_iter().collect()), Extensions::UNWRAP_VARIANT_NEWTYPES, ); assert_eq_serialize_roundtrip( TestEnum::TupleNewtypeMap(vec![(2, Struct { a: 4, b: false })].into_iter().collect()), Extensions::UNWRAP_VARIANT_NEWTYPES, ); } fn assert_eq_serialize_roundtrip< S: Serialize + serde::de::DeserializeOwned + PartialEq + core::fmt::Debug, >( value: S, extensions: Extensions, ) { let ron = to_string_pretty(&value, PrettyConfig::default().extensions(extensions)).unwrap(); let result = from_str::(&ron); assert_eq!(result.as_ref(), Ok(&value), "{}", ron,); } ron-0.12.0/tests/254_typetag.rs000064400000000000000000000102631046102023000142630ustar 00000000000000use core::fmt::Write; use serde::{Deserialize, Serialize}; #[test] fn typetag_usage() { #[derive(Deserialize, Serialize, Debug)] struct Component1 { value: f32, value_2: f32, } #[derive(Deserialize, Serialize, Debug)] struct Component2 { value: f32, } #[typetag::serde(tag = "type")] trait MyTrait: core::fmt::Debug { fn do_stuff(&self, buffer: &mut String); } #[typetag::serde] impl MyTrait for Component1 { fn do_stuff(&self, buffer: &mut String) { let _ = writeln!(buffer, "{:#?}", self); } } #[typetag::serde] impl MyTrait for Component2 { fn do_stuff(&self, buffer: &mut String) { let _ = writeln!(buffer, "{:#?}", self); } } #[derive(Deserialize, Serialize, Debug)] struct EntityConfig { name: String, components: Vec>, } let ron_config = r#"EntityConfig( name: "some name", components: [ { "type": "Component1", "value": 22.0, "value_2": 35.0, }, { "type": "Component2", "value": 3.14, }, ], )"#; let entity_config: EntityConfig = ron::from_str(ron_config).unwrap(); assert_eq!(entity_config.name, "some name"); assert_eq!(entity_config.components.len(), 2); let mut buffer = String::new(); for component in &entity_config.components { component.do_stuff(&mut buffer); } assert_eq!( buffer, r#"Component1 { value: 22.0, value_2: 35.0, } Component2 { value: 3.14, } "# ); let ron = ron::ser::to_string_pretty( &entity_config, ron::ser::PrettyConfig::default().struct_names(true), ) .unwrap(); assert_eq!(ron, ron_config); } #[test] fn typetag_with_enum() { #[derive(Deserialize, Serialize, Debug)] enum SampleTestEnum { Test(i32), AnotherTest(f32, f32), } #[derive(Deserialize, Serialize, Debug)] struct Component1 { value: f32, value_2: f32, something: SampleTestEnum, } #[derive(Deserialize, Serialize, Debug)] struct Component2 { value: f32, } #[typetag::serde(tag = "type")] trait MyTrait: core::fmt::Debug { fn do_stuff(&self, buffer: &mut String); } #[typetag::serde] impl MyTrait for Component1 { fn do_stuff(&self, buffer: &mut String) { match self.something { SampleTestEnum::Test(number) => { let _ = writeln!(buffer, "my number: {:#?}", number); } SampleTestEnum::AnotherTest(float_1, float_2) => { let _ = writeln!(buffer, "f1: {:#?}, f2: {:#?}", float_1, float_2); } } } } #[typetag::serde] impl MyTrait for Component2 { fn do_stuff(&self, buffer: &mut String) { let _ = writeln!(buffer, "{:#?}", self.value); } } #[derive(Deserialize, Serialize, Debug)] struct EntityConfig { name: String, components: Vec>, } let ron_config = r#"EntityConfig( name: "some other name", components: [ { "type": "Component1", "value": 22.0, "value_2": 35.0, "something": Test(22), }, { "type": "Component1", "value": 12.0, "value_2": 11.0, "something": AnotherTest(11.0, 22.0), }, { "type": "Component2", "value": 3.1, }, ], )"#; let entity_config: EntityConfig = ron::from_str(ron_config).unwrap(); assert_eq!(entity_config.name, "some other name"); assert_eq!(entity_config.components.len(), 3); let mut buffer = String::new(); for component in &entity_config.components { component.do_stuff(&mut buffer); } assert_eq!( buffer, r#"my number: 22 f1: 11.0, f2: 22.0 3.1 "# ); let ron = ron::ser::to_string_pretty( &entity_config, ron::ser::PrettyConfig::default().struct_names(true), ) .unwrap(); assert_eq!(ron, ron_config); } ron-0.12.0/tests/256_comma_error.rs000064400000000000000000000055761046102023000151300ustar 00000000000000#![allow(dead_code)] use ron::error::{Error, Position, Span, SpannedError}; #[cfg(feature = "internal-span-substring-test")] use ron::util::span_substring::check_error_span_inclusive; #[derive(Debug, serde::Deserialize)] struct Test { a: i32, b: i32, } #[test] fn test_missing_comma_error() { let tuple_string = r#"( 1 // <-- forgotten comma here 2 )"#; assert_eq!( ron::from_str::<(i32, i32)>(tuple_string).unwrap_err(), SpannedError { code: Error::ExpectedComma, span: Span { start: Position { line: 3, col: 9 }, end: Position { line: 3, col: 9 } } } ); let list_string = r#"[ 0, 1 // <-- forgotten comma here 2 ]"#; assert_eq!( ron::from_str::>(list_string).unwrap_err(), SpannedError { code: Error::ExpectedComma, span: Span { start: Position { line: 4, col: 9 }, end: Position { line: 4, col: 9 } } } ); let struct_string = r#"Test( a: 1 // <-- forgotten comma here b: 2 )"#; assert_eq!( ron::from_str::(struct_string).unwrap_err(), SpannedError { code: Error::ExpectedComma, span: Span { start: Position { line: 3, col: 9 }, end: Position { line: 3, col: 9 } } } ); let map_string = r#"{ "a": 1 // <-- forgotten comma here "b": 2 }"#; assert_eq!( ron::from_str::>(map_string).unwrap_err(), SpannedError { code: Error::ExpectedComma, span: Span { start: Position { line: 3, col: 9 }, end: Position { line: 3, col: 9 } } } ); let extensions_string = r#"#![enable( implicit_some // <-- forgotten comma here unwrap_newtypes ]) 42"#; assert_eq!( ron::from_str::(extensions_string).unwrap_err(), SpannedError { code: Error::ExpectedComma, span: Span { start: Position { line: 2, col: 50 }, end: Position { line: 3, col: 9 } } } ); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::( extensions_string, Err(SpannedError { code: Error::ExpectedComma, span: Span { start: Position { line: 2, col: 50 }, end: Position { line: 3, col: 9 }, }, }), "\n u", ); } #[test] fn test_comma_end() { assert_eq!(ron::from_str::<(i32, i32)>("(0, 1)").unwrap(), (0, 1)); assert_eq!(ron::from_str::<(i32, i32)>("(0, 1,)").unwrap(), (0, 1)); assert_eq!(ron::from_str::<()>("()"), Ok(())); } ron-0.12.0/tests/289_enumerate_arrays.rs000064400000000000000000000022511046102023000161620ustar 00000000000000const EXPTECTED: &str = "[ /*[0]*/ None, /*[1]*/ Some([]), /*[2]*/ Some([ /*[0]*/ 42, ]), /*[3]*/ Some([ /*[0]*/ 4, /*[1]*/ 2, ]), /*[4]*/ None, ]"; const EXPTECTED_COMPACT: &str = "[/*[0]*/ None, /*[1]*/ Some([]), /*[2]*/ Some([/*[0]*/ 42]), \ /*[3]*/ Some([/*[0]*/ 4, /*[1]*/ 2]), /*[4]*/ None]"; #[test] fn enumerate_arrays() { let v: Vec>> = vec![None, Some(vec![]), Some(vec![42]), Some(vec![4, 2]), None]; let pretty = ron::ser::PrettyConfig::new().enumerate_arrays(true); let ser = ron::ser::to_string_pretty(&v, pretty).unwrap(); assert_eq!(ser, EXPTECTED); let de: Vec>> = ron::from_str(&ser).unwrap(); assert_eq!(v, de) } #[test] fn enumerate_compact_arrays() { let v: Vec>> = vec![None, Some(vec![]), Some(vec![42]), Some(vec![4, 2]), None]; let pretty = ron::ser::PrettyConfig::new() .enumerate_arrays(true) .compact_arrays(true); let ser = ron::ser::to_string_pretty(&v, pretty).unwrap(); assert_eq!(ser, EXPTECTED_COMPACT); let de: Vec>> = ron::from_str(&ser).unwrap(); assert_eq!(v, de) } ron-0.12.0/tests/301_struct_name_mismatch.rs000064400000000000000000000110031046102023000170010ustar 00000000000000use ron::error::{Error, Position, Span, SpannedError}; use serde::Deserialize; #[derive(Debug, Deserialize, PartialEq)] struct MyUnitStruct; #[derive(Debug, Deserialize, PartialEq)] struct MyTupleStruct(bool, i32); #[derive(Debug, Deserialize, PartialEq)] struct MyNewtypeStruct(MyTupleStruct); #[derive(Debug, Deserialize, PartialEq)] struct MyStruct { a: bool, b: i32, } #[test] fn test_unit_struct_name_mismatch() { assert_eq!(ron::from_str::("()"), Ok(MyUnitStruct),); assert_eq!( ron::from_str::("MyUnitStruct"), Ok(MyUnitStruct), ); assert_eq!( ron::from_str::("MyUnit Struct"), Err(SpannedError { code: Error::ExpectedDifferentStructName { expected: "MyUnitStruct", found: String::from("MyUnit") }, span: Span { start: ron::error::Position { line: 1, col: 1 }, end: Position { line: 1, col: 7 } } }), ); assert_eq!( ron::from_str::("42"), Err(SpannedError { code: Error::ExpectedNamedStructLike("MyUnitStruct"), span: Span { start: ron::error::Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 } } }), ); } #[test] fn test_tuple_struct_name_mismatch() { assert_eq!( ron::from_str::("(true, 42)"), Ok(MyTupleStruct(true, 42)), ); assert_eq!( ron::from_str::("MyTupleStruct(true, 42)"), Ok(MyTupleStruct(true, 42)), ); assert_eq!( ron::from_str::("MyTypleStruct(true, 42)"), Err(SpannedError { code: Error::ExpectedDifferentStructName { expected: "MyTupleStruct", found: String::from("MyTypleStruct") }, span: Span { start: ron::error::Position { line: 1, col: 1 }, end: Position { line: 1, col: 14 } } }), ); assert_eq!( ron::from_str::("42"), Err(SpannedError { code: Error::ExpectedNamedStructLike("MyTupleStruct"), span: Span { start: ron::error::Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 } } }), ); } #[test] fn test_newtype_struct_name_mismatch() { assert_eq!( ron::from_str::("((true, 42))"), Ok(MyNewtypeStruct(MyTupleStruct(true, 42))), ); assert_eq!( ron::from_str::("MyNewtypeStruct((true, 42))"), Ok(MyNewtypeStruct(MyTupleStruct(true, 42))), ); assert_eq!( ron::from_str::("MyNewtypeStrucl((true, 42))"), Err(SpannedError { code: Error::ExpectedDifferentStructName { expected: "MyNewtypeStruct", found: String::from("MyNewtypeStrucl") }, span: Span { start: ron::error::Position { line: 1, col: 1 }, end: Position { line: 1, col: 16 } } }), ); assert_eq!( ron::from_str::("42"), Err(SpannedError { code: Error::ExpectedNamedStructLike("MyNewtypeStruct"), span: Span { start: ron::error::Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 } } }), ); } #[test] fn test_struct_name_mismatch() { assert_eq!( ron::from_str::("(a: true, b: 42)"), Ok(MyStruct { a: true, b: 42 }), ); assert_eq!( ron::from_str::("MyStruct(a: true, b: 42)"), Ok(MyStruct { a: true, b: 42 }), ); assert_eq!( ron::from_str::("MuStryct(a: true, b: 42)"), Err(SpannedError { code: Error::ExpectedDifferentStructName { expected: "MyStruct", found: String::from("MuStryct") }, span: Span { start: ron::error::Position { line: 1, col: 1 }, end: Position { line: 1, col: 9 } } }), ); assert_eq!( ron::from_str::("42"), Err(SpannedError { code: Error::ExpectedNamedStructLike("MyStruct"), span: Span { start: ron::error::Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 } } }), ); } ron-0.12.0/tests/307_stack_overflow.rs000064400000000000000000000167361046102023000156500ustar 00000000000000use serde::Serialize; #[test] fn test_deeply_nested_map() { let err = ron::from_str::(&"{".repeat(10_000)).unwrap_err(); assert_eq!(err.code, ron::Error::ExceededRecursionLimit) } #[derive(Serialize)] struct SelfReferential<'a> { inner: Option<&'a SelfReferential<'a>>, } #[test] fn test_deeply_nested_struct() { let mut value = SelfReferential { inner: None }; let ptr = &value as *const _; value.inner = Some(unsafe { &*ptr }); let err = ron::to_string(&value).unwrap_err(); assert_eq!(err, ron::Error::ExceededRecursionLimit) } #[test] fn test_fuzz_4797530127073280() { let ron = "{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{({{[[[[[[[{{[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{({{[[[[[[[{{[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[({{{{{{{{{{({{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{ {("; let err = ron::from_str::(ron).unwrap_err(); assert_eq!(err.code, ron::Error::ExceededRecursionLimit) } #[test] fn test_fuzz_4592535633002496() { let ron = "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{H{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{H{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{[{{P"; let err = ron::from_str::(ron).unwrap_err(); assert_eq!(err.code, ron::Error::ExceededRecursionLimit) } #[test] fn test_fuzz_4740606375362560() { let ron = "{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{({{{{{{{{{{{{{{{{{{{{{{{{{ {("; let err = ron::from_str::(ron).unwrap_err(); assert_eq!(err.code, ron::Error::ExceededRecursionLimit) } ron-0.12.0/tests/321_unicode_ident.rs000064400000000000000000000013031046102023000154050ustar 00000000000000use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, PartialEq, Serialize)] enum EnumWithUnicode { Äöß, 你好世界, } #[test] fn roundtrip_unicode_ident() { let value = [EnumWithUnicode::Äöß, EnumWithUnicode::你好世界]; let ron = ron::ser::to_string(&value).unwrap(); assert_eq!(ron, "(Äöß,你好世界)"); let de = ron::de::from_str(&ron); assert_eq!(Ok(value), de); } #[test] fn fuzzer_issues() { assert_eq!( ron::from_str::("(__: ())").unwrap(), ron::Value::Map( [(ron::Value::String(String::from("__")), ron::Value::Unit)] .into_iter() .collect() ) ); } ron-0.12.0/tests/322_escape_idents.rs000064400000000000000000000015541046102023000154130ustar 00000000000000use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "kebab-case")] enum MyEnumWithDashes { ThisIsMyUnitVariant, ThisIsMyTupleVariant(bool, i32), } #[derive(Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "kebab-case")] struct MyStructWithDashes { my_enum: MyEnumWithDashes, #[serde(rename = "2nd")] my_enum2: MyEnumWithDashes, will_be_renamed: u32, } #[test] fn roundtrip_ident_with_dash() { let value = MyStructWithDashes { my_enum: MyEnumWithDashes::ThisIsMyUnitVariant, my_enum2: MyEnumWithDashes::ThisIsMyTupleVariant(false, -3), will_be_renamed: 32, }; let serial = ron::ser::to_string(&value).unwrap(); println!("Serialized: {}", serial); let deserial = ron::de::from_str(&serial); assert_eq!(Ok(value), deserial); } ron-0.12.0/tests/337_value_float_roundtrip.rs000064400000000000000000000013541046102023000172200ustar 00000000000000#[test] fn roundtrip_value_float_with_decimals() { let v: ron::Value = ron::from_str("1.0").unwrap(); assert_eq!(v, ron::Value::Number(1.0_f32.into())); let ser = ron::ser::to_string(&v).unwrap(); let roundtrip = ron::from_str(&ser).unwrap(); assert_eq!(v, roundtrip); } #[test] #[allow(clippy::float_cmp)] fn roundtrip_value_float_into() { let v: ron::Value = ron::from_str("1.0").unwrap(); assert_eq!(v, ron::Value::Number(1.0_f32.into())); let ser = ron::ser::to_string(&v).unwrap(); let f1: f64 = ron::from_str(&ser).unwrap(); assert_eq!(f1, 1.0_f64); let roundtrip: ron::Value = ron::from_str(&ser).unwrap(); let f2: f64 = roundtrip.into_rust().unwrap(); assert_eq!(f2, 1.0_f64); } ron-0.12.0/tests/357_untagged_enum_roundtrip.rs000064400000000000000000000013731046102023000175440ustar 00000000000000use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] enum MyValue { Int(i64), String(String), Enum(Enum), List(Vec), } #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] enum Enum { First(String), Second(i64), } #[test] fn untagged_enum_not_a_list() { // Contributed by @obi1kenobi in https://github.com/ron-rs/ron/issues/357 let value = MyValue::Enum(Enum::First("foo".to_string())); let ron = ron::to_string(&value).unwrap(); assert_eq!(ron, "First(\"foo\")"); let de = ron::from_str(&ron).unwrap(); println!("{}", ron); // This used to fail as the value was deserialised as `List([String("foo")])` assert_eq!(value, de); } ron-0.12.0/tests/359_deserialize_seed.rs000064400000000000000000000035531046102023000161200ustar 00000000000000#[test] fn test_deserialize_seed() { // Test adapted from David Tolnay's serde-yaml: // https://github.com/dtolnay/serde-yaml/blob/8a806e316302fd2e6541dccee6d166dd51b689d6/tests/test_de.rs#L357-L392 struct Seed(i64); impl<'de> serde::de::DeserializeSeed<'de> for Seed { type Value = i64; fn deserialize(self, deserializer: D) -> Result where D: serde::de::Deserializer<'de>, { struct Visitor(i64); impl<'de> serde::de::Visitor<'de> for Visitor { type Value = i64; fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { write!(formatter, "an integer") } fn visit_i64(self, v: i64) -> Result { Ok(v * self.0) } fn visit_u64(self, v: u64) -> Result { Ok(v as i64 * self.0) } } deserializer.deserialize_any(Visitor(self.0)) } } let cases = [("3", 5, 15), ("6", 7, 42), ("-5", 9, -45)]; for &(ron, seed, expected) in &cases { let deserialized = ron::Options::default() .from_str_seed(ron, Seed(seed)) .unwrap(); assert_eq!(expected, deserialized); } assert_eq!( ron::Options::default().from_str_seed("'a'", Seed(42)), Err(ron::error::SpannedError { code: ron::Error::InvalidValueForType { expected: String::from("an integer"), found: String::from("the string \"a\""), }, span: ron::error::Span { start: ron::error::Position { line: 1, col: 3 }, end: ron::error::Position { line: 1, col: 4 }, } }) ); } ron-0.12.0/tests/367_implicit_some.rs000064400000000000000000000077101046102023000154530ustar 00000000000000#[derive(Debug, PartialEq, serde::Deserialize, serde::Serialize)] struct MaybeFields { f1: i64, f2: Option, f3: Option>, } #[test] fn test_recursive_implicit_some() { // Test case provided by d86leader in // https://github.com/ron-rs/ron/issues/367#issue-1147920589 let x1: std::result::Result = ron::from_str("#![enable(implicit_some)]\n(f1: 1)"); let x2: std::result::Result = ron::from_str("#![enable(implicit_some)]\n(f1: 1, f2: None, f3: None)"); let x3: std::result::Result = ron::from_str("#![enable(implicit_some)]\n(f1: 1, f2: 2, f3: 3)"); let x4: std::result::Result = ron::from_str("#![enable(implicit_some)]\n(f1: 1, f2: 2, f3: Some(3))"); let x5: std::result::Result = ron::from_str("#![enable(implicit_some)]\n(f1: 1, f2: 2, f3: Some(Some(3)))"); let x6: std::result::Result = ron::from_str("#![enable(implicit_some)]\n(f1: 1, f2: 2, f3: Some(None))"); assert_eq!( x1, Ok(MaybeFields { f1: 1, f2: None, f3: None }) ); assert_eq!( x2, Ok(MaybeFields { f1: 1, f2: None, f3: None }) ); assert_eq!( x3, Ok(MaybeFields { f1: 1, f2: Some(2), f3: Some(Some(3)) }) ); assert_eq!( x4, Ok(MaybeFields { f1: 1, f2: Some(2), f3: Some(Some(3)) }) ); assert_eq!( x5, Ok(MaybeFields { f1: 1, f2: Some(2), f3: Some(Some(3)) }) ); assert_eq!( x6, Ok(MaybeFields { f1: 1, f2: Some(2), f3: Some(None) }) ); } #[test] fn test_nested_implicit_some() { assert_eq!( ron::from_str::>>>("#![enable(implicit_some)]\n5"), Ok(Some(Some(Some(5)))) ); assert_eq!( ron::from_str::>>>("#![enable(implicit_some)]\nNone"), Ok(None) ); assert_eq!( ron::from_str::>>>("#![enable(implicit_some)]\nSome(5)"), Ok(Some(Some(Some(5)))) ); assert_eq!( ron::from_str::>>>("#![enable(implicit_some)]\nSome(None)"), Ok(Some(None)) ); assert_eq!( ron::from_str::>>>("#![enable(implicit_some)]\nSome(Some(5))"), Ok(Some(Some(Some(5)))) ); assert_eq!( ron::from_str::>>>("#![enable(implicit_some)]\nSome(Some(None))"), Ok(Some(Some(None))) ); assert_eq!( ron::from_str::>>>( "#![enable(implicit_some)]\nSome(Some(Some(5)))" ), Ok(Some(Some(Some(5)))) ); } #[test] fn fuzzer_found_issues() { #[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] struct Some_(); #[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] struct None_(); let ser_some = ron::ser::to_string_pretty( &Some(Some_()), ron::ser::PrettyConfig::default() .struct_names(true) .extensions(ron::extensions::Extensions::IMPLICIT_SOME), ) .unwrap(); assert_eq!(ser_some, "#![enable(implicit_some)]\nSome_()"); let ser_none = ron::ser::to_string_pretty( &Some(None_()), ron::ser::PrettyConfig::default() .struct_names(true) .extensions(ron::extensions::Extensions::IMPLICIT_SOME), ) .unwrap(); assert_eq!(ser_none, "#![enable(implicit_some)]\nNone_()"); assert_eq!( ron::from_str::>(&ser_some).unwrap(), Some(Some_()) ); assert_eq!( ron::from_str::>(&ser_none).unwrap(), Some(None_()) ); } ron-0.12.0/tests/370_float_parsing.rs000064400000000000000000000035301046102023000154340ustar 00000000000000use ron::{ de::{Position, Span, SpannedError}, Error, }; #[test] fn test_float_literal_parsing() { assert_eq!(ron::from_str("inf"), Ok(f64::INFINITY)); assert_eq!(ron::from_str("+inf"), Ok(f64::INFINITY)); assert_eq!(ron::from_str("-inf"), Ok(f64::NEG_INFINITY)); assert!(ron::from_str::("NaN").unwrap().is_nan()); assert!(ron::from_str::("+NaN").unwrap().is_nan()); assert!(ron::from_str::("-NaN").unwrap().is_nan()); assert_eq!(ron::from_str("1"), Ok(1.0_f64)); assert_eq!(ron::from_str("+1"), Ok(1.0_f64)); assert_eq!(ron::from_str("-1"), Ok(-1.0_f64)); assert_eq!(ron::from_str("1e3"), Ok(1000.0_f64)); assert_eq!(ron::from_str("1e+1"), Ok(10.0_f64)); assert_eq!(ron::from_str("7E-1"), Ok(0.7_f64)); assert_eq!(ron::from_str("1."), Ok(1.0_f64)); assert_eq!(ron::from_str("+1.1"), Ok(1.1_f64)); assert_eq!(ron::from_str("-1.42"), Ok(-1.42_f64)); assert_eq!(ron::from_str("-1.5e3"), Ok(-1500.0_f64)); assert_eq!(ron::from_str("1.e+1"), Ok(10.0_f64)); assert_eq!(ron::from_str("7.4E-1"), Ok(0.74_f64)); assert_eq!(ron::from_str(".1"), Ok(0.1_f64)); assert_eq!(ron::from_str("+.1"), Ok(0.1_f64)); assert_eq!(ron::from_str("-.42"), Ok(-0.42_f64)); assert_eq!(ron::from_str("-.5e3"), Ok(-500.0_f64)); assert_eq!(ron::from_str(".3e+1"), Ok(3.0_f64)); assert_eq!(ron::from_str(".4E-1"), Ok(0.04_f64)); assert_eq!(ron::from_str("1_0.1_0"), Ok(10.1_f32),); assert_eq!(ron::from_str("1_0.10"), Ok(10.1_f32),); assert_eq!(ron::from_str("10.1_0"), Ok(10.1_f32),); assert_eq!( ron::from_str::("1.0e1.0"), Err(SpannedError { code: Error::ExpectedFloat, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 }, } }) ); } ron-0.12.0/tests/393_serde_errors.rs000064400000000000000000000231751046102023000153160ustar 00000000000000use ron::error::{Position, Span, SpannedError}; #[cfg(feature = "internal-span-substring-test")] use ron::util::span_substring::check_error_span_inclusive; #[cfg(feature = "internal-span-substring-test")] use ron::util::span_substring::check_error_span_exclusive; #[derive(Debug, serde::Deserialize, PartialEq)] #[serde(deny_unknown_fields)] enum TestEnum { StructVariant { a: bool, b: char, c: i32 }, NewtypeVariant(TestStruct), } #[derive(Debug, serde::Deserialize, PartialEq)] #[serde(tag = "type")] enum TestEnumInternal { StructVariant { a: bool }, } #[derive(Debug, serde::Deserialize, PartialEq)] #[serde(tag = "type", content = "content")] enum TestEnumAdjacent { StructVariant { a: bool }, } #[derive(Debug, serde::Deserialize, PartialEq)] #[serde(untagged)] enum TestEnumUntagged { StructVariant { a: bool }, } #[derive(Debug, serde::Deserialize, PartialEq)] #[serde(deny_unknown_fields)] struct TestStruct { a: bool, b: char, c: i32, } #[test] fn test_unknown_enum_variant() { assert_eq!( ron::from_str::("NotAVariant"), Err(SpannedError { code: ron::Error::NoSuchEnumVariant { expected: &["StructVariant", "NewtypeVariant"], found: String::from("NotAVariant"), outer: Some(String::from("TestEnum")), }, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 12 }, } }) ); #[cfg(feature = "internal-span-substring-test")] check_error_span_exclusive::( "NotAVariant", Err(SpannedError { code: ron::Error::NoSuchEnumVariant { expected: &["StructVariant", "NewtypeVariant"], found: String::from("NotAVariant"), outer: Some(String::from("TestEnum")), }, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 12 }, }, }), "NotAVariant", ); } #[test] fn test_struct_enum_fields() { let bogus_struct = "StructVariant(a: true, b: 'b', c: -42, d: \"gotcha\")"; let expected_err = Err(SpannedError { code: ron::Error::NoSuchStructField { expected: &["a", "b", "c"], found: String::from("d"), outer: Some(String::from("StructVariant")), }, span: Span { start: Position { line: 1, col: 40 }, end: Position { line: 1, col: 41 }, }, }); assert_eq!(ron::from_str::(bogus_struct), expected_err); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::(bogus_struct, expected_err, "d:"); let bogus_struct = "StructVariant(a: true, c: -42)"; let expected_err = Err(SpannedError { code: ron::Error::MissingStructField { field: "b", outer: Some(String::from("StructVariant")), }, span: Span { start: Position { line: 1, col: 30 }, end: Position { line: 1, col: 30 }, }, }); assert_eq!(ron::from_str::(bogus_struct), expected_err); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::(bogus_struct, expected_err, ")"); let bogus_struct = "StructVariant(a: true, b: 'b', a: false, c: -42)"; let expected_err = Err(SpannedError { code: ron::Error::DuplicateStructField { field: "a", outer: Some(String::from("StructVariant")), }, span: Span { start: Position { line: 1, col: 32 }, end: Position { line: 1, col: 33 }, }, }); assert_eq!(ron::from_str::(bogus_struct), expected_err); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::(bogus_struct, expected_err, "a:"); } #[test] fn test_newtype_enum_fields() { let bogus_struct = "#![enable(unwrap_variant_newtypes)] NewtypeVariant(a: true, b: 'b', c: -42, d: \"gotcha\")"; let expected_err = Err(SpannedError { code: ron::Error::NoSuchStructField { expected: &["a", "b", "c"], found: String::from("d"), outer: Some(String::from("NewtypeVariant")), }, span: Span { start: Position { line: 1, col: 77 }, end: Position { line: 1, col: 78 }, }, }); assert_eq!(ron::from_str::(bogus_struct), expected_err); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::(bogus_struct, expected_err, "d:"); let bogus_struct = "#![enable(unwrap_variant_newtypes)] NewtypeVariant(a: true, c: -42)"; let expected_err = Err(SpannedError { code: ron::Error::MissingStructField { field: "b", outer: Some(String::from("NewtypeVariant")), }, span: Span { start: Position { line: 1, col: 67 }, end: Position { line: 1, col: 67 }, }, }); assert_eq!(ron::from_str::(bogus_struct), expected_err); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::(bogus_struct, expected_err, ")"); let bogus_struct = "#![enable(unwrap_variant_newtypes)] NewtypeVariant(a: true, b: 'b', a: false, c: -42)"; let expected_err = Err(SpannedError { code: ron::Error::DuplicateStructField { field: "a", outer: Some(String::from("NewtypeVariant")), }, span: Span { start: Position { line: 1, col: 69 }, end: Position { line: 1, col: 70 }, }, }); assert_eq!(ron::from_str::(bogus_struct), expected_err); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::(bogus_struct, expected_err, "a:"); } #[test] fn test_struct_fields() { let bogus_struct = "TestStruct(a: true, b: 'b', c: -42, d: \"gotcha\")"; let expected_err = Err(SpannedError { code: ron::Error::NoSuchStructField { expected: &["a", "b", "c"], found: String::from("d"), outer: Some(String::from("TestStruct")), }, span: Span { start: Position { line: 1, col: 37 }, end: Position { line: 1, col: 38 }, }, }); assert_eq!(ron::from_str::(bogus_struct), expected_err); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::(bogus_struct, expected_err, "d:"); assert_eq!( ron::from_str::("TestStruct(a: true, c: -42)"), Err(SpannedError { code: ron::Error::MissingStructField { field: "b", outer: Some(String::from("TestStruct")), }, span: Span { start: Position { line: 1, col: 27 }, end: Position { line: 1, col: 27 }, } }) ); let bogus_struct = "TestStruct(a: true, b: 'b', a: false, c: -42)"; let expected_err = Err(SpannedError { code: ron::Error::DuplicateStructField { field: "a", outer: Some(String::from("TestStruct")), }, span: Span { start: Position { line: 1, col: 29 }, end: Position { line: 1, col: 30 }, }, }); assert_eq!(ron::from_str::(bogus_struct), expected_err); #[cfg(feature = "internal-span-substring-test")] check_error_span_inclusive::(bogus_struct, expected_err, "a:"); } #[test] fn test_internally_tagged_enum() { // Note: Not extracting the variant type is not great, // but at least not wrong either // Since the error occurs in serde-generated user code, // after successfully deserialising, we cannot annotate let bogus_struct = "(type: \"StructVariant\")"; let expected_err = Err(SpannedError { code: ron::Error::MissingStructField { field: "a", outer: None, }, span: Span { start: Position { line: 1, col: 23 }, end: Position { line: 1, col: 24 }, }, }); assert_eq!( ron::from_str::(bogus_struct), expected_err ); #[cfg(feature = "internal-span-substring-test")] check_error_span_exclusive::(bogus_struct, expected_err, ")"); } #[test] fn test_adjacently_tagged_enum() { // Note: TestEnumAdjacent makes sense here since we are now treating // the enum as a struct assert_eq!( ron::from_str::("(type: StructVariant, content: (d: 4))"), Err(SpannedError { code: ron::Error::MissingStructField { field: "a", outer: Some(String::from("TestEnumAdjacent")), }, span: Span { start: Position { line: 1, col: 37 }, end: Position { line: 1, col: 37 }, } }) ); } #[test] fn test_untagged_enum() { // Note: Errors inside untagged enums are not bubbled up let bogus_struct = "(a: true, a: false)"; let expected_err = Err(SpannedError { code: ron::Error::Message(String::from( "data did not match any variant of untagged enum TestEnumUntagged", )), span: Span { start: Position { line: 1, col: 19 }, end: Position { line: 1, col: 20 }, }, }); assert_eq!( ron::from_str::(bogus_struct), expected_err ); #[cfg(feature = "internal-span-substring-test")] check_error_span_exclusive::(bogus_struct, expected_err, ")"); } ron-0.12.0/tests/401_raw_identifier.rs000064400000000000000000000141011046102023000155660ustar 00000000000000use ron::error::{Position, Span, SpannedError}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] #[serde(rename = "Hello World")] struct InvalidStruct; #[derive(Serialize, Deserialize, Debug)] #[serde(rename = "")] struct EmptyStruct; #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] #[serde(rename = "Hello+World")] #[serde(deny_unknown_fields)] struct RawStruct { #[serde(rename = "ab.cd-ef")] field: bool, really_not_raw: i32, } #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] enum RawEnum { #[serde(rename = "Hello-World")] RawVariant, } #[test] fn test_invalid_identifiers() { let ser = ron::ser::to_string_pretty( &InvalidStruct, ron::ser::PrettyConfig::default().struct_names(true), ); assert_eq!( ser, Err(ron::Error::InvalidIdentifier(String::from("Hello World"))) ); let ser = ron::ser::to_string_pretty( &EmptyStruct, ron::ser::PrettyConfig::default().struct_names(true), ); assert_eq!(ser, Err(ron::Error::InvalidIdentifier(String::from("")))); let de = ron::from_str::("Hello World").unwrap_err(); assert_eq!( de, SpannedError { code: ron::Error::ExpectedDifferentStructName { expected: "Hello World", found: String::from("Hello"), }, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 6 }, } } ); let de = ron::from_str::("").unwrap_err(); assert_eq!( de, SpannedError { code: ron::Error::ExpectedUnit, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 }, } } ); let de = ron::from_str::("r#").unwrap_err(); assert_eq!( format!("{}", de), "1:1: Expected only opening `(`, no name, for un-nameable struct" ); let de = ron::from_str::("").unwrap_err(); assert_eq!( de, SpannedError { code: ron::Error::ExpectedNamedStructLike("Hello+World"), span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 }, } }, ); let de = ron::from_str::("r#").unwrap_err(); assert_eq!( de, SpannedError { code: ron::Error::ExpectedNamedStructLike("Hello+World"), span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 }, } }, ); let de = ron::from_str::("Hello+World").unwrap_err(); assert_eq!( de, SpannedError { code: ron::Error::SuggestRawIdentifier(String::from("Hello+World")), span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 }, } } ); let de = ron::from_str::( "r#Hello+World( ab.cd-ef: true, )", ) .unwrap_err(); assert_eq!( de, SpannedError { code: ron::Error::SuggestRawIdentifier(String::from("ab.cd-ef")), span: Span { start: Position { line: 1, col: 15 }, end: Position { line: 2, col: 9 }, } } ); let de = ron::from_str::( "r#Hello+World( rab.cd-ef: true, )", ) .unwrap_err(); assert_eq!( de, SpannedError { code: ron::Error::SuggestRawIdentifier(String::from("rab.cd-ef")), span: Span { start: Position { line: 1, col: 15 }, end: Position { line: 2, col: 9 }, } } ); let de = ron::from_str::( "r#Hello+World( r#ab.cd+ef: true, )", ) .unwrap_err(); assert_eq!( de, SpannedError { code: ron::Error::NoSuchStructField { expected: &["ab.cd-ef", "really_not_raw"], found: String::from("ab.cd+ef"), outer: Some(String::from("Hello+World")), }, span: Span { start: Position { line: 2, col: 11 }, end: Position { line: 2, col: 19 }, } } ); let de = ron::from_str::("Hello-World").unwrap_err(); assert_eq!( de, SpannedError { code: ron::Error::SuggestRawIdentifier(String::from("Hello-World")), span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 }, } } ); let de = ron::from_str::("r#Hello+World").unwrap_err(); assert_eq!( de, SpannedError { code: ron::Error::NoSuchEnumVariant { expected: &["Hello-World"], found: String::from("Hello+World"), outer: Some(String::from("RawEnum")), }, span: Span { start: Position { line: 1, col: 3 }, end: Position { line: 1, col: 14 }, } } ); let de = ron::from_str::("r#+").unwrap_err(); assert_eq!( format!("{}", de), r#"1:3-1:4: Expected only opening `(`, no name, for un-nameable struct"#, ); } #[test] fn test_raw_identifier_roundtrip() { let val = RawStruct { field: true, really_not_raw: 42, }; let ser = ron::ser::to_string_pretty(&val, ron::ser::PrettyConfig::default().struct_names(true)) .unwrap(); assert_eq!( ser, "r#Hello+World(\n r#ab.cd-ef: true,\n really_not_raw: 42,\n)" ); let de: RawStruct = ron::from_str(&ser).unwrap(); assert_eq!(de, val); let val = RawEnum::RawVariant; let ser = ron::ser::to_string(&val).unwrap(); assert_eq!(ser, "r#Hello-World"); let de: RawEnum = ron::from_str(&ser).unwrap(); assert_eq!(de, val); } ron-0.12.0/tests/407_raw_value.rs000064400000000000000000000267451046102023000146070ustar 00000000000000use ron::{ de::from_bytes, error::{Error, Position, Span, SpannedError}, from_str, to_string, value::RawValue, }; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] struct WithRawValue { a: bool, b: Box, } #[test] fn test_raw_value_simple() { let raw: &RawValue = from_str("true").unwrap(); assert_eq!(raw.get_ron(), "true"); let ser = to_string(raw).unwrap(); assert_eq!(ser, "true"); } #[test] fn test_raw_value_inner() { let raw: WithRawValue = from_str("(a: false, b: [1, /* lol */ 2, 3])").unwrap(); assert_eq!(raw.b.get_ron(), " [1, /* lol */ 2, 3]"); let ser = to_string(&raw).unwrap(); assert_eq!(ser, "(a:false,b: [1, /* lol */ 2, 3])"); } #[test] fn test_raw_value_comment() { let raw: WithRawValue = from_str("(a: false, b: /* yeah */ 4)").unwrap(); assert_eq!(raw.b.get_ron(), " /* yeah */ 4"); let raw: WithRawValue = from_str("(a: false, b: 4 /* yes */)").unwrap(); assert_eq!(raw.b.get_ron(), " 4 /* yes */"); let raw: WithRawValue = from_str("(a: false, b: (/* this */ 4 /* too */))").unwrap(); assert_eq!(raw.b.get_ron(), " (/* this */ 4 /* too */)"); } #[test] fn test_raw_value_invalid() { let err = from_str::<&RawValue>("4.d").unwrap_err(); assert_eq!( err, SpannedError { code: Error::TrailingCharacters, span: Span { start: Position { line: 1, col: 3 }, end: Position { line: 1, col: 3 } } } ); let err = from_bytes::<&RawValue>(b"\0").unwrap_err(); assert_eq!( err, SpannedError { code: Error::UnexpectedChar('\0'), span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 } } } ) } #[test] fn test_raw_value_from_ron() { let raw = RawValue::from_ron("/* hi */ (None, 4.2) /* bye */").unwrap(); assert_eq!(raw.get_ron(), "/* hi */ (None, 4.2) /* bye */"); let err = RawValue::from_ron("4.d").unwrap_err(); assert_eq!( err, SpannedError { code: Error::TrailingCharacters, span: Span { start: Position { line: 1, col: 3 }, end: Position { line: 1, col: 3 } } } ); let raw = RawValue::from_boxed_ron(String::from("/* hi */ (None, 4.2) /* bye */").into_boxed_str()) .unwrap(); assert_eq!(raw.get_ron(), "/* hi */ (None, 4.2) /* bye */"); let err = RawValue::from_boxed_ron(String::from("(").into_boxed_str()).unwrap_err(); assert_eq!( err, SpannedError { code: Error::Eof, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 2 }, } } ); } #[test] fn test_raw_value_into_rust() { let raw = RawValue::from_ron("/* hi */ (a: false, b: None) /* bye */").unwrap(); let with: WithRawValue = raw.into_rust().unwrap(); assert_eq!( with, WithRawValue { a: false, b: from_str(" None").unwrap(), } ); let err = raw.into_rust::().unwrap_err(); assert_eq!( err, SpannedError { code: Error::ExpectedInteger, span: Span { start: Position { line: 1, col: 9 }, end: Position { line: 1, col: 10 }, } } ); } #[test] fn test_raw_value_from_rust() { let raw = RawValue::from_rust(&42).unwrap(); assert_eq!(raw.get_ron(), "42"); let raw = RawValue::from_rust(&WithRawValue { a: true, b: from_str("4.2").unwrap(), }) .unwrap(); assert_eq!(raw.get_ron(), "(a:true,b:4.2)"); } #[test] fn test_raw_value_serde_json() { let raw = RawValue::from_ron("/* hi */ (None, 4.2) /* bye */").unwrap(); let ser = serde_json::to_string(&WithRawValue { a: true, b: raw.to_owned(), }) .unwrap(); assert_eq!(ser, "{\"a\":true,\"b\":\"/* hi */ (None, 4.2) /* bye */\"}"); let with: WithRawValue = serde_json::from_str(&ser).unwrap(); assert_eq!(raw, &*with.b); let err = serde_json::from_str::("{\"a\":true,\"b\":\"/* hi */ (a:) /* bye */\"}") .unwrap_err(); assert_eq!( err.to_string(), "invalid RON value at 1:13: Unexpected char ')' at line 1 column 39" ); let err = serde_json::from_str::("{\"a\":true,\"b\":42}").unwrap_err(); assert_eq!( err.to_string(), "invalid type: integer `42`, expected any valid RON-value-string at line 1 column 16" ); let err = serde_json::from_str::<&RawValue>("\"/* hi */ (a:) /* bye */\"").unwrap_err(); assert_eq!( err.to_string(), "invalid RON value at 1:13: Unexpected char ')' at line 1 column 25" ); let err = serde_json::from_str::<&RawValue>("42").unwrap_err(); assert_eq!( err.to_string(), "invalid type: integer `42`, expected any valid borrowed RON-value-string at line 1 column 2" ); } #[test] fn test_raw_value_clone_into() { let raw = RawValue::from_boxed_ron(String::from("(None, 4.2)").into_boxed_str()).unwrap(); let raw2 = raw.clone(); assert_eq!(raw, raw2); let boxed_str: Box = raw2.into(); assert_eq!(&*boxed_str, "(None, 4.2)"); } #[test] fn test_raw_value_debug_display() { let raw = RawValue::from_ron("/* hi */ (None, 4.2) /* bye */").unwrap(); assert_eq!(format!("{}", raw), "/* hi */ (None, 4.2) /* bye */"); assert_eq!( format!("{:#?}", raw), "\ RawValue( /* hi */ (None, 4.2) /* bye */, )\ " ); } #[test] fn test_boxed_raw_value_deserialise_from_string() { let string = serde::de::value::StringDeserializer::::new(String::from("4.2")); let err = <&RawValue>::deserialize(string.clone()).unwrap_err(); assert_eq!( err, Error::InvalidValueForType { expected: String::from("any valid borrowed RON-value-string"), found: String::from("the string \"4.2\""), } ); let boxed_raw = Box::::deserialize(string).unwrap(); assert_eq!(boxed_raw.get_ron(), "4.2"); let string = serde::de::value::StringDeserializer::::new(String::from("[")); let err = Box::::deserialize(string).unwrap_err(); assert_eq!( err, Error::Message(String::from( "invalid RON value at 1:1-1:2: Unexpected end of RON" )) ); } #[test] fn test_whitespace_rountrip_issues_and_trim() { let mut v = WithRawValue { a: true, b: RawValue::from_ron("42 ").unwrap().to_owned(), }; v = ron::from_str(&ron::to_string(&v).unwrap()).unwrap(); assert_eq!(v.b.get_ron(), "42 "); assert_eq!(v.b.trim().get_ron(), "42"); assert_eq!(v.b.to_owned().trim_boxed().get_ron(), "42"); for i in 1..=10 { v = ron::from_str( &ron::ser::to_string_pretty(&v, ron::ser::PrettyConfig::default()).unwrap(), ) .unwrap(); assert_eq!(v.b.get_ron(), &format!("{: <1$}42 ", "", i)); assert_eq!(v.b.trim().get_ron(), "42"); assert_eq!(v.b.to_owned().trim_boxed().get_ron(), "42"); } assert_eq!(RawValue::from_ron("42").unwrap().trim().get_ron(), "42"); assert_eq!( RawValue::from_ron(" /* start */ 42 // end\n ") .unwrap() .trim() .get_ron(), "42" ); assert_eq!( RawValue::from_ron("42") .unwrap() .to_owned() .trim_boxed() .get_ron(), "42" ); assert_eq!( RawValue::from_ron(" /* start */ 42 // end\n ") .unwrap() .to_owned() .trim_boxed() .get_ron(), "42" ); } #[test] fn test_fuzzer_found_issue() { assert_eq!( RawValue::from_ron(""), Err(SpannedError { code: Error::Eof, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 1 }, } }) ); assert_eq!(ron::from_str("C"), RawValue::from_ron("C")); assert_eq!(ron::from_str(" t "), RawValue::from_ron(" t ")); assert_eq!( RawValue::from_ron("#![enable(implicit_some)] 42"), Err(SpannedError { code: Error::Message(String::from( "ron::value::RawValue cannot enable extensions" )), span: Span { start: Position { line: 1, col: 27 }, end: Position { line: 1, col: 27 }, } }) ); assert_eq!( RawValue::from_boxed_ron(String::from("#![enable(implicit_some)] 42").into_boxed_str()), Err(SpannedError { code: Error::Message(String::from( "ron::value::RawValue cannot enable extensions" )), span: Span { start: Position { line: 1, col: 27 }, end: Position { line: 1, col: 27 }, } }) ); assert_eq!( RawValue::from_ron("42 //"), Err(SpannedError { code: Error::UnclosedLineComment, span: Span { start: Position { line: 1, col: 6 }, end: Position { line: 1, col: 6 }, } }) ); assert_eq!( ron::from_str::<&RawValue>("42 //"), Err(SpannedError { code: Error::UnclosedLineComment, span: Span { start: Position { line: 1, col: 6 }, end: Position { line: 1, col: 6 }, } }) ); assert_eq!( ron::from_str::<&RawValue>("42 //\n").unwrap(), RawValue::from_ron("42 //\n").unwrap() ); assert_eq!( ron::from_str::<&RawValue>("42 /**/").unwrap(), RawValue::from_ron("42 /**/").unwrap() ); assert_eq!( ron::from_str::<&RawValue>("42 ").unwrap(), RawValue::from_ron("42 ").unwrap() ); assert_eq!( RawValue::from_ron("a//"), Err(SpannedError { code: Error::UnclosedLineComment, span: Span { start: Position { line: 1, col: 4 }, end: Position { line: 1, col: 4 }, } }) ); assert_eq!( ron::from_str::<&RawValue>("a//"), Err(SpannedError { code: Error::UnclosedLineComment, span: Span { start: Position { line: 1, col: 4 }, end: Position { line: 1, col: 4 }, } }) ); assert_eq!( ron::from_str::<&RawValue>("a//\n").unwrap(), RawValue::from_ron("a//\n").unwrap() ); assert_eq!( ron::from_str::<&RawValue>("a/**/").unwrap(), RawValue::from_ron("a/**/").unwrap() ); assert_eq!( ron::from_str::<&RawValue>("a").unwrap(), RawValue::from_ron("a").unwrap() ); assert_eq!( ron::to_string(&Some(Some(RawValue::from_ron("None").unwrap()))).unwrap(), "Some(Some(None))" ); // Since a RawValue can contain anything, no implicit Some are allowed around it assert_eq!( ron::Options::default() .with_default_extension(ron::extensions::Extensions::IMPLICIT_SOME) .to_string(&Some(Some(RawValue::from_ron("None").unwrap()))) .unwrap(), "Some(Some(None))" ); assert_eq!( ron::from_str::>>("Some(Some(None))").unwrap(), Some(Some(RawValue::from_ron("None").unwrap())) ); } ron-0.12.0/tests/410_trailing_comma.rs000064400000000000000000000050651046102023000155710ustar 00000000000000use std::collections::HashMap; use ron::from_str; use serde::Deserialize; #[derive(Deserialize)] #[allow(dead_code)] struct Newtype(i32); #[derive(Deserialize)] #[allow(dead_code)] struct Tuple(i32, i32); #[derive(Deserialize)] #[allow(dead_code)] struct Struct { a: i32, b: i32, } #[derive(Deserialize)] #[allow(dead_code)] enum Enum { Newtype(i32), Tuple(i32, i32), Struct { a: i32, b: i32 }, } #[test] fn test_trailing_comma_some() { assert!(from_str::>("Some(1)").is_ok()); assert!(from_str::>("Some(1,)").is_ok()); assert!(from_str::>("Some(1,,)").is_err()); } #[test] fn test_trailing_comma_tuple() { assert!(from_str::<(i32, i32)>("(1,2)").is_ok()); assert!(from_str::<(i32, i32)>("(1,2,)").is_ok()); assert!(from_str::<(i32, i32)>("(1,2,,)").is_err()); } #[test] fn test_trailing_comma_list() { assert!(from_str::>("[1,2]").is_ok()); assert!(from_str::>("[1,2,]").is_ok()); assert!(from_str::>("[1,2,,]").is_err()); } #[test] fn test_trailing_comma_map() { assert!(from_str::>("{1:false,2:true}").is_ok()); assert!(from_str::>("{1:false,2:true,}").is_ok()); assert!(from_str::>("{1:false,2:true,,}").is_err()); } #[test] fn test_trailing_comma_newtype_struct() { assert!(from_str::("(1)").is_ok()); assert!(from_str::("(1,)").is_ok()); assert!(from_str::("(1,,)").is_err()); } #[test] fn test_trailing_comma_tuple_struct() { assert!(from_str::("(1,2)").is_ok()); assert!(from_str::("(1,2,)").is_ok()); assert!(from_str::("(1,2,,)").is_err()); } #[test] fn test_trailing_comma_struct() { assert!(from_str::("(a:1,b:2)").is_ok()); assert!(from_str::("(a:1,b:2,)").is_ok()); assert!(from_str::("(a:1,b:2,,)").is_err()); } #[test] fn test_trailing_comma_enum_newtype_variant() { assert!(from_str::("Newtype(1)").is_ok()); assert!(from_str::("Newtype(1,)").is_ok()); assert!(from_str::("Newtype(1,,)").is_err()); } #[test] fn test_trailing_comma_enum_tuple_variant() { assert!(from_str::("Tuple(1,2)").is_ok()); assert!(from_str::("Tuple(1,2,)").is_ok()); assert!(from_str::("Tuple(1,2,,)").is_err()); } #[test] fn test_trailing_comma_enum_struct_variant() { assert!(from_str::("Struct(a:1,b:2)").is_ok()); assert!(from_str::("Struct(a:1,b:2,)").is_ok()); assert!(from_str::("Struct(a:1,b:2,,)").is_err()); } ron-0.12.0/tests/423_de_borrowed_identifier.rs000064400000000000000000000022071046102023000173000ustar 00000000000000use std::any::Any; use serde::{ de::{MapAccess, Visitor}, Deserializer, }; #[test] fn manually_deserialize_dyn() { let ron = r#"SerializeDyn( type: "engine_utils::types::registry::tests::Player", )"#; let mut de = ron::Deserializer::from_bytes(ron.as_bytes()).unwrap(); let result = de .deserialize_struct("SerializeDyn", &["type"], SerializeDynVisitor) .unwrap(); assert_eq!( *result.downcast::>().unwrap(), Some(( String::from("type"), String::from("engine_utils::types::registry::tests::Player") )) ); } struct SerializeDynVisitor; impl<'de> Visitor<'de> for SerializeDynVisitor { type Value = Box; // GRCOV_EXCL_START fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { write!(formatter, "a serialize dyn struct") } // GRCOV_EXCL_STOP fn visit_map>(self, mut map: A) -> Result { let entry = map.next_entry::<&str, String>()?; Ok(Box::new(entry.map(|(k, v)| (String::from(k), v)))) } } ron-0.12.0/tests/425_escape_strings.rs000064400000000000000000000040451046102023000156200ustar 00000000000000use ron::{ de::from_str, ser::{to_string, to_string_pretty, PrettyConfig}, }; fn test_string_roundtrip(s: &str, config: Option) -> String { let ser = match config { Some(config) => to_string_pretty(s, config), None => to_string(s), } .unwrap(); let de: String = from_str(&ser).unwrap(); assert_eq!(s, de); ser } #[test] fn test_escaped_string() { let config = Some(PrettyConfig::default()); assert_eq!(test_string_roundtrip("a\nb", None), r#""a\nb""#); assert_eq!(test_string_roundtrip("a\nb", config.clone()), r#""a\nb""#); assert_eq!(test_string_roundtrip("", None), "\"\""); assert_eq!(test_string_roundtrip("", config.clone()), "\"\""); assert_eq!(test_string_roundtrip("\"", None), r#""\"""#); assert_eq!(test_string_roundtrip("\"", config.clone()), r#""\"""#); assert_eq!(test_string_roundtrip("#", None), "\"#\""); assert_eq!(test_string_roundtrip("#", config.clone()), "\"#\""); assert_eq!(test_string_roundtrip("\"#", None), r##""\"#""##); assert_eq!(test_string_roundtrip("\"#", config.clone()), r##""\"#""##); assert_eq!(test_string_roundtrip("#\"#", None), r##""#\"#""##); assert_eq!(test_string_roundtrip("#\"#", config.clone()), r##""#\"#""##); assert_eq!(test_string_roundtrip("#\"##", None), r###""#\"##""###); assert_eq!(test_string_roundtrip("#\"##", config), r###""#\"##""###); } #[test] fn test_unescaped_string() { let config = Some(PrettyConfig::default().escape_strings(false)); assert_eq!(test_string_roundtrip("a\nb", config.clone()), "\"a\nb\""); assert_eq!(test_string_roundtrip("", config.clone()), "\"\""); assert_eq!(test_string_roundtrip("\"", config.clone()), "r#\"\"\"#"); assert_eq!(test_string_roundtrip("#", config.clone()), "\"#\""); assert_eq!(test_string_roundtrip("\"#", config.clone()), "r##\"\"#\"##"); assert_eq!( test_string_roundtrip("#\"#", config.clone()), "r##\"#\"#\"##" ); assert_eq!(test_string_roundtrip("#\"##", config), "r###\"#\"##\"###"); } ron-0.12.0/tests/436_untagged_bytes.rs000064400000000000000000000047741046102023000156260ustar 00000000000000#[test] fn test_serde_bytes() { #[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] #[serde(rename = "b")] struct BytesVal { pub b: serde_bytes::ByteBuf, } #[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] #[serde(untagged)] enum Bad { Bytes(BytesVal), } let s = ron::to_string(&serde_bytes::Bytes::new(b"test")).unwrap(); assert_eq!(s, r#"b"test""#); let v: Bad = ron::from_str(r#"(b: b"test")"#).unwrap(); assert_eq!( format!("{:?}", v), "Bytes(BytesVal { b: [116, 101, 115, 116] })" ); let s = ron::to_string(&v).unwrap(); assert_eq!(s, r#"(b:b"test")"#); } #[test] fn test_bytes() { #[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] #[serde(rename = "b")] struct BytesVal { pub b: bytes::Bytes, } #[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] #[serde(untagged)] enum Bad { Bytes(BytesVal), } let s = ron::to_string(&bytes::Bytes::from("test")).unwrap(); assert_eq!(s, r#"b"test""#); let v: Bad = ron::from_str(r#"(b: b"test")"#).unwrap(); assert_eq!(format!("{:?}", v), r#"Bytes(BytesVal { b: b"test" })"#); let s = ron::to_string(&v).unwrap(); assert_eq!(s, r#"(b:b"test")"#); } #[test] fn test_strongly_typed_base64() { use base64::engine::{general_purpose::STANDARD as BASE64, Engine}; enum Base64 {} impl Base64 { fn serialize(data: &[u8], serializer: S) -> Result { serializer.serialize_str(&BASE64.encode(data)) } fn deserialize<'de, D: serde::Deserializer<'de>>( deserializer: D, ) -> Result, D::Error> { let base64_str: &str = serde::Deserialize::deserialize(deserializer)?; BASE64.decode(base64_str).map_err(serde::de::Error::custom) } } #[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] #[serde(rename = "b")] struct BytesVal { #[serde(with = "Base64")] pub b: Vec, } #[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] #[serde(untagged)] enum Bad { Bytes(BytesVal), } let v: Bad = ron::from_str(r#"(b: "dGVzdA==")"#).unwrap(); assert_eq!( v, Bad::Bytes(BytesVal { b: b"test".to_vec() }) ); let s = ron::to_string(&v).unwrap(); assert_eq!(s, r#"(b:"dGVzdA==")"#); } ron-0.12.0/tests/438_rusty_byte_strings.rs000064400000000000000000000332071046102023000165770ustar 00000000000000use ron::{ error::{Position, Span, SpannedError}, Error, }; use serde::Deserialize; #[derive(Debug, Deserialize, PartialEq)] struct BytesStruct { small: Vec, #[serde(with = "serde_bytes")] large: Vec, } #[test] fn rusty_byte_string() { assert_eq!( Ok(BytesStruct { small: vec![1, 2], large: vec![1, 2, 0, 4] }), ron::from_str("BytesStruct( small:[1, 2], large: b\"\\x01\\u{2}\\0\\x04\" )"), ); assert_eq!( ron::from_str::("\"Hello \\x01 \\u{2}!\"").unwrap(), "Hello \x01 \u{2}!", ); assert_eq!( &*ron::from_str::("b\"Hello \\x01 \\u{2}!\"").unwrap(), b"Hello \x01 \x02!", ); rusty_byte_string_roundtrip(b"hello", "b\"hello\"", "b\"hello\""); rusty_byte_string_roundtrip(b"\"", "b\"\\\"\"", "br#\"\"\"#"); rusty_byte_string_roundtrip(b"\"#", "b\"\\\"#\"", "br##\"\"#\"##"); rusty_byte_string_roundtrip(b"\n", "b\"\\n\"", "b\"\n\""); assert_eq!( ron::from_str::("b\"\\xf\"").unwrap_err(), SpannedError { code: Error::InvalidEscape("Non-hex digit found"), span: Span { start: Position { line: 1, col: 3 }, end: Position { line: 1, col: 7 }, } }, ); assert_eq!( ron::from_str::("b\"\\xf🦀\"").unwrap_err(), SpannedError { code: Error::InvalidEscape("Non-hex digit found"), span: Span { start: Position { line: 1, col: 3 }, end: Position { line: 1, col: 7 }, } }, ); let err = ron::from_str::("br#q\"").unwrap_err(); assert_eq!( err, SpannedError { code: Error::ExpectedByteString, span: Span { start: Position { line: 1, col: 3 }, end: Position { line: 1, col: 4 }, } }, ); assert_eq!(format!("{}", err.code), "Expected byte string",); assert_eq!( ron::from_str::("br#\"q").unwrap_err(), SpannedError { code: Error::ExpectedStringEnd, span: Span { start: Position { line: 1, col: 4 }, end: Position { line: 1, col: 5 }, } }, ); assert_eq!( ron::from_str::("r#q\"").unwrap_err(), SpannedError { code: Error::ExpectedString, span: Span { start: Position { line: 1, col: 2 }, end: Position { line: 1, col: 3 }, } }, ); assert_eq!( ron::from_str::("r#\"q").unwrap_err(), SpannedError { code: Error::ExpectedStringEnd, span: Span { start: Position { line: 1, col: 3 }, end: Position { line: 1, col: 4 }, } }, ); } fn rusty_byte_string_roundtrip(bytes: &[u8], ron: &str, ron_raw: &str) { let ser_list = ron::to_string(bytes).unwrap(); let de_list: Vec = ron::from_str(&ser_list).unwrap(); assert_eq!(de_list, bytes); let ser = ron::to_string(&bytes::Bytes::copy_from_slice(bytes)).unwrap(); assert_eq!(ser, ron); let ser_non_raw = ron::ser::to_string_pretty( &bytes::Bytes::copy_from_slice(bytes), ron::ser::PrettyConfig::default(), ) .unwrap(); assert_eq!(ser_non_raw, ron); let ser_raw = ron::ser::to_string_pretty( &bytes::Bytes::copy_from_slice(bytes), ron::ser::PrettyConfig::default().escape_strings(false), ) .unwrap(); assert_eq!(ser_raw, ron_raw); let de: bytes::Bytes = ron::from_str(&ser).unwrap(); assert_eq!(de, bytes); let de_raw: bytes::Bytes = ron::from_str(&ser_raw).unwrap(); assert_eq!(de_raw, bytes); } #[test] fn fuzzer_failures() { assert_eq!( ron::to_string(&bytes::Bytes::copy_from_slice(&[ 123, 0, 0, 0, 0, 214, 214, 214, 214, 214 ])) .unwrap(), r#"b"{\x00\x00\x00\x00\xd6\xd6\xd6\xd6\xd6""# ); // Need to fall back to escaping so no invalid UTF-8 is produced assert_eq!( ron::ser::to_string_pretty( &bytes::Bytes::copy_from_slice(&[123, 0, 0, 0, 0, 214, 214, 214, 214, 214]), ron::ser::PrettyConfig::default().escape_strings(false) ) .unwrap(), r#"b"{\x00\x00\x00\x00\xd6\xd6\xd6\xd6\xd6""# ); assert_eq!( ron::to_string(&bytes::Bytes::copy_from_slice(&[123, 0, 0, 0, 0])).unwrap(), r#"b"{\x00\x00\x00\x00""# ); assert_eq!( ron::ser::to_string_pretty( &bytes::Bytes::copy_from_slice(&[123, 0, 0, 0, 0]), ron::ser::PrettyConfig::default().escape_strings(false) ) .unwrap(), "b\"{\x00\x00\x00\x00\"" ); // `br#` should be parsed as the start of a byte string, not the identifier `br` and a `#` assert_eq!( ron::from_str(r##"br#"""#"##), Ok(ron::Value::Bytes(vec![34])) ); assert_eq!( ron::from_str(r##"{"error": br#"""#}"##), Ok(ron::Value::Map( [( ron::Value::String(String::from("error")), ron::Value::Bytes(vec![34]) )] .into_iter() .collect() )) ); assert_eq!( ron::from_str( r##"#![enable(unwrap_newtypes)] #![enable(unwrap_variant_newtypes)] Some({"error": br#"""#}) "## ), Ok(ron::Value::Option(Some(Box::new(ron::Value::Map( [( ron::Value::String(String::from("error")), ron::Value::Bytes(vec![34]) )] .into_iter() .collect() ))))) ); // `br"` should be parsed as the start of a byte string, not the identifier `br` and a `"` assert_eq!(ron::from_str(r#"br"""#), Ok(ron::Value::Bytes(vec![]))); assert_eq!( ron::from_str(r#"{"error": br""}"#), Ok(ron::Value::Map( [( ron::Value::String(String::from("error")), ron::Value::Bytes(vec![]) )] .into_iter() .collect() )) ); assert_eq!( ron::from_str( r#"#![enable(unwrap_newtypes)] #![enable(unwrap_variant_newtypes)] Some({"error": br""}) "# ), Ok(ron::Value::Option(Some(Box::new(ron::Value::Map( [( ron::Value::String(String::from("error")), ron::Value::Bytes(vec![]) )] .into_iter() .collect() ))))) ); // Test that the struct type check for newtype variant unwrapping does // not enter inside a byte string to find a bad comment start assert_eq!( ron::from_str::>( r#"#![enable(unwrap_variant_newtypes)] Some(b"\xff/not a comment")"# ) .unwrap(), Some(ron::Value::Bytes(b"\xff/not a comment".to_vec())) ); // `b'` should be parsed as the start of a byte literal, not the identifier `b` and a `'` assert_eq!( ron::from_str(r"b'\xff'"), Ok(ron::Value::Number(ron::value::Number::U8(b'\xff'))) ); // `b`, `br`, `bq`, and `brq` should all be parsed as identifiers for id in ["b", "br", "bq", "brq"] { assert_eq!(ron::from_str(id), Ok(ron::Value::Unit)); } } #[test] fn serialize_backslash_byte_string() { check_roundtrip('\\', r"'\\'", r"'\\'"); check_roundtrip( bytes::Bytes::copy_from_slice(b"\\"), r#"b"\\""#, "br#\"\\\"#", ); } fn check_roundtrip< T: PartialEq + core::fmt::Debug + serde::Serialize + serde::de::DeserializeOwned, >( val: T, cmp: &str, cmp_raw: &str, ) { let ron = ron::to_string(&val).unwrap(); assert_eq!(ron, cmp); let ron_escaped = ron::ser::to_string_pretty(&val, ron::ser::PrettyConfig::default().escape_strings(true)) .unwrap(); assert_eq!(ron_escaped, cmp); let ron_raw = ron::ser::to_string_pretty( &val, ron::ser::PrettyConfig::default().escape_strings(false), ) .unwrap(); assert_eq!(ron_raw, cmp_raw); let de = ron::from_str::(&ron).unwrap(); assert_eq!(de, val); let de_raw = ron::from_str::(&ron_raw).unwrap(); assert_eq!(de_raw, val); } #[test] fn test_weird_escapes() { assert_eq!( ron::from_str::(r#""\u{1F980}""#), Ok(String::from("\u{1F980}")) ); assert_eq!( ron::from_str::(r#"b"\xf0\x9f\xa6\x80""#), Ok(bytes::Bytes::copy_from_slice("\u{1F980}".as_bytes())) ); assert_eq!( ron::from_str::(r#""\xf0\x9f\xa6\x80""#), Ok(String::from("\u{1F980}")) ); assert_eq!( ron::from_str::(r#""\xf0""#), Err(SpannedError { code: Error::InvalidEscape("Not a valid byte-escaped Unicode character"), span: Span { start: Position { line: 1, col: 2 }, end: Position { line: 1, col: 6 } } }) ); assert_eq!( ron::from_str::(r#""\xf0\x9f""#), Err(SpannedError { code: Error::InvalidEscape("Not a valid byte-escaped Unicode character"), span: Span { start: Position { line: 1, col: 6 }, end: Position { line: 1, col: 10 } } }) ); assert_eq!( ron::from_str::(r#""\xf0\x9f\x40""#), Err(SpannedError { code: Error::InvalidEscape("Not a valid byte-escaped Unicode character"), span: Span { start: Position { line: 1, col: 10 }, end: Position { line: 1, col: 14 } } }) ); assert_eq!( ron::from_str::(r#""\xf0\x9f\xa6""#), Err(SpannedError { code: Error::InvalidEscape("Not a valid byte-escaped Unicode character"), span: Span { start: Position { line: 1, col: 10 }, end: Position { line: 1, col: 14 } } }) ); assert_eq!( ron::from_str::(r#""\xff\xff\xff\xff""#), Err(SpannedError { code: Error::InvalidEscape("Not a valid byte-escaped Unicode character"), span: Span { start: Position { line: 1, col: 14 }, end: Position { line: 1, col: 18 } } }) ); assert_eq!(ron::from_str::(r"'\u{1F980}'"), Ok('\u{1F980}')); assert_eq!( ron::from_str::(r"'\xf0\x9f\xa6\x80'"), Err(SpannedError { code: Error::InvalidEscape("Not a valid byte-escaped Unicode character"), span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 6 } } }) ); } #[test] fn byte_literal() { assert_eq!( ron::from_str("b'\0'"), Ok(ron::Value::Number(ron::value::Number::U8(0))) ); assert_eq!( ron::from_str("b'\\0'"), Ok(ron::Value::Number(ron::value::Number::U8(0))) ); for b in 0..=255_u8 { let default = std::ascii::escape_default(b) .map(char::from) .collect::(); let lower = format!(r"\x{:02x}", b); let upper = format!(r"\x{:02X}", b); assert_eq!( ron::from_str(&format!("b'{}'", default)), Ok(ron::Value::Number(ron::value::Number::U8(b))) ); assert_eq!( ron::from_str(&format!("b'{}'", lower)), Ok(ron::Value::Number(ron::value::Number::U8(b))) ); assert_eq!( ron::from_str(&format!("b'{}'", upper)), Ok(ron::Value::Number(ron::value::Number::U8(b))) ); } assert_eq!( ron::from_str::(r#"b'\u{0}'"#), Err(SpannedError { code: Error::InvalidEscape("Unexpected Unicode escape in byte literal"), span: Span { start: Position { line: 1, col: 7 }, end: Position { line: 1, col: 8 }, } }) ); let err = ron::from_str::(r#"b'🦀'"#).unwrap_err(); assert_eq!( err, SpannedError { code: Error::ExpectedByteLiteral, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 4 }, } } ); assert_eq!(format!("{}", err.code), "Expected byte literal"); assert_eq!( ron::from_str::(r#"b'qq'"#).unwrap_err(), SpannedError { code: Error::ExpectedByteLiteral, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 4 }, } } ); assert_eq!( ron::from_str::(r#"b'9'"#), Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("an 8-bit signed integer"), found: String::from(r#"b'9'"#) }, span: Span { start: Position { line: 1, col: 4 }, end: Position { line: 1, col: 5 }, } }) ); } #[test] fn invalid_identifier() { #[allow(dead_code)] #[derive(Debug, Deserialize)] // GRCOV_EXCL_LINE struct Test { a: i32, } for id in ["b\"", "b'", "br#", "br\"", "r\"", "r#\"", "r##"] { assert_eq!( ron::from_str::(&format!("({}: 42)", id)).unwrap_err(), SpannedError { code: Error::ExpectedIdentifier, span: Span { start: Position { line: 1, col: 2 }, end: Position { line: 1, col: 2 }, } } ); } } ron-0.12.0/tests/447_compact_maps_structs.rs000064400000000000000000000017311046102023000170470ustar 00000000000000use std::collections::BTreeMap; use ron::ser::{to_string, to_string_pretty, PrettyConfig}; #[derive(serde_derive::Serialize)] struct Struct { a: u8, b: u8, } #[test] fn compact_structs() { let s = Struct { a: 4, b: 2 }; assert_eq!(to_string(&s).unwrap(), "(a:4,b:2)"); assert_eq!( to_string_pretty(&s, PrettyConfig::default()).unwrap(), "(\n a: 4,\n b: 2,\n)" ); assert_eq!( to_string_pretty(&s, PrettyConfig::default().compact_structs(true)).unwrap(), "(a: 4, b: 2)" ); } #[test] fn compact_maps() { let m: BTreeMap<&str, i32> = BTreeMap::from_iter([("a", 4), ("b", 2)]); assert_eq!(to_string(&m).unwrap(), "{\"a\":4,\"b\":2}"); assert_eq!( to_string_pretty(&m, PrettyConfig::default()).unwrap(), "{\n \"a\": 4,\n \"b\": 2,\n}" ); assert_eq!( to_string_pretty(&m, PrettyConfig::default().compact_maps(true)).unwrap(), "{\"a\": 4, \"b\": 2}" ); } ron-0.12.0/tests/449_tagged_enum.rs000064400000000000000000000136241046102023000150770ustar 00000000000000use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] enum InnerEnum { Unit, Newtype(bool), Tuple(bool, i32), Struct { field: char }, } #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(deny_unknown_fields)] struct Container { field: InnerEnum, } #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] enum OuterEnum { Variant(Container), Sum { field: InnerEnum, value: i32 }, } #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(tag = "tag")] enum OuterEnumInternal { Variant(Container), Sum { field: InnerEnum, value: i32 }, } #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(tag = "tag", content = "c")] enum OuterEnumAdjacent { Variant(Container), Sum { field: InnerEnum, value: i32 }, } #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(untagged)] enum OuterEnumUntagged { Variant(Container), Sum { field: InnerEnum, value: i32 }, } #[test] fn test_enum_in_enum_roundtrip() { let outer = OuterEnum::Variant(Container { field: InnerEnum::Unit, }); let ron = ron::to_string(&outer).unwrap(); assert_eq!(ron, "Variant((field:Unit))"); let de = ron::from_str::(&ron); assert_eq!(de, Ok(outer)); let outer = OuterEnum::Sum { field: InnerEnum::Newtype(true), value: 42, }; let ron = ron::to_string(&outer).unwrap(); assert_eq!(ron, "Sum(field:Newtype(true),value:42)"); let de = ron::from_str::(&ron); assert_eq!(de, Ok(outer)); let outer = OuterEnum::Sum { field: InnerEnum::Tuple(true, 24), value: 42, }; let ron = ron::to_string(&outer).unwrap(); assert_eq!(ron, "Sum(field:Tuple(true,24),value:42)"); let de = ron::from_str::(&ron); assert_eq!(de, Ok(outer)); let outer = OuterEnum::Sum { field: InnerEnum::Struct { field: '🦀' }, value: 42, }; let ron = ron::to_string(&outer).unwrap(); assert_eq!(ron, "Sum(field:Struct(field:'🦀'),value:42)"); let de = ron::from_str::(&ron); assert_eq!(de, Ok(outer)); } #[test] fn test_enum_in_internally_tagged_roundtrip() { let outer = OuterEnumInternal::Variant(Container { field: InnerEnum::Unit, }); let ron = ron::to_string(&outer).unwrap(); assert_eq!(ron, "(tag:\"Variant\",field:Unit)"); let de = ron::from_str::(&ron); assert_eq!(de, Ok(outer)); let outer = OuterEnumInternal::Sum { field: InnerEnum::Newtype(true), value: 42, }; let ron = ron::to_string(&outer).unwrap(); assert_eq!(ron, "(tag:\"Sum\",field:Newtype(true),value:42)"); let de = ron::from_str::(&ron); assert_eq!(de, Ok(outer)); let outer = OuterEnumInternal::Sum { field: InnerEnum::Tuple(true, 24), value: 42, }; let ron = ron::to_string(&outer).unwrap(); assert_eq!(ron, "(tag:\"Sum\",field:Tuple(true,24),value:42)"); let de = ron::from_str::(&ron); assert_eq!(de, Ok(outer)); let outer = OuterEnumInternal::Sum { field: InnerEnum::Struct { field: '🦀' }, value: 42, }; let ron = ron::to_string(&outer).unwrap(); assert_eq!(ron, "(tag:\"Sum\",field:Struct(field:'🦀'),value:42)"); let de = ron::from_str::(&ron); assert_eq!(de, Ok(outer)); } #[test] fn test_enum_in_adjacently_tagged_roundtrip() { let outer = OuterEnumAdjacent::Variant(Container { field: InnerEnum::Unit, }); let ron = ron::to_string(&outer).unwrap(); assert_eq!(ron, "(tag:Variant,c:(field:Unit))"); let de = ron::from_str::(&ron); assert_eq!(de, Ok(outer)); let outer = OuterEnumAdjacent::Sum { field: InnerEnum::Newtype(true), value: 42, }; let ron = ron::to_string(&outer).unwrap(); assert_eq!(ron, "(tag:Sum,c:(field:Newtype(true),value:42))"); let de = ron::from_str::(&ron); assert_eq!(de, Ok(outer)); let outer = OuterEnumAdjacent::Sum { field: InnerEnum::Tuple(true, 24), value: 42, }; let ron = ron::to_string(&outer).unwrap(); assert_eq!(ron, "(tag:Sum,c:(field:Tuple(true,24),value:42))"); let de = ron::from_str::(&ron); assert_eq!(de, Ok(outer)); let outer = OuterEnumAdjacent::Sum { field: InnerEnum::Struct { field: '🦀' }, value: 42, }; let ron = ron::to_string(&outer).unwrap(); assert_eq!(ron, "(tag:Sum,c:(field:Struct(field:'🦀'),value:42))"); let de = ron::from_str::(&ron); assert_eq!(de, Ok(outer)); } #[test] fn test_enum_in_untagged_roundtrip() { let outer = OuterEnumUntagged::Variant(Container { field: InnerEnum::Unit, }); let ron = ron::to_string(&outer).unwrap(); assert_eq!(ron, "(field:Unit)"); let de = ron::from_str::(&ron); assert_eq!(de, Ok(outer)); let outer = OuterEnumUntagged::Sum { field: InnerEnum::Newtype(true), value: 42, }; let ron = ron::to_string(&outer).unwrap(); assert_eq!(ron, "(field:Newtype(true),value:42)"); let de = ron::from_str::(&ron); assert_eq!(de, Ok(outer)); let outer = OuterEnumUntagged::Sum { field: InnerEnum::Tuple(true, 24), value: 42, }; let ron = ron::to_string(&outer).unwrap(); assert_eq!(ron, "(field:Tuple(true,24),value:42)"); let de = ron::from_str::(&ron); assert_eq!(de, Ok(outer)); let outer = OuterEnumUntagged::Sum { field: InnerEnum::Struct { field: '🦀' }, value: 42, }; let ron = ron::to_string(&outer).unwrap(); assert_eq!(ron, "(field:Struct(field:'🦀'),value:42)"); let de = ron::from_str::(&ron); assert_eq!(de, Ok(outer)); } ron-0.12.0/tests/462_bytes.rs000064400000000000000000000017021046102023000137330ustar 00000000000000#[test] fn test_deserialise_byte_slice() { let val: &[u8] = &[0_u8, 1_u8, 2_u8, 3_u8]; let ron = ron::to_string(val).unwrap(); assert_eq!(ron, "[0,1,2,3]"); // deserialising a byte slice from a byte sequence should fail // with the error that a borrowed slice was expected but a byte // buffer was provided // NOT with an expected string error, since the byte slice // serialisation never serialises to a string assert_eq!( ron::from_str::<&[u8]>(&ron), Err(ron::error::SpannedError { code: ron::error::Error::InvalidValueForType { expected: String::from("a borrowed byte array"), found: String::from("the byte string b\"\\x00\\x01\\x02\\x03\""), }, span: ron::error::Span { start: ron::error::Position { line: 1, col: 9 }, end: ron::error::Position { line: 1, col: 10 }, } }) ); } ron-0.12.0/tests/465_implicit_some_stack.rs000064400000000000000000000014061046102023000166330ustar 00000000000000#[test] fn roundtrip_implicit_some_stack() { check_roundtrip(Option::<()>::None, "None"); check_roundtrip(Some(()), "()"); check_roundtrip(Some(Some(())), "()"); check_roundtrip(Some(Some(Some(()))), "()"); check_roundtrip(Some(Option::<()>::None), "Some(None)"); check_roundtrip(Some(Some(Option::<()>::None)), "Some(Some(None))"); } fn check_roundtrip< T: PartialEq + core::fmt::Debug + serde::Serialize + serde::de::DeserializeOwned, >( val: T, check: &str, ) { let options = ron::Options::default().with_default_extension(ron::extensions::Extensions::IMPLICIT_SOME); let ron = options.to_string(&val).unwrap(); assert_eq!(ron, check); let de: T = options.from_str(&ron).unwrap(); assert_eq!(de, val); } ron-0.12.0/tests/465_no_comment_char_value.rs000064400000000000000000000007041046102023000171400ustar 00000000000000#[test] fn value_deserialises_r_name() { // deserialising "A('/')" into ron::Value previously failed as the struct type // searcher reads into the char and then finds a weird comment starter there assert_eq!( ron::from_str("A('/')"), Ok(ron::Value::Seq(vec![ron::Value::Char('/')])) ); assert_eq!( ron::from_str("A(\"/\")"), Ok(ron::Value::Seq(vec![ron::Value::String(String::from("/"))])) ); } ron-0.12.0/tests/465_r_name_value.rs000064400000000000000000000011531046102023000152450ustar 00000000000000#[test] fn value_deserialises_r_name() { assert_eq!(ron::from_str("r"), Ok(ron::Value::Unit)); assert_eq!(ron::from_str("r()"), Ok(ron::Value::Seq(vec![]))); assert_eq!( ron::from_str("r(42)"), Ok(ron::Value::Seq(vec![ron::Value::Number( ron::value::Number::U8(42) )])) ); assert_eq!( ron::from_str("r(a:42)"), Ok(ron::Value::Map( [( ron::Value::String(String::from("a")), ron::Value::Number(ron::value::Number::U8(42)) )] .into_iter() .collect() )) ); } ron-0.12.0/tests/465_ser_backslash_string.rs000064400000000000000000000016111046102023000170010ustar 00000000000000#[test] fn serialize_backslash_string() { check_roundtrip('\\', r"'\\'", r"'\\'"); check_roundtrip(String::from("\\"), r#""\\""#, "r#\"\\\"#"); } fn check_roundtrip< T: PartialEq + core::fmt::Debug + serde::Serialize + serde::de::DeserializeOwned, >( val: T, cmp: &str, cmp_raw: &str, ) { let ron = ron::to_string(&val).unwrap(); assert_eq!(ron, cmp); let ron_escaped = ron::ser::to_string_pretty(&val, ron::ser::PrettyConfig::default().escape_strings(true)) .unwrap(); assert_eq!(ron_escaped, cmp); let ron_raw = ron::ser::to_string_pretty( &val, ron::ser::PrettyConfig::default().escape_strings(false), ) .unwrap(); assert_eq!(ron_raw, cmp_raw); let de = ron::from_str::(&ron).unwrap(); assert_eq!(de, val); let de_raw = ron::from_str::(&ron_raw).unwrap(); assert_eq!(de_raw, val); } ron-0.12.0/tests/465_unwrap_some_newtype_variant_value.rs000064400000000000000000000031631046102023000216450ustar 00000000000000#[test] fn deserialise_value_with_unwrap_some_newtype_variant() { assert_eq!( ron::from_str::("Some(a: 42)"), Err(ron::error::SpannedError { code: ron::Error::ExpectedOptionEnd, span: ron::error::Span { start: ron::error::Position { line: 1, col: 7 }, end: ron::error::Position { line: 1, col: 7 }, } }), ); assert_eq!( ron::from_str("#![enable(unwrap_variant_newtypes)] Some(a: 42)"), Ok(ron::Value::Option(Some(Box::new(ron::Value::Map( [( ron::Value::String(String::from("a")), ron::Value::Number(ron::value::Number::U8(42)) )] .into_iter() .collect() ))))), ); assert_eq!( ron::from_str("#![enable(unwrap_variant_newtypes)] Some(42, true)"), Ok(ron::Value::Option(Some(Box::new(ron::Value::Seq(vec![ ron::Value::Number(ron::value::Number::U8(42)), ron::Value::Bool(true) ]))))), ); assert_eq!( ron::from_str("#![enable(unwrap_variant_newtypes)] Some(42,)"), Ok(ron::Value::Option(Some(Box::new(ron::Value::Number( ron::value::Number::U8(42) ))))), ); assert_eq!( ron::from_str("#![enable(unwrap_variant_newtypes)] Some()"), Ok(ron::Value::Option(Some(Box::new(ron::Value::Unit)))), ); assert_eq!( ron::from_str("#![enable(unwrap_variant_newtypes)] Some(42)"), Ok(ron::Value::Option(Some(Box::new(ron::Value::Number( ron::value::Number::U8(42) ))))), ); } ron-0.12.0/tests/465_validate_ser_identifiers.rs000064400000000000000000000051161046102023000176420ustar 00000000000000use serde::ser::{SerializeStruct, SerializeStructVariant, Serializer}; #[test] fn invalid_struct_name() { let mut ser = ron::Serializer::new(String::new(), None).unwrap(); assert_eq!( ser.serialize_newtype_struct("", &true).err(), Some(ron::Error::InvalidIdentifier(String::from(""))), ); assert_eq!( ser.serialize_tuple_struct("", 0).err(), Some(ron::Error::InvalidIdentifier(String::from(""))), ); assert_eq!( ser.serialize_struct("", 0).err(), Some(ron::Error::InvalidIdentifier(String::from(""))), ); } #[test] fn invalid_enum_variant_name() { let mut ser = ron::Serializer::new(String::new(), None).unwrap(); assert_eq!( ser.serialize_unit_variant("", 0, "A").err(), Some(ron::Error::InvalidIdentifier(String::from(""))), ); assert_eq!( ser.serialize_unit_variant("A", 0, "").err(), Some(ron::Error::InvalidIdentifier(String::from(""))), ); assert_eq!( ser.serialize_newtype_variant("", 0, "A", &true).err(), Some(ron::Error::InvalidIdentifier(String::from(""))), ); assert_eq!( ser.serialize_newtype_variant("A", 0, "", &true).err(), Some(ron::Error::InvalidIdentifier(String::from(""))), ); assert_eq!( ser.serialize_tuple_variant("", 0, "A", 0).err(), Some(ron::Error::InvalidIdentifier(String::from(""))), ); assert_eq!( ser.serialize_tuple_variant("A", 0, "", 0).err(), Some(ron::Error::InvalidIdentifier(String::from(""))), ); assert_eq!( ser.serialize_struct_variant("", 0, "A", 0).err(), Some(ron::Error::InvalidIdentifier(String::from(""))), ); assert_eq!( ser.serialize_struct_variant("A", 0, "", 0).err(), Some(ron::Error::InvalidIdentifier(String::from(""))), ); } #[test] fn invalid_struct_field_name() { let mut ser = ron::Serializer::new(String::new(), None).unwrap(); let mut r#struct = ser.serialize_struct("A", 2).unwrap(); SerializeStruct::serialize_field(&mut r#struct, "A", &true).unwrap(); assert_eq!( SerializeStruct::serialize_field(&mut r#struct, "", &true).err(), Some(ron::Error::InvalidIdentifier(String::from(""))), ); std::mem::drop(r#struct); let mut r#struct = ser.serialize_struct_variant("A", 0, "A", 2).unwrap(); SerializeStructVariant::serialize_field(&mut r#struct, "A", &true).unwrap(); assert_eq!( SerializeStructVariant::serialize_field(&mut r#struct, "", &true).err(), Some(ron::Error::InvalidIdentifier(String::from(""))), ); } ron-0.12.0/tests/481_number_underscores_suffixes.rs000064400000000000000000000634701046102023000204400ustar 00000000000000use ron::Number; #[test] #[allow(clippy::unusual_byte_groupings)] #[allow(clippy::inconsistent_digit_grouping)] #[allow(clippy::zero_prefixed_literal)] fn de_integer_underscores() { assert_eq!(ron::from_str("0b10_10___101_"), Ok(0b10_10___101__u8)); assert_eq!( ron::from_str::("_0b1"), Err(ron::error::SpannedError { code: ron::Error::UnderscoreAtBeginning, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 1 }, } }) ); assert_eq!( ron::from_str::("_0b1_u8"), Err(ron::error::SpannedError { code: ron::Error::UnderscoreAtBeginning, // ? Start before end ? span: ron::error::Span { start: ron::error::Position { line: 1, col: 6 }, end: ron::error::Position { line: 1, col: 1 }, } }) ); assert_eq!( ron::from_str::("0b2"), Err(ron::error::SpannedError { code: ron::Error::InvalidIntegerDigit { digit: '2', base: 2 }, span: ron::error::Span { start: ron::error::Position { line: 1, col: 3 }, end: ron::error::Position { line: 1, col: 3 }, } }) ); assert_eq!( ron::from_str::("-0b2_i32"), Err(ron::error::SpannedError { code: ron::Error::InvalidIntegerDigit { digit: '2', base: 2 }, span: ron::error::Span { start: ron::error::Position { line: 1, col: 4 }, end: ron::error::Position { line: 1, col: 4 }, } }) ); assert_eq!(ron::from_str("0o71_32___145_"), Ok(0o71_32___145_)); assert_eq!( ron::from_str::("_0o5"), Err(ron::error::SpannedError { code: ron::Error::UnderscoreAtBeginning, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 1 }, } }) ); assert_eq!( ron::from_str::("0oA"), Err(ron::error::SpannedError { code: ron::Error::InvalidIntegerDigit { digit: 'A', base: 8 }, span: ron::error::Span { start: ron::error::Position { line: 1, col: 3 }, end: ron::error::Position { line: 1, col: 3 }, } }) ); assert_eq!(ron::from_str("0xa1_fe___372_"), Ok(0xa1_fe___372_)); assert_eq!( ron::from_str::("_0xF"), Err(ron::error::SpannedError { code: ron::Error::UnderscoreAtBeginning, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 1 }, } }) ); assert_eq!( ron::from_str::("0xZ"), Err(ron::error::SpannedError { code: ron::Error::ExpectedInteger, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 3 }, } }) ); assert_eq!(ron::from_str("0_6_163_810___17"), Ok(0_6_163_810___17)); assert_eq!( ron::from_str::("_123"), Err(ron::error::SpannedError { code: ron::Error::UnderscoreAtBeginning, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 1 }, } }) ); assert_eq!( ron::from_str::("12a"), Err(ron::error::SpannedError { code: ron::Error::InvalidIntegerDigit { digit: 'a', base: 10 }, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 3 }, } }) ); } #[test] #[allow(clippy::inconsistent_digit_grouping)] fn de_float_underscores() { assert_eq!(ron::from_str("2_18__6_"), Ok(2_18__6__f32)); assert_eq!( ron::from_str::("_286"), Err(ron::error::SpannedError { code: ron::Error::UnderscoreAtBeginning, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 1 }, } }) ); assert_eq!( ron::from_str::("2a86"), Err(ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 2 }, end: ron::error::Position { line: 1, col: 2 }, } }) ); assert_eq!(ron::from_str("2_18__6_."), Ok(2_18__6__f32)); assert_eq!( ron::from_str::("2_18__6_._"), Err(ron::error::SpannedError { code: ron::Error::FloatUnderscore, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 10 }, } }) ); assert_eq!( ron::from_str::("2_18__6_.3__7_"), Ok(2_18__6_.3__7__f32) ); assert_eq!(ron::from_str::(".3__7_"), Ok(0.3__7__f32)); assert_eq!( ron::from_str::("._3__7_"), Err(ron::error::SpannedError { code: ron::Error::FloatUnderscore, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 2 }, } }) ); assert_eq!( ron::from_str::("2_18__6_.3__7_e____7_3__"), Ok(2_18__6_.3__7_e____7_3___f64) ); assert_eq!( ron::from_str::("2_18__6_.3__7_e+____"), Err(ron::error::SpannedError { code: ron::Error::ExpectedFloat, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 1 }, } }) ); } #[test] fn value_number_suffix_roundtrip() { assert_eq!( ron::from_str::("1_f32").unwrap(), ron::Value::Number(ron::value::Number::new(1_f32)) ); assert_eq!( ron::from_str::("-1_f32").unwrap(), ron::Value::Number(ron::value::Number::new(-1_f32)) ); check_number_roundtrip(f32::NAN, "f32", f64::NAN); check_number_roundtrip(-f32::NAN, "f32", -f64::NAN); check_number_roundtrip(f32::INFINITY, "f32", f64::INFINITY); check_number_roundtrip(f32::NEG_INFINITY, "f32", f64::NEG_INFINITY); check_number_roundtrip(f64::NAN, "f64", f64::NAN); check_number_roundtrip(-f64::NAN, "f64", -f64::NAN); check_number_roundtrip(f64::INFINITY, "f64", f64::INFINITY); check_number_roundtrip(f64::NEG_INFINITY, "f64", f64::NEG_INFINITY); macro_rules! test_min_max { ($($ty:ty),*) => { $( check_number_roundtrip(<$ty>::MIN, stringify!($ty), <$ty>::MIN as f64); check_number_roundtrip(<$ty>::MAX, stringify!($ty), <$ty>::MAX as f64); )* }; } test_min_max! { i8, i16, i32, i64, u8, u16, u32, u64, f32, f64 } #[cfg(feature = "integer128")] test_min_max! { i128, u128 } } fn check_number_roundtrip< T: Copy + Into + serde::Serialize + serde::de::DeserializeOwned + PartialEq + core::fmt::Debug, >( n: T, suffix: &str, n_f64: f64, ) { let number: Number = n.into(); let ron = ron::ser::to_string_pretty( &number, ron::ser::PrettyConfig::default().number_suffixes(true), ) .unwrap(); assert!(ron.ends_with(suffix)); let ron = ron::ser::to_string_pretty(&n, ron::ser::PrettyConfig::default().number_suffixes(true)) .unwrap(); assert!(ron.ends_with(suffix)); let de: ron::Value = ron::from_str(&ron).unwrap(); assert_eq!(de, ron::Value::Number(number)); let de: T = ron::from_str(&ron).unwrap(); let de_number: Number = de.into(); assert_eq!(de_number, number); assert_eq!(Number::from(de_number.into_f64()), Number::from(n_f64)); } #[test] fn negative_unsigned() { assert_eq!( ron::from_str::("-1u8"), Err(ron::error::SpannedError { code: ron::Error::IntegerOutOfBounds, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 5 }, } }) ); assert_eq!( ron::from_str::("-1u16"), Err(ron::error::SpannedError { code: ron::Error::IntegerOutOfBounds, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 6 }, } }) ); assert_eq!( ron::from_str::("-1u32"), Err(ron::error::SpannedError { code: ron::Error::IntegerOutOfBounds, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 6 }, } }) ); assert_eq!( ron::from_str::("-1u64"), Err(ron::error::SpannedError { code: ron::Error::IntegerOutOfBounds, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 6 }, } }) ); #[cfg(feature = "integer128")] assert_eq!( ron::from_str::("-1u128"), Err(ron::error::SpannedError { code: ron::Error::IntegerOutOfBounds, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 7 }, } }) ); assert_eq!( ron::from_str::("-1u8"), Err(ron::error::SpannedError { code: ron::Error::IntegerOutOfBounds, span: ron::error::Span { start: ron::error::Position { line: 1, col: 3 }, end: ron::error::Position { line: 1, col: 5 }, } }) ); assert_eq!( ron::from_str::("-1u16"), Err(ron::error::SpannedError { code: ron::Error::IntegerOutOfBounds, span: ron::error::Span { start: ron::error::Position { line: 1, col: 3 }, end: ron::error::Position { line: 1, col: 6 }, } }) ); assert_eq!( ron::from_str::("-1u32"), Err(ron::error::SpannedError { code: ron::Error::IntegerOutOfBounds, span: ron::error::Span { start: ron::error::Position { line: 1, col: 3 }, end: ron::error::Position { line: 1, col: 6 }, } }) ); assert_eq!( ron::from_str::("-1u64"), Err(ron::error::SpannedError { code: ron::Error::IntegerOutOfBounds, span: ron::error::Span { start: ron::error::Position { line: 1, col: 3 }, end: ron::error::Position { line: 1, col: 6 }, } }) ); #[cfg(feature = "integer128")] assert_eq!( ron::from_str::("-1u128"), Err(ron::error::SpannedError { code: ron::Error::IntegerOutOfBounds, span: ron::error::Span { start: ron::error::Position { line: 1, col: 3 }, end: ron::error::Position { line: 1, col: 7 }, } }) ); } #[test] fn invalid_suffix() { assert_eq!( ron::from_str::("1u7"), Err(ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 2 }, end: ron::error::Position { line: 1, col: 2 }, } }) ); assert_eq!( ron::from_str::("1f17"), Err(ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 2 }, end: ron::error::Position { line: 1, col: 2 }, } }) ); #[cfg(not(feature = "integer128"))] assert_eq!( ron::from_str::("1u128"), Err(ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 2 }, end: ron::error::Position { line: 1, col: 2 }, } }) ); #[cfg(not(feature = "integer128"))] assert_eq!( ron::from_str::("1i128"), Err(ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 2 }, end: ron::error::Position { line: 1, col: 2 }, } }) ); assert_eq!( ron::from_str::("1u7"), Err(ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 2 }, end: ron::error::Position { line: 1, col: 2 }, } }) ); assert_eq!( ron::from_str::("1f17"), Err(ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 2 }, end: ron::error::Position { line: 1, col: 2 }, } }) ); #[cfg(not(feature = "integer128"))] assert_eq!( ron::from_str::("1u128"), Err(ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 2 }, end: ron::error::Position { line: 1, col: 2 }, } }) ); #[cfg(not(feature = "integer128"))] assert_eq!( ron::from_str::("1i128"), Err(ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 2 }, end: ron::error::Position { line: 1, col: 2 }, } }) ); } #[test] fn number_type_mismatch() { assert_eq!( ron::from_str::("1i32"), Err(ron::error::SpannedError { code: ron::Error::InvalidValueForType { expected: String::from("an 8-bit unsigned integer"), found: String::from("1i32") }, span: ron::error::Span { start: ron::error::Position { line: 1, col: 2 }, end: ron::error::Position { line: 1, col: 5 }, } }) ); assert_eq!( ron::from_str::("-1u8"), Err(ron::error::SpannedError { code: ron::Error::IntegerOutOfBounds, span: ron::error::Span { start: ron::error::Position { line: 1, col: 3 }, end: ron::error::Position { line: 1, col: 5 }, } }) ); assert_eq!( ron::from_str::("1f64"), Err(ron::error::SpannedError { code: ron::Error::InvalidValueForType { expected: String::from("a 32-bit floating point number"), found: String::from("1f64") }, span: ron::error::Span { start: ron::error::Position { line: 1, col: 2 }, end: ron::error::Position { line: 1, col: 5 }, } }) ); assert_eq!( ron::from_str::("1f32"), Err(ron::error::SpannedError { code: ron::Error::InvalidValueForType { expected: String::from("a 64-bit floating point number"), found: String::from("1f32") }, span: ron::error::Span { start: ron::error::Position { line: 1, col: 2 }, end: ron::error::Position { line: 1, col: 5 }, } }) ); macro_rules! test_mismatch { ($($ty:ty),*) => { $( check_number_type_mismatch::<$ty>("i8"); check_number_type_mismatch::<$ty>("i16"); check_number_type_mismatch::<$ty>("i32"); check_number_type_mismatch::<$ty>("i64"); #[cfg(feature = "integer128")] check_number_type_mismatch::<$ty>("i128"); check_number_type_mismatch::<$ty>("u8"); check_number_type_mismatch::<$ty>("u16"); check_number_type_mismatch::<$ty>("u32"); check_number_type_mismatch::<$ty>("u64"); #[cfg(feature = "integer128")] check_number_type_mismatch::<$ty>("u128"); )* }; } test_mismatch! { i8, i16, i32, i64, u8, u16, u32, u64 } #[cfg(feature = "integer128")] test_mismatch! { i128, u128 } } fn check_number_type_mismatch(suffix: &str) { let ron = format!("0{suffix}"); if suffix.starts_with(std::any::type_name::()) { assert!(ron::from_str::(&ron).is_ok()); return; } let err = ron::from_str::(&ron).unwrap_err(); println!("{:?} {}", err, suffix); assert_eq!( err.span.end, ron::error::Position { line: 1, col: 2 + suffix.len() } ); if !matches!(&err.code, ron::Error::InvalidValueForType { found, .. } if found == &ron ) { panic!("{:?}", err.code); // GRCOV_EXCL_LINE } } #[test] fn float_const_prefix() { assert_eq!( ron::from_str::("NaNf32a").unwrap_err(), ron::error::SpannedError { code: ron::Error::ExpectedFloat, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 1 }, } } ); assert_eq!( ron::from_str::("-inff64a").unwrap_err(), ron::error::SpannedError { code: ron::Error::ExpectedFloat, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 1 }, } } ); assert_eq!( ron::from_str::("+NaNf17").unwrap_err(), ron::error::SpannedError { code: ron::Error::ExpectedFloat, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 1 }, } } ); } #[test] fn invalid_float() { assert_eq!( ron::from_str::("1ee3").unwrap_err(), ron::error::SpannedError { code: ron::Error::ExpectedFloat, span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 1 }, } } ); assert_eq!( ron::from_str::("1ee3f32").unwrap_err(), ron::error::SpannedError { code: ron::Error::ExpectedFloat, // ? Start before end ? span: ron::error::Span { start: ron::error::Position { line: 1, col: 5 }, end: ron::error::Position { line: 1, col: 1 }, } } ); assert_eq!( ron::from_str::("1ee3f64").unwrap_err(), ron::error::SpannedError { code: ron::Error::ExpectedFloat, // ? Start before end ? span: ron::error::Span { start: ron::error::Position { line: 1, col: 5 }, end: ron::error::Position { line: 1, col: 1 }, } } ); } #[test] fn fuzzer_found_issues() { #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)] enum A { #[serde(rename = "true")] True(bool), #[serde(rename = "false")] False(bool), #[serde(rename = "Some")] Some(bool), #[serde(rename = "None")] None(bool), #[serde(rename = "inf")] Inf(bool), #[serde(rename = "inff32")] InfF32(bool), #[serde(rename = "inff64")] InfF64(bool), #[serde(rename = "NaN")] NaN(bool), #[serde(rename = "NaNf32")] NaNF32(bool), #[serde(rename = "NaNf64")] NaNF64(bool), } assert_eq!(ron::to_string(&A::True(false)).unwrap(), "r#true(false)"); assert_eq!(ron::from_str::("r#true(false)").unwrap(), A::True(false)); assert_eq!( ron::from_str::("true(false)").unwrap_err(), ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 5 }, end: ron::error::Position { line: 1, col: 5 }, } } ); assert_eq!(ron::to_string(&A::False(true)).unwrap(), "r#false(true)"); assert_eq!(ron::from_str::("r#false(true)").unwrap(), A::False(true)); assert_eq!( ron::from_str::("false(true)").unwrap_err(), ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 6 }, end: ron::error::Position { line: 1, col: 6 }, } } ); assert_eq!(ron::to_string(&A::Some(false)).unwrap(), "r#Some(false)"); assert_eq!(ron::from_str::("r#Some(false)").unwrap(), A::Some(false)); assert_eq!( ron::from_str::("Some(false)").unwrap(), ron::Value::Option(Some(Box::new(ron::Value::Bool(false)))), ); assert_eq!(ron::to_string(&A::None(true)).unwrap(), "r#None(true)"); assert_eq!(ron::from_str::("r#None(true)").unwrap(), A::None(true)); assert_eq!( ron::from_str::("None(true)").unwrap_err(), ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 5 }, end: ron::error::Position { line: 1, col: 5 }, } } ); assert_eq!(ron::to_string(&A::Inf(false)).unwrap(), "r#inf(false)"); assert_eq!(ron::from_str::("r#inf(false)").unwrap(), A::Inf(false)); assert_eq!( ron::from_str::("inf(false)").unwrap_err(), ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 4 }, end: ron::error::Position { line: 1, col: 4 }, } } ); assert_eq!( ron::to_string(&A::InfF32(false)).unwrap(), "r#inff32(false)" ); assert_eq!( ron::from_str::("r#inff32(false)").unwrap(), A::InfF32(false) ); assert_eq!( ron::from_str::("inff32(false)").unwrap_err(), ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 7 }, end: ron::error::Position { line: 1, col: 7 }, } } ); assert_eq!( ron::to_string(&A::InfF64(false)).unwrap(), "r#inff64(false)" ); assert_eq!( ron::from_str::("r#inff64(false)").unwrap(), A::InfF64(false) ); assert_eq!( ron::from_str::("inff64(false)").unwrap_err(), ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 7 }, end: ron::error::Position { line: 1, col: 7 }, } } ); assert_eq!(ron::to_string(&A::NaN(true)).unwrap(), "r#NaN(true)"); assert_eq!(ron::from_str::("r#NaN(true)").unwrap(), A::NaN(true)); assert_eq!( ron::from_str::("NaN(true)").unwrap_err(), ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 4 }, end: ron::error::Position { line: 1, col: 4 }, } } ); assert_eq!(ron::to_string(&A::NaNF32(true)).unwrap(), "r#NaNf32(true)"); assert_eq!( ron::from_str::("r#NaNf32(true)").unwrap(), A::NaNF32(true) ); assert_eq!( ron::from_str::("NaNf32(true)").unwrap_err(), ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 7 }, end: ron::error::Position { line: 1, col: 7 }, } } ); assert_eq!(ron::to_string(&A::NaNF64(true)).unwrap(), "r#NaNf64(true)"); assert_eq!( ron::from_str::("r#NaNf64(true)").unwrap(), A::NaNF64(true) ); assert_eq!( ron::from_str::("NaNf64(true)").unwrap_err(), ron::error::SpannedError { code: ron::Error::TrailingCharacters, span: ron::error::Span { start: ron::error::Position { line: 1, col: 7 }, end: ron::error::Position { line: 1, col: 7 }, } } ); } ron-0.12.0/tests/492_enum_in_untagged_enum.rs000064400000000000000000000013571046102023000171520ustar 00000000000000use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Serialize, Deserialize)] enum Other { Env(String), } #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(untagged)] enum MaybeEnv { Value(String), Other(Other), } #[test] fn enum_in_untagged_enum() { check_roundtrip(&MaybeEnv::Value(String::from("foo")), "\"foo\""); check_roundtrip( &MaybeEnv::Other(Other::Env(String::from("bar"))), "Env(\"bar\")", ); } fn check_roundtrip( val: &T, check: &str, ) { let ron = ron::to_string(&val).unwrap(); assert_eq!(ron, check); let de = ron::from_str::(&ron).unwrap(); assert_eq!(&de, val); } ron-0.12.0/tests/502_known_bugs.rs000064400000000000000000002516241046102023000147660ustar 00000000000000use std::collections::{BTreeMap, HashMap}; use ron::{ error::Position, error::Span, error::SpannedError, extensions::Extensions, ser::PrettyConfig, Error, }; use serde::{Deserialize, Serialize}; #[test] fn struct_names_inside_internally_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: 42 } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: 42 } }, PrettyConfig::default().struct_names(true) ), Err(Err(SpannedError { code: Error::MissingStructField { field: "hi", outer: None }, span: Span { start: Position { line: 7, col: 1 }, end: Position { line: 7, col: 2 } } })), ); } #[test] fn struct_names_inside_adjacently_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum AdjacentlyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: 42 } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: 42 } }, PrettyConfig::default().struct_names(true) ), Ok(()), ); assert_eq!( ron::from_str::( "AdjacentlyTagged(tag: B, content: B(ho: 24, a: A(hi: 42)))" ), Ok(AdjacentlyTagged::B { ho: 24, a: A { hi: 42 } }), ); assert_eq!( ron::from_str::( "AdjacentlyTagged(content: B(ho: 24, a: A(hi: 42)), tag: B)" ), Err(SpannedError { code: Error::MissingStructField { field: "ho", outer: Some(String::from("AdjacentlyTagged")) }, span: Span { start: Position { line: 1, col: 58 }, end: Position { line: 1, col: 58 } } }), ); } #[test] fn struct_names_inside_untagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: 42 } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: 42 } }, PrettyConfig::default().struct_names(true) ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), span: Span { start: Position { line: 6, col: 1 }, end: Position { line: 6, col: 2 } } })), ); } #[test] fn struct_names_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: B, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: 42 } } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: 42 } } }, PrettyConfig::default().struct_names(true) ), Err(Err(SpannedError { code: Error::MissingStructField { field: "hi", outer: None }, span: Span { start: Position { line: 6, col: 1 }, end: Position { line: 6, col: 1 } } })), ); } #[test] fn struct_names_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: B, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: 42 } } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: 42 } } }, PrettyConfig::default().struct_names(true) ), Err(Err(SpannedError { code: Error::MissingStructField { field: "hi", outer: Some(String::from("C")) }, span: Span { start: Position { line: 6, col: 1 }, end: Position { line: 6, col: 1 } } })), ); } #[test] fn implicit_some_inside_internally_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Option>, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: Some(Some(())) } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: Some(Some(())) } }, PrettyConfig::default().extensions(Extensions::IMPLICIT_SOME) ), Err(Ok(Error::Message(String::from("ROUNDTRIP error: B { ho: 24, a: A { hi: Some(Some(())) } } != B { ho: 24, a: A { hi: None } }")))) ); } #[test] fn implicit_some_inside_adjacently_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Unit; #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Option>, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum AdjacentlyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: Some(Some(Unit)) } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: Some(Some(Unit)) } }, PrettyConfig::default().extensions(Extensions::IMPLICIT_SOME) ), Ok(()), ); assert_eq!( ron::from_str::( "#![enable(implicit_some)] (tag: B, content: (ho: 24, a: A(hi: ())))" ), Ok(AdjacentlyTagged::B { ho: 24, a: A { hi: Some(Some(Unit)) } }), ); assert_eq!( ron::from_str::( "#![enable(implicit_some)] (content: (ho: 24, a: A(hi: ())), tag: B)" ), Ok(AdjacentlyTagged::B { ho: 24, a: A { hi: None } // THIS IS WRONG }), ); } #[test] fn implicit_some_inside_untagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Option>, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: Some(Some(())) } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: Some(Some(())) } }, PrettyConfig::default().extensions(Extensions::IMPLICIT_SOME) ), Err(Ok(Error::Message(String::from( "ROUNDTRIP error: B { ho: 24, a: A { hi: Some(Some(())) } } != B { ho: 24, a: A { hi: None } }" )))), ); } #[test] fn implicit_some_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Unit, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Option>, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: B, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: Some(Some(Untagged::Unit)) } } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: Some(Some(Untagged::Unit)) } } }, PrettyConfig::default().extensions(Extensions::IMPLICIT_SOME) ), Err(Ok(Error::Message(String::from("ROUNDTRIP error: FlattenedStruct { ho: 24, a: B { a: A { hi: Some(Some(Unit)) } } } != FlattenedStruct { ho: 24, a: B { a: A { hi: None } } }")))) ); } #[test] fn implicit_some_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Option>, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: B, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: Some(Some([])) } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("an empty array"), found: String::from("a unit value") }, span: Span { start: Position { line: 6, col: 1 }, end: Position { line: 6, col: 1 } } })) ); assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: Some(Some([])) } } }, PrettyConfig::default().extensions(Extensions::IMPLICIT_SOME) ), Err(Ok(Error::Message(String::from("ROUNDTRIP error: C { ho: 24, a: B { a: A { hi: Some(Some([])) } } } != C { ho: 24, a: B { a: A { hi: None } } }")))) ); } #[test] fn newtype_inside_internally_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A(i32); #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { B { ho: i32, a: A }, } // NOTE: // 1. ron is correctly collected into Content, newtype is a one-seq here // 2. newtype asks ContentDeserializer for newtype // 3. ContentDeserializer forwards any value to visit_newtype_struct // https://github.com/serde-rs/serde/blob/8c4aad3a59515f7b779f764d5e16d6bae297ab7f/serde/src/private/de.rs#L1347-L1359 assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A(42) }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a sequence") }, span: Span { start: Position { line: 5, col: 1 }, end: Position { line: 5, col: 2 } } })) ); } #[test] fn newtype_inside_adjacently_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A(i32); #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum AdjacentlyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A(42) }, PrettyConfig::default() ), Ok(()) ); assert_eq!( ron::from_str::("(tag: B, content: (ho: 24, a: (42)))"), Ok(AdjacentlyTagged::B { ho: 24, a: A(42) }), ); assert_eq!( ron::from_str::("(content: (ho: 24, a: (42)), tag: B)"), Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a sequence") }, span: Span { start: Position { line: 1, col: 36 }, end: Position { line: 1, col: 36 } } }) ); } #[test] fn newtype_inside_untagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A(i32); #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip(&Untagged::B { ho: 24, a: A(42) }, PrettyConfig::default()), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), span: Span { start: Position { line: 4, col: 1 }, end: Position { line: 4, col: 2 } } })) ); } #[test] fn newtype_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A(i32); #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: B, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A(42) } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a sequence") }, span: Span { start: Position { line: 4, col: 1 }, end: Position { line: 4, col: 1 } } })) ); } #[test] fn newtype_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A(i32); #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: B, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A(42) } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a sequence") }, span: Span { start: Position { line: 4, col: 1 }, end: Position { line: 4, col: 1 } } })) ); } #[test] fn one_tuple_inside_unwrapped_newtype_variant_inside_internally_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum A { Newtype((i32,)), } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A::Newtype((42,)) }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A::Newtype((42,)) }, PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("a tuple of size 1"), found: String::from("the unsigned integer `42`") }, span: Span { start: Position { line: 6, col: 1 }, end: Position { line: 6, col: 2 } } })) ); } #[test] fn one_tuple_inside_unwrapped_newtype_variant_inside_adjacently_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum A { Newtype([i32; 1]), } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum AdjacentlyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A::Newtype([42]) }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A::Newtype([42]) }, PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) ), Ok(()) ); assert_eq!( ron::from_str::( "#![enable(unwrap_variant_newtypes)] (tag: B, content: (ho: 24, a: Newtype(42)))" ), Ok(AdjacentlyTagged::B { ho: 24, a: A::Newtype([42]) }), ); assert_eq!( ron::from_str::( "#![enable(unwrap_variant_newtypes)] (content: (ho: 24, a: Newtype(42)), tag: B)" ), Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("an array of length 1"), found: String::from("the unsigned integer `42`") }, span: Span { start: Position { line: 1, col: 79 }, end: Position { line: 1, col: 79 } } }) ); } #[test] fn one_tuple_inside_unwrapped_newtype_variant_inside_untagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct OneTuple(i32, #[serde(skip)] ()); #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { B { ho: i32, a: Option }, } assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: Some(OneTuple(42, ())) }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: Some(OneTuple(42, ())) }, PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), span: Span { start: Position { line: 5, col: 1 }, end: Position { line: 5, col: 2 } } })) ); } #[test] fn one_tuple_inside_unwrapped_newtype_variant_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum A { Newtype([i32; 1]), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: B, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A::Newtype([42]) } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A::Newtype([42]) } }, PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("an array of length 1"), found: String::from("the unsigned integer `42`") }, span: Span { start: Position { line: 5, col: 1 }, end: Position { line: 5, col: 1 } } })) ); } #[test] fn one_tuple_inside_unwrapped_newtype_variant_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum A { Newtype((i32,)), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: B, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A::Newtype((42,)) } }, PrettyConfig::default() ), Ok(()), ); assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A::Newtype((42,)) } }, PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("a tuple of size 1"), found: String::from("the unsigned integer `42`") }, span: Span { start: Position { line: 5, col: 1 }, end: Position { line: 5, col: 1 } } })) ); } #[test] fn one_tuple_variant_inside_internally_tagged() { // A tuple variant with just one element that is not a newtype variant #[derive(PartialEq, Debug, Serialize, Deserialize)] enum OneEnum { OneTuple(i32, #[serde(skip)] ()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: OneEnum, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: OneEnum::OneTuple(42, ()) } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("tuple variant"), found: String::from("the unsigned integer `42`") }, span: Span { start: Position { line: 7, col: 1 }, end: Position { line: 7, col: 2 } } })) ); } #[test] fn one_tuple_variant_inside_adjacently_tagged() { // A tuple variant with just one element that is not a newtype variant #[derive(PartialEq, Debug, Serialize, Deserialize)] enum OneEnum { OneTuple(i32, #[serde(skip)] ()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: OneEnum, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum AdjacentlyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: OneEnum::OneTuple(42, ()) } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( ron::from_str::("(tag: B, content: (ho: 24, a: (hi: OneTuple(42))))"), Ok(AdjacentlyTagged::B { ho: 24, a: A { hi: OneEnum::OneTuple(42, ()) } }), ); assert_eq!( ron::from_str::("(content: (ho: 24, a: (hi: OneTuple(42))), tag: B)"), Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("tuple variant"), found: String::from("the unsigned integer `42`") }, span: Span { start: Position { line: 1, col: 50 }, end: Position { line: 1, col: 50 } } }) ); } #[test] fn one_tuple_variant_inside_untagged() { // A tuple variant with just one element that is not a newtype variant #[derive(PartialEq, Debug, Serialize, Deserialize)] enum OneEnum { OneTuple(i32, #[serde(skip)] ()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: OneEnum, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: OneEnum::OneTuple(42, ()) } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), span: Span { start: Position { line: 6, col: 1 }, end: Position { line: 6, col: 2 } } })) ); } #[test] fn one_tuple_variant_inside_flatten_struct() { // A tuple variant with just one element that is not a newtype variant #[derive(PartialEq, Debug, Serialize, Deserialize)] enum OneEnum { OneTuple(i32, #[serde(skip)] ()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: OneEnum, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: B, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: OneEnum::OneTuple(42, ()) } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("tuple variant"), found: String::from("the unsigned integer `42`") }, span: Span { start: Position { line: 6, col: 1 }, end: Position { line: 6, col: 1 } } })) ); } #[test] fn one_tuple_variant_inside_flatten_struct_variant() { // A tuple variant with just one element that is not a newtype variant #[derive(PartialEq, Debug, Serialize, Deserialize)] enum OneEnum { OneTuple(i32, #[serde(skip)] ()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: OneEnum, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: B, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: OneEnum::OneTuple(42, ()) } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("tuple variant"), found: String::from("the unsigned integer `42`") }, span: Span { start: Position { line: 6, col: 1 }, end: Position { line: 6, col: 1 } } })) ); } #[test] fn raw_value_inside_internally_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Box, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: ron::value::RawValue::from_boxed_ron(String::from("42").into_boxed_str()) .unwrap(), } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any valid RON-value-string"), found: String::from("the unsigned integer `42`") }, span: Span { start: Position { line: 7, col: 1 }, end: Position { line: 7, col: 2 } } })) ); } #[test] fn raw_value_inside_adjacently_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Box, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum AdjacentlyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: ron::value::RawValue::from_boxed_ron(String::from("42").into_boxed_str()) .unwrap(), } }, PrettyConfig::default() ), // adds an extra space Err(Ok(Error::Message(String::from("ROUNDTRIP error: B { ho: 24, a: A { hi: RawValue(42) } } != B { ho: 24, a: A { hi: RawValue( 42) } }")))) ); assert_eq!( ron::from_str::("(tag: B, content: (ho: 24, a: (hi:42)))"), Ok(AdjacentlyTagged::B { ho: 24, a: A { hi: ron::value::RawValue::from_boxed_ron(String::from("42").into_boxed_str()) .unwrap(), } }), ); assert_eq!( ron::from_str::("(content: (ho: 24, a: (hi:42)), tag: B)"), Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any valid RON-value-string"), found: String::from("the unsigned integer `42`") }, span: Span { start: Position { line: 1, col: 39 }, end: Position { line: 1, col: 39 } } }) ); } #[test] fn raw_value_inside_untagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Box, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: ron::value::RawValue::from_boxed_ron(String::from("42").into_boxed_str()) .unwrap(), } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), span: Span { start: Position { line: 6, col: 1 }, end: Position { line: 6, col: 2 } } })) ); } #[test] fn raw_value_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Box, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: B, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: ron::value::RawValue::from_boxed_ron( String::from("42").into_boxed_str() ) .unwrap(), } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any valid RON-value-string"), found: String::from("the unsigned integer `42`") }, span: Span { start: Position { line: 6, col: 1 }, end: Position { line: 6, col: 1 } } })) ); } #[test] fn raw_value_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Box, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: B, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: ron::value::RawValue::from_boxed_ron( String::from("42").into_boxed_str() ) .unwrap(), } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any valid RON-value-string"), found: String::from("the unsigned integer `42`") }, span: Span { start: Position { line: 6, col: 1 }, end: Position { line: 6, col: 1 } } })) ); } #[test] fn unit_like_zero_length_inside_internally_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: [i32; 0], } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: [] } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("an empty array"), found: String::from("a unit value") }, span: Span { start: Position { line: 7, col: 1 }, end: Position { line: 7, col: 2 } } })) ); } #[test] fn unit_like_zero_length_inside_adjacently_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct TupleStruct(); #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: TupleStruct, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum AdjacentlyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: TupleStruct() } }, PrettyConfig::default() ), Ok(()), ); assert_eq!( ron::from_str::("(tag: B, content: (ho: 24, a: (hi: ())))"), Ok(AdjacentlyTagged::B { ho: 24, a: A { hi: TupleStruct() } }), ); assert_eq!( ron::from_str::("(content: (ho: 24, a: (hi: ())), tag: B)"), Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("tuple struct TupleStruct"), found: String::from("a unit value") }, span: Span { start: Position { line: 1, col: 40 }, end: Position { line: 1, col: 40 } } }) ); } #[test] fn unit_like_zero_length_inside_untagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Struct {} #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Struct, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: Struct {} } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), span: Span { start: Position { line: 6, col: 1 }, end: Position { line: 6, col: 2 } } })) ); } #[test] fn unit_like_zero_length_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum Enum { Tuple(), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Enum, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: B, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: Enum::Tuple() } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("tuple variant"), found: String::from("a unit value") }, span: Span { start: Position { line: 6, col: 1 }, end: Position { line: 6, col: 1 } } })) ); } #[test] fn unit_like_zero_length_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum Enum { Struct {}, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Enum, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: B, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: Enum::Struct {} } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("struct variant"), found: String::from("a unit value") }, span: Span { start: Position { line: 6, col: 1 }, end: Position { line: 6, col: 1 } } })) ); } #[test] fn i128_inside_internally_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: i128, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { B { ho: i32, a: A }, } #[cfg(not(feature = "integer128"))] assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: i128::MAX } }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("i128 is not supported")))) ); #[cfg(feature = "integer128")] assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: i128::MAX } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any value"), found: format!("integer `{}` as u128", i128::MAX) }, span: Span { start: Position { line: 5, col: 13 }, end: Position { line: 5, col: 52 } } })) ); } #[test] fn u128_inside_adjacently_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: u128, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum AdjacentlyTagged { B { ho: i32, a: A }, } #[cfg(not(feature = "integer128"))] assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: u128::MAX } }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("u128 is not supported")))) ); #[cfg(feature = "integer128")] assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: u128::MAX } }, PrettyConfig::default() ), Ok(()), ); #[cfg(feature = "integer128")] assert_eq!( ron::from_str::(&format!( "(tag: B, content: (ho: 24, a: (hi: {})))", u128::MAX ),), Ok(AdjacentlyTagged::B { ho: 24, a: A { hi: u128::MAX } }), ); #[cfg(feature = "integer128")] assert_eq!( ron::from_str::(&format!( "(content: (ho: 24, a: (hi: {})), tag: B)", u128::MAX ),), Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any value"), found: format!("integer `{}` as u128", u128::MAX) }, span: Span { start: Position { line: 1, col: 28 }, end: Position { line: 1, col: 67 } } }), ); } #[test] fn i128_inside_untagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: i128, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { B { ho: i32, a: A }, } #[cfg(not(feature = "integer128"))] assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: i128::MIN } }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("i128 is not supported")))) ); #[cfg(feature = "integer128")] assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: i128::MIN } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any value"), found: format!("integer `{}` as i128", i128::MIN) }, span: Span { start: Position { line: 4, col: 14 }, end: Position { line: 4, col: 53 } } })) ); } #[test] fn u128_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: u128, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: B, } #[cfg(not(feature = "integer128"))] assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: u128::MAX } } }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("u128 is not supported")))) ); #[cfg(feature = "integer128")] assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: u128::MAX } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any value"), found: format!("integer `{}` as u128", u128::MAX) }, span: Span { start: Position { line: 4, col: 13 }, end: Position { line: 4, col: 52 } } })) ); } #[test] fn i128_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: i128, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: B, }, } #[cfg(not(feature = "integer128"))] assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: i128::MIN } } }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("i128 is not supported")))) ); #[cfg(feature = "integer128")] assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: i128::MIN } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any value"), found: format!("integer `{}` as i128", i128::MIN) }, span: Span { start: Position { line: 4, col: 14 }, end: Position { line: 4, col: 53 } } })) ); } #[test] fn duplicate_key_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: [(String::from("ho"), 42)].into_iter().collect(), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::DuplicateStructField { field: "ho", outer: None }, span: Span { start: Position { line: 3, col: 6 }, end: Position { line: 3, col: 9 } } })) ); } #[test] fn duplicate_key_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct C { ho: i32, hi: bool, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { #[serde(flatten)] inner: C, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] inner: B, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, inner: B { inner: C { ho: 42, hi: false } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::DuplicateStructField { field: "ho", outer: Some(String::from("A")) }, span: Span { start: Position { line: 3, col: 6 }, end: Position { line: 3, col: 9 } } })) ); } #[test] fn non_string_key_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: [(1, true), (0, false)].into_iter().collect(), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::ExpectedString, span: Span { start: Position { line: 3, col: 5 }, end: Position { line: 3, col: 5 } } })) ); } #[test] fn non_string_key_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: [('h', 0), ('i', 1)].into_iter().collect(), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::ExpectedString, span: Span { start: Position { line: 3, col: 5 }, end: Position { line: 3, col: 5 } } })) ); } #[test] fn more_than_one_flatten_map_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Deep { #[serde(flatten)] other: HashMap, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Inner { #[serde(flatten)] deep: Deep, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] hi: Inner, #[serde(flatten)] other: HashMap, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, hi: Inner { deep: Deep { other: HashMap::new(), }, }, other: [(String::from("42"), true)].into_iter().collect(), }, PrettyConfig::default() ), // both maps collect all the unknown keys Err(Ok(Error::Message(String::from("ROUNDTRIP error: FlattenedStruct { ho: 24, hi: Inner { deep: Deep { other: {} } }, other: {\"42\": true} } != FlattenedStruct { ho: 24, hi: Inner { deep: Deep { other: {\"42\": true} } }, other: {\"42\": true} }")))) ); } #[test] fn more_than_one_flatten_map_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Deep { #[serde(flatten)] other: HashMap, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Inner { #[serde(flatten)] deep: Deep, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] hi: Inner, #[serde(flatten)] other: HashMap, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, hi: Inner { deep: Deep { other: [(String::from("24"), false)].into_iter().collect(), }, }, other: HashMap::new(), }, PrettyConfig::default() ), // both maps collect all the unknown keys Err(Ok(Error::Message(String::from("ROUNDTRIP error: A { ho: 24, hi: Inner { deep: Deep { other: {\"24\": false} } }, other: {} } != A { ho: 24, hi: Inner { deep: Deep { other: {\"24\": false} } }, other: {\"24\": false} }")))) ); } #[test] fn flatten_struct_beside_map_inside_flatten_struct() { // The non-flattened struct must contain some flattened fields #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Flattened { hi: i32, #[serde(flatten)] flat: (), } // The non-flattened struct must be behind a level of flatten #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Inner { flat: Flattened, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] inner: Inner, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: HashMap::new(), inner: Inner { flat: Flattened { hi: 42, flat: () }, }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a map") }, span: Span { start: Position { line: 6, col: 1 }, end: Position { line: 6, col: 1 } } })) ); } #[test] fn flatten_struct_beside_map_inside_flatten_struct_variant() { // The non-flattened struct must contain some flattened fields #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Flattened { hi: i32, #[serde(flatten)] flat: (), } // The non-flattened struct must be behind a level of flatten #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Inner { flat: Flattened, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] inner: Inner, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: HashMap::new(), inner: Inner { flat: Flattened { hi: 42, flat: () }, }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a map") }, span: Span { start: Position { line: 6, col: 1 }, end: Position { line: 6, col: 1 } } })) ); } #[test] fn untagged_flatten_struct_variant_beside_map_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Flattened { Struct { hi: i32, #[serde(flatten)] flat: (), }, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: HashMap::new(), flat: Flattened::Struct { hi: 42, flat: () }, }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("ROUNDTRIP error: FlattenedStruct { ho: 24, other: {}, flat: Struct { hi: 42, flat: () } } != FlattenedStruct { ho: 24, other: {\"hi\": 42}, flat: Struct { hi: 42, flat: () } }")))) ); } #[test] fn untagged_flatten_struct_variant_beside_map_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Flattened { Struct { hi: i32, #[serde(flatten)] flat: (), }, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: HashMap::new(), flat: Flattened::Struct { hi: 42, flat: () }, }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("ROUNDTRIP error: A { ho: 24, other: {}, flat: Struct { hi: 42, flat: () } } != A { ho: 24, other: {\"hi\": 42}, flat: Struct { hi: 42, flat: () } }")))) ); } #[test] fn externally_tagged_newtype_variant_beside_map_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum Flattened { Newtype(()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: HashMap::new(), flat: Flattened::Newtype(()), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a unit value") }, span: Span { start: Position { line: 4, col: 1 }, end: Position { line: 4, col: 1 } } })) ); } #[test] fn externally_tagged_struct_variant_beside_map_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum Flattened { Struct { hi: i32 }, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: HashMap::new(), flat: Flattened::Struct { hi: 42 }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a map") }, span: Span { start: Position { line: 6, col: 1 }, end: Position { line: 6, col: 1 } } })) ); } #[test] fn externally_tagged_tuple_variant_beside_map_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum Flattened { Tuple(i32, bool), } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: HashMap::new(), flat: Flattened::Tuple(42, true), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a sequence") }, span: Span { start: Position { line: 7, col: 1 }, end: Position { line: 7, col: 1 } } })) ); } #[test] fn internally_tagged_unit_variant_beside_map_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum Flattened { Unit, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: HashMap::new(), flat: Flattened::Unit, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Unit\"") }, span: Span { start: Position { line: 4, col: 1 }, end: Position { line: 4, col: 1 } } })) ); } #[test] fn internally_tagged_newtype_variant_beside_map_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum Flattened { Newtype(()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: HashMap::new(), flat: Flattened::Newtype(()), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Newtype\"") }, span: Span { start: Position { line: 4, col: 1 }, end: Position { line: 4, col: 1 } } })) ); } #[test] fn internally_tagged_struct_variant_beside_map_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum Flattened { Struct { hi: i32 }, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: HashMap::new(), flat: Flattened::Struct { hi: 42 }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Struct\"") }, span: Span { start: Position { line: 5, col: 1 }, end: Position { line: 5, col: 1 } } })) ); } #[test] fn adjacently_tagged_unit_variant_beside_map_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum Flattened { Unit, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: HashMap::new(), flat: Flattened::Unit, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Unit\"") }, span: Span { start: Position { line: 4, col: 1 }, end: Position { line: 4, col: 1 } } })) ); } #[test] fn adjacently_tagged_newtype_variant_beside_map_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum Flattened { Newtype(()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: HashMap::new(), flat: Flattened::Newtype(()), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Newtype\"") }, span: Span { start: Position { line: 5, col: 1 }, end: Position { line: 5, col: 1 } } })) ); } #[test] fn adjacently_tagged_struct_variant_beside_map_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum Flattened { Struct { hi: i32 }, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: HashMap::new(), flat: Flattened::Struct { hi: 42 }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Struct\"") }, span: Span { start: Position { line: 7, col: 1 }, end: Position { line: 7, col: 1 } } })) ); } #[test] fn adjacently_tagged_tuple_variant_beside_map_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum Flattened { Tuple(i32, bool), } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: HashMap::new(), flat: Flattened::Tuple(42, true), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Tuple\"") }, span: Span { start: Position { line: 5, col: 1 }, end: Position { line: 5, col: 1 } } })) ); } #[test] fn tagged_struct_beside_map_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] struct Flattened { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: HashMap::new(), flat: Flattened { hi: 42 }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Flattened\"") }, span: Span { start: Position { line: 5, col: 1 }, end: Position { line: 5, col: 1 } } })) ); } #[test] fn tagged_struct_beside_map_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] struct Flattened { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: HashMap::new(), flat: Flattened { hi: 42 }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Flattened\"") }, span: Span { start: Position { line: 5, col: 1 }, end: Position { line: 5, col: 1 } } })) ); } #[test] fn zero_length_untagged_tuple_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { A(), } assert_eq!( check_roundtrip(&Untagged::A(), PrettyConfig::default()), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 3 } } })) ); } #[test] fn zero_length_untagged_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { A {}, } assert_eq!( check_roundtrip(&Untagged::A {}, PrettyConfig::default()), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 3 } } })) ); } #[test] fn unwrapped_one_element_untagged_tuple_variant() { // A tuple variant with just one element that is not a newtype variant #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { OneTuple(i32, #[serde(skip)] ()), } assert_eq!( check_roundtrip(&Untagged::OneTuple(42, ()), PrettyConfig::default()), Ok(()) ); assert_eq!( check_roundtrip( &Untagged::OneTuple(42, ()), PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) ), Ok(()) ); } #[test] fn unit_inside_untagged_newtype_variant_inside_internally_tagged_newtype_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Newtype(()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { Newtype(Untagged), } assert_eq!( check_roundtrip( &InternallyTagged::Newtype(Untagged::Newtype(())), PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), span: Span { start: Position { line: 3, col: 1 }, end: Position { line: 3, col: 2 } } })) ); } #[test] fn unit_inside_untagged_newtype_variant_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Newtype(()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: Untagged, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: Untagged::Newtype(()), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), span: Span { start: Position { line: 3, col: 1 }, end: Position { line: 3, col: 1 } } })) ); } #[test] fn unit_struct_inside_untagged_newtype_variant_inside_internally_tagged_newtype_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Unit; #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Newtype(Unit), } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { Newtype(Untagged), } assert_eq!( check_roundtrip( &InternallyTagged::Newtype(Untagged::Newtype(Unit)), PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), span: Span { start: Position { line: 3, col: 1 }, end: Position { line: 3, col: 2 } } })) ); } #[test] fn untagged_unit_variant_inside_internally_tagged_newtype_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Unit, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: Untagged, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: Untagged::Unit, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), span: Span { start: Position { line: 3, col: 1 }, end: Position { line: 3, col: 1 } } })) ); } #[test] fn untagged_unit_variant_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum Enum { Unit, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Unit, Newtype(Enum), } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { Newtype(Untagged), } assert_eq!( check_roundtrip( &InternallyTagged::Newtype(Untagged::Newtype(Enum::Unit)), PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &InternallyTagged::Newtype(Untagged::Unit), PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), span: Span { start: Position { line: 3, col: 1 }, end: Position { line: 3, col: 2 } } })) ); } #[test] fn unit_inside_internally_tagged_newtype_variant_inside_multi_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct AnotherFlattenedStruct { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { Newtype(()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: InternallyTagged, #[serde(flatten)] b: AnotherFlattenedStruct, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: InternallyTagged::Newtype(()), b: AnotherFlattenedStruct { hi: 42 }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("unit"), found: String::from("a map") }, span: Span { start: Position { line: 5, col: 1 }, end: Position { line: 5, col: 1 } } })) ); } #[test] fn untagged_unit_variant_inside_internally_tagged_newtype_variant_inside_multi_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Unit, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct AnotherFlattenedStruct { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { Newtype(Untagged), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: InternallyTagged, #[serde(flatten)] b: AnotherFlattenedStruct, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: InternallyTagged::Newtype(Untagged::Unit), b: AnotherFlattenedStruct { hi: 42 }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), span: Span { start: Position { line: 5, col: 1 }, end: Position { line: 5, col: 1 } } })) ); } #[test] fn flattened_externally_tagged_newtype_variant_beside_flattened_intenally_tagged_enum() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum ExternallyTagged { Newtype(()), Other(()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { Newtype(ExternallyTagged), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Flattened { #[serde(flatten)] a: InternallyTagged, #[serde(flatten)] b: ExternallyTagged, } assert_eq!( check_roundtrip( &Flattened { a: InternallyTagged::Newtype(ExternallyTagged::Other(())), b: ExternallyTagged::Newtype(()), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("map with a single key"), found: String::from("a map"), }, span: Span { start: Position { line: 5, col: 1 }, end: Position { line: 5, col: 1 } } })) ); } #[test] fn flattened_externally_tagged_struct_variant_beside_flattened_intenally_tagged_enum() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum ExternallyTagged { Struct { a: i32 }, Other(()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { Newtype(ExternallyTagged), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Flattened { #[serde(flatten)] a: InternallyTagged, #[serde(flatten)] b: ExternallyTagged, } assert_eq!( check_roundtrip( &Flattened { a: InternallyTagged::Newtype(ExternallyTagged::Other(())), b: ExternallyTagged::Struct { a: 42 }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("map with a single key"), found: String::from("a map"), }, span: Span { start: Position { line: 7, col: 1 }, end: Position { line: 7, col: 1 } } })) ); } #[test] fn flattened_map_inside_option_beside_flattened_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Struct { a: i32 }, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Flattened { #[serde(flatten)] a: Untagged, #[serde(flatten)] b: Option>, } assert_eq!( check_roundtrip( &Flattened { a: Untagged::Struct { a: 42, }, b: Some([(String::from("b"), 24)].into_iter().collect()), }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("ROUNDTRIP error: Flattened { a: Struct { a: 42 }, b: Some({\"b\": 24}) } != Flattened { a: Struct { a: 42 }, b: Some({\"a\": 42, \"b\": 24}) }")))) ); } #[test] fn flattened_untagged_struct_beside_flattened_untagged_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Struct { a: i32 }, Other { b: i32 }, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Flattened { #[serde(flatten)] a: Untagged, #[serde(flatten)] b: Untagged, } assert_eq!( check_roundtrip( &Flattened { a: Untagged::Struct { a: 42, }, b: Untagged::Other { b: 24, }, }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("ROUNDTRIP error: Flattened { a: Struct { a: 42 }, b: Other { b: 24 } } != Flattened { a: Struct { a: 42 }, b: Struct { a: 42 } }")))) ); } #[test] fn flattened_field_inside_flattened_struct_alongside_map() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Units { a: i32, #[serde(flatten)] b: (), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Flattened { #[serde(flatten)] a: Units, #[serde(flatten)] b: BTreeMap, } assert_eq!( check_roundtrip( &Flattened { a: Units { a: 42, b: (), }, b: [(String::from("c"), 24)].into_iter().collect(), }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("ROUNDTRIP error: Flattened { a: Units { a: 42, b: () }, b: {\"c\": 24} } != Flattened { a: Units { a: 42, b: () }, b: {\"a\": 42, \"c\": 24} }")))) ); } fn check_roundtrip( val: &T, config: PrettyConfig, ) -> Result<(), Result> { let ron = ron::ser::to_string_pretty(val, config).map_err(|err| Ok(err))?; println!("{ron}"); let de = ron::from_str(&ron).map_err(|err| Err(err))?; if val == &de { Ok(()) } else { Err(Ok(Error::Message(format!( "ROUNDTRIP error: {:?} != {:?}", val, de )))) } } ron-0.12.0/tests/508_value_adjacently_tagged_bug.rs000064400000000000000000000040111046102023000202640ustar 00000000000000use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(tag = "type", content = "value")] enum TheEnum { Variant([f32; 3]), } #[test] fn roundtrip_through_value() { let value = TheEnum::Variant([0.1, 0.1, 0.1]); let ron = ron::to_string(&value).unwrap(); assert_eq!(ron, "(type:Variant,value:(0.1,0.1,0.1))"); let de = ron::from_str::(&ron).unwrap(); assert_eq!(de, value); let ron_value = ron::from_str::(&ron).unwrap(); // Known bug: ron::Value only stores a unit, cannot find a variant let err = ron_value.into_rust::().unwrap_err(); assert_eq!( err, ron::Error::InvalidValueForType { expected: String::from("variant of enum TheEnum"), found: String::from("a unit value") } ); let old_serde_ron: &str = "(type:\"Variant\",value:(0.1,0.1,0.1))"; // Known bug: serde no longer uses strings in > v1.0.180 to deserialize the variant let err = ron::from_str::(&old_serde_ron).unwrap_err(); assert_eq!( err, ron::error::SpannedError { code: ron::Error::ExpectedIdentifier, span: ron::error::Span { start: ron::error::Position { line: 1, col: 7 }, end: ron::error::Position { line: 1, col: 7 }, } } ); let ron_value = ron::from_str::(&old_serde_ron).unwrap(); // Known bug: ron::Value is asked for an enum but has no special handling for it (yet) let err = ron_value.into_rust::().unwrap_err(); assert_eq!( err, ron::Error::InvalidValueForType { expected: String::from("variant of enum TheEnum"), found: String::from("the string \"Variant\"") } ); // This still works, but is a bug as well let ron_value = ron::from_str::("(\"Variant\",(0.1,0.1,0.1))").unwrap(); let de: TheEnum = ron_value.into_rust::().unwrap(); assert_eq!(de, value); } ron-0.12.0/tests/511_deserialize_any_map_string_key.rs000064400000000000000000000046121046102023000210450ustar 00000000000000#[test] fn test_map_custom_deserialize() { use std::collections::HashMap; #[derive(PartialEq, Debug)] struct CustomMap(HashMap); // Use a custom deserializer for CustomMap in order to extract String // keys in the visit_map method. impl<'de> serde::de::Deserialize<'de> for CustomMap { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { struct CVisitor; impl<'de> serde::de::Visitor<'de> for CVisitor { type Value = CustomMap; // GRCOV_EXCL_START fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { write!(formatter, "a map with string keys and values") } // GRCOV_EXCL_STOP fn visit_map(self, mut map: A) -> Result where A: serde::de::MapAccess<'de>, { let mut inner = HashMap::new(); while let Some((k, v)) = map.next_entry::()? { inner.insert(k, v); } Ok(CustomMap(inner)) } } // Note: This method will try to deserialize any value. In this test, it will // invoke the visit_map method in the visitor. deserializer.deserialize_any(CVisitor) } } let mut map = HashMap::::new(); map.insert("key1".into(), "value1".into()); map.insert("key2".into(), "value2".into()); let result: Result = ron::from_str( r#"( key1: "value1", key2: "value2", )"#, ); assert_eq!(result, Ok(CustomMap(map))); } #[test] fn test_ron_struct_as_json_map() { let json: serde_json::Value = ron::from_str("(f1: 0, f2: 1)").unwrap(); assert_eq!( json, serde_json::Value::Object( [ ( String::from("f1"), serde_json::Value::Number(serde_json::Number::from(0)) ), ( String::from("f2"), serde_json::Value::Number(serde_json::Number::from(1)) ), ] .into_iter() .collect() ) ); } ron-0.12.0/tests/522_explicit_struct_names.rs000064400000000000000000000057101046102023000172150ustar 00000000000000use ron::{ extensions::Extensions, from_str, ser::{to_string_pretty, PrettyConfig}, Error, Options, }; use serde_derive::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Serialize, Deserialize)] struct Id(u32); #[derive(Debug, PartialEq, Serialize, Deserialize)] struct Position(f32, f32); #[derive(Debug, PartialEq, Serialize, Deserialize)] enum Query { None, Creature(Id), Location(Position), } #[derive(Debug, PartialEq, Serialize, Deserialize)] struct Foo { #[allow(unused)] pub id: Id, #[allow(unused)] pub position: Position, #[allow(unused)] pub query: Query, } const EXPECT_ERROR_MESSAGE: &'static str = "expected `Err(Error::ExpectedStructName)`, deserializer returned `Ok`"; #[test] fn explicit_struct_names() { let options = Options::default().with_default_extension(Extensions::EXPLICIT_STRUCT_NAMES); let foo_ser = Foo { id: Id(3), position: Position(0.0, 8.72), query: Query::Creature(Id(4)), }; // phase 1 (regular structs) let content_regular = r#"( id: Id(3), position: Position(0.0, 8.72), query: None, )"#; let foo = options.from_str::(content_regular); assert_eq!( foo.expect_err(EXPECT_ERROR_MESSAGE).code, Error::ExpectedStructName("Foo".to_string()) ); // phase 2 (newtype structs) let content_newtype = r#"Foo( id: (3), position: Position(0.0, 8.72), query: None, )"#; let foo = options.from_str::(content_newtype); assert_eq!( foo.expect_err(EXPECT_ERROR_MESSAGE).code, Error::ExpectedStructName("Id".to_string()) ); // phase 3 (tuple structs) let content_tuple = r#"Foo( id: Id(3), position: (0.0, 8.72), query: None, )"#; let foo = options.from_str::(content_tuple); assert_eq!( foo.expect_err(EXPECT_ERROR_MESSAGE).code, Error::ExpectedStructName("Position".to_string()) ); // phase 4 (test without this extension) let _foo1 = from_str::(content_regular).unwrap(); let _foo2 = from_str::(content_newtype).unwrap(); let _foo3 = from_str::(content_tuple).unwrap(); // phase 5 (test serialization) let pretty_config = PrettyConfig::new() .extensions(Extensions::EXPLICIT_STRUCT_NAMES | Extensions::UNWRAP_VARIANT_NEWTYPES); let content = to_string_pretty(&foo_ser, pretty_config).unwrap(); assert_eq!( content, r#"#![enable(unwrap_variant_newtypes)] #![enable(explicit_struct_names)] Foo( id: Id(3), position: Position(0.0, 8.72), query: Creature(4), )"# ); let foo_de = from_str::(&content); match foo_de { // GRCOV_EXCL_START Err(err) => panic!( "failed to deserialize with `explicit_struct_names` and `unwrap_variant_newtypes`: {}", err ), // GRCOV_EXCL_STOP Ok(foo_de) => assert_eq!(foo_de, foo_ser), } } ron-0.12.0/tests/526_flatten.rs000064400000000000000000000023441046102023000142460ustar 00000000000000use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] struct SomeCollection { inner: Vec, } #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] struct SomeItem { #[serde(flatten)] foo: Foo, #[serde(flatten)] bar: Bar, } #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] struct Bar { name: String, some_enum: Option, } #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] struct Foo { something: String, } #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] enum SomeEnum { A, B, } #[test] fn roundtrip() { let scene = SomeCollection { inner: vec![SomeItem { foo: Foo { something: "something".to_string(), }, bar: Bar { name: "name".to_string(), some_enum: Some(SomeEnum::A), }, }], }; let ron = ron::ser::to_string(&scene).unwrap(); let de: SomeCollection = ron::de::from_str(&ron).unwrap(); assert_eq!(de, scene); let ron = ron::ser::to_string_pretty(&scene, Default::default()).unwrap(); let _deser_scene: SomeCollection = ron::de::from_str(&ron).unwrap(); assert_eq!(de, scene); } ron-0.12.0/tests/530_untagged_union.rs000064400000000000000000000010151046102023000156040ustar 00000000000000use std::ops::Bound; #[derive(serde::Deserialize, serde::Serialize, PartialEq, Eq, Debug)] #[serde(untagged)] enum EitherInterval { B(Interval), D(V, Option), } type Interval = (Bound, Bound); #[test] fn serde_roundtrip() { let interval = EitherInterval::B((Bound::Excluded(0u8), Bound::Unbounded)); let ron: String = ron::ser::to_string(&interval).unwrap(); assert_eq!(ron, "(Excluded(0),Unbounded)"); let de = ron::de::from_str(&ron).unwrap(); assert_eq!(interval, de); } ron-0.12.0/tests/544_path_meta.rs000064400000000000000000000057731046102023000145640ustar 00000000000000use ron::ser::{path_meta::Field, PrettyConfig}; #[test] fn serialize_field() { #[derive(serde::Serialize)] enum PetKind { Isopod, } #[derive(serde::Serialize)] struct Pet { name: &'static str, age: u8, kind: PetKind, } #[derive(serde::Serialize)] struct Person { name: &'static str, age: u8, knows: Vec, pet: Option, } let value = ( Person { name: "Walter", age: 43, knows: vec![0, 1], pet: None, }, vec![ Person { name: "Alice", age: 29, knows: vec![1], pet: Some(Pet { name: "Herbert", age: 7, kind: PetKind::Isopod, }), }, Person { name: "Bob", age: 29, knows: vec![0], pet: None, }, ], ); let mut config = PrettyConfig::default(); config .path_meta .get_or_insert_with(Field::empty) .build_fields(|fields| { fields .field("age") .with_doc("0@age (person)\nmust be within range 0..256"); fields .field("knows") .with_doc("0@knows (person)\nmust be list of valid person indices"); fields.field("pet").build_fields(|fields| { fields .field("age") .with_doc("1@age (pet)\nmust be valid range 0..256"); fields .field("kind") .with_doc("1@kind (pet)\nmust be `Isopod`"); }); // provide meta for a field that doesn't exist; // this should not end up anywhere in the final string fields.field("0").with_doc("unreachable"); }); let s = ron::ser::to_string_pretty(&value, config).unwrap(); assert_eq!( s, r#"(( name: "Walter", /// 0@age (person) /// must be within range 0..256 age: 43, /// 0@knows (person) /// must be list of valid person indices knows: [ 0, 1, ], pet: None, ), [ ( name: "Alice", /// 0@age (person) /// must be within range 0..256 age: 29, /// 0@knows (person) /// must be list of valid person indices knows: [ 1, ], pet: Some(( name: "Herbert", /// 1@age (pet) /// must be valid range 0..256 age: 7, /// 1@kind (pet) /// must be `Isopod` kind: Isopod, )), ), ( name: "Bob", /// 0@age (person) /// must be within range 0..256 age: 29, /// 0@knows (person) /// must be list of valid person indices knows: [ 0, ], pet: None, ), ])"# ); } ron-0.12.0/tests/564_exhaustive_number.rs000064400000000000000000000024531046102023000163510ustar 00000000000000use ron::Number; #[test] fn exhaustive_number_match() { match Number::U8(42) { Number::I8(v) => println!("{v}"), Number::I16(v) => println!("{v}"), Number::I32(v) => println!("{v}"), Number::I64(v) => println!("{v}"), #[cfg(feature = "integer128")] Number::I128(v) => println!("{v}"), Number::U8(v) => println!("{v}"), Number::U16(v) => println!("{v}"), Number::U32(v) => println!("{v}"), Number::U64(v) => println!("{v}"), #[cfg(feature = "integer128")] Number::U128(v) => println!("{v}"), Number::F32(v) => println!("{}", v.0), Number::F64(v) => println!("{}", v.0), #[cfg(not(doc))] Number::__NonExhaustive(never) => never.never(), } } #[test] fn non_exhaustive_number_match() { match Number::U8(42) { Number::I8(v) => println!("{v}"), Number::I16(v) => println!("{v}"), Number::I32(v) => println!("{v}"), Number::I64(v) => println!("{v}"), Number::U8(v) => println!("{v}"), Number::U16(v) => println!("{v}"), Number::U32(v) => println!("{v}"), Number::U64(v) => println!("{v}"), Number::F32(v) => println!("{}", v.0), Number::F64(v) => println!("{}", v.0), v => println!("{v:?}"), } } ron-0.12.0/tests/581_serde_core_content.rs000064400000000000000000000011521046102023000164520ustar 00000000000000use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(tag = "type")] enum Message { Request { id: u32, resource: String, operation: String, }, Response { id: u32, value: String, }, } #[test] fn internally_tagged_enum_serde_core_content_detection() { let value = Message::Response { id: 60069, value: "Foobar".into(), }; let serialized = ron::to_string(&value).unwrap(); let deserialized: Message = ron::from_str(&serialized).unwrap(); assert_eq!(deserialized, value); } ron-0.12.0/tests/big_struct.rs000064400000000000000000000041261046102023000143620ustar 00000000000000use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] pub struct ImVec2 { pub x: f32, pub y: f32, } #[derive(Serialize, Deserialize)] pub struct ImColorsSave { pub text: f32, } #[derive(Serialize, Deserialize)] pub struct ImGuiStyleSave { pub alpha: f32, pub window_padding: ImVec2, pub window_min_size: ImVec2, pub window_rounding: f32, pub window_title_align: ImVec2, pub child_window_rounding: f32, pub frame_padding: ImVec2, pub frame_rounding: f32, pub item_spacing: ImVec2, pub item_inner_spacing: ImVec2, pub touch_extra_padding: ImVec2, pub indent_spacing: f32, pub columns_min_spacing: f32, pub scrollbar_size: f32, pub scrollbar_rounding: f32, pub grab_min_size: f32, pub grab_rounding: f32, pub button_text_align: ImVec2, pub display_window_padding: ImVec2, pub display_safe_area_padding: ImVec2, pub anti_aliased_lines: bool, pub anti_aliased_shapes: bool, pub curve_tessellation_tol: f32, pub colors: ImColorsSave, pub new_type: NewType, } #[derive(Serialize, Deserialize)] pub struct NewType(i32); const CONFIG: &str = "( alpha: 1.0, window_padding: (x: 8, y: 8), window_min_size: (x: 32, y: 32), window_rounding: 9.0, window_title_align: (x: 0.0, y: 0.5), child_window_rounding: 0.0, frame_padding: (x: 4, y: 3), frame_rounding: 0.0, item_spacing: (x: 8, y: 4), item_inner_spacing: (x: 4, y: 4), touch_extra_padding: (x: 0, y: 0), indent_spacing: 21.0, columns_min_spacing: 6.0, scrollbar_size: 16, scrollbar_rounding: 9, grab_min_size: 10, grab_rounding: 0, button_text_align: (x: 0.5, y: 0.5), display_window_padding: (x: 22, y: 22), display_safe_area_padding: (x: 4, y: 4), anti_aliased_lines: true, anti_aliased_shapes: true, curve_tessellation_tol: 1.25, colors: (text: 4), new_type: NewType( 1 ), ignored_field: \"Totally ignored, not causing a panic. Hopefully.\", )"; #[test] fn deserialize_big_struct() { ron::de::from_str::(CONFIG).unwrap(); } ron-0.12.0/tests/borrowed_str.rs000064400000000000000000000005101046102023000147210ustar 00000000000000use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] struct Borrowed<'a> { value: &'a str, } const BORROWED: &str = "Borrowed(value: \"test\")"; #[test] fn borrowed_str() { assert_eq!( ron::de::from_str(BORROWED).ok(), Some(Borrowed { value: "test" }) ); } ron-0.12.0/tests/comments.rs000064400000000000000000000036241046102023000140440ustar 00000000000000use ron::de::{from_str, Error, Position, Span, SpannedError as RonErr}; #[test] fn test_simple() { assert_eq!( from_str( "/* * We got a hexadecimal number here! * */0x507" ), Ok(0x507) ); } #[test] fn test_nested() { assert_eq!( from_str( "/* /* quite * some * nesting * going * on * /* here /* (yeah, maybe a bit too much) */ */ */ */ // The actual value comes.. /* // very soon, these are just checks that */ // multi-line comments don't trigger in line comments /* \"THE VALUE\" /* This is the value /* :) */ */ " ), Ok("THE VALUE".to_owned()) ); } #[test] fn test_unclosed() { assert_eq!( from_str::("\"hi\" /*"), Err(RonErr { code: Error::UnclosedBlockComment, span: Span { start: Position { line: 1, col: 6 }, end: Position { line: 1, col: 8 } } }) ); assert_eq!( from_str::( "/* /* quite * some * nesting * going * on * /* here /* (yeah, maybe a bit too much) */ */ */ */ // The actual value comes.. /* // very soon, these are just checks that */ // multi-line comments don't trigger in line comments /* /* Unfortunately, this comment won't get closed :( \"THE VALUE (which is invalid)\" " ), Err(RonErr { code: Error::UnclosedBlockComment, span: Span { start: Position { line: 7, col: 3 }, end: Position { line: 9, col: 1 } } }) ); } #[test] fn test_unexpected_byte() { assert_eq!( from_str::("42 /q"), Err(RonErr { code: Error::UnexpectedChar('q'), span: Span { start: Position { line: 1, col: 4 }, end: Position { line: 1, col: 6 }, } }) ); } ron-0.12.0/tests/depth_limit.rs000064400000000000000000000022421046102023000145140ustar 00000000000000use std::collections::HashMap; use serde::Serialize; #[derive(Serialize)] struct Config { float: (f32, f64), tuple: TupleStruct, map: HashMap, nested: Nested, var: Variant, array: Vec<()>, } #[derive(Serialize)] struct TupleStruct((), bool); #[derive(Serialize)] enum Variant { A(u8, &'static str), } #[derive(Serialize)] struct Nested { a: String, b: char, } const EXPECTED: &str = "( float: (2.18, -1.1), tuple: ((), false), map: {8: '1'}, nested: (a: \"a\", b: 'b'), var: A(255, \"\"), array: [(), (), ()], )"; #[test] fn depth_limit() { let data = Config { float: (2.18, -1.1), tuple: TupleStruct((), false), map: vec![(8, '1')].into_iter().collect(), nested: Nested { a: "a".to_owned(), b: 'b', }, var: Variant::A(!0, ""), array: vec![(); 3], }; let pretty = ron::ser::PrettyConfig::new() .depth_limit(1) .separate_tuple_members(true) .enumerate_arrays(true) .new_line("\n"); let s = ron::ser::to_string_pretty(&data, pretty); assert_eq!(s, Ok(EXPECTED.to_string())); } ron-0.12.0/tests/escape.rs000064400000000000000000000053341046102023000134570ustar 00000000000000use std::{char::from_u32, fmt::Debug}; use ron::{de::from_str, ser::to_string}; use serde::{Deserialize, Serialize}; #[test] fn test_escape_basic() { assert_eq!(to_string(&"\x07").unwrap(), "\"\\u{7}\""); assert_eq!(from_str::("\"\\x07\"").unwrap(), "\x07"); assert_eq!(from_str::("\"\\u{7}\"").unwrap(), "\x07"); assert_eq!(from_str::("\'\\x07\'").unwrap(), '\x07'); assert_eq!(from_str::("\'\\u{7}\'").unwrap(), '\x07'); assert_eq!( from_str::("\'\\u{}\'").unwrap_err(), ron::error::SpannedError { code: ron::Error::InvalidEscape("Expected 1-6 digits, got 0 digits in Unicode escape"), span: ron::error::Span { start: ron::error::Position { line: 1, col: 4 }, end: ron::error::Position { line: 1, col: 5 }, } } ); assert_eq!( from_str::("\'\\q\'").unwrap_err(), ron::error::SpannedError { code: ron::Error::InvalidEscape("Unknown escape character"), span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 4 }, } } ) } fn check_same(t: T) where T: Debug + for<'a> Deserialize<'a> + PartialEq + Serialize, { let s: String = to_string(&t).unwrap(); println!("Serialized: \n\n{}\n\n", s); assert_eq!(from_str(&s), Ok(t)); } #[test] fn test_ascii_10() { check_same("\u{10}".to_owned()); } #[test] fn test_ascii_chars() { (1..128).flat_map(from_u32).for_each(check_same) } #[test] fn test_ascii_string() { let s: String = (1..128).flat_map(from_u32).collect(); check_same(s); } #[test] fn test_non_ascii() { assert_eq!(to_string(&"♠").unwrap(), "\"♠\""); assert_eq!(to_string(&"ß").unwrap(), "\"ß\""); assert_eq!(to_string(&"ä").unwrap(), "\"ä\""); assert_eq!(to_string(&"ö").unwrap(), "\"ö\""); assert_eq!(to_string(&"ü").unwrap(), "\"ü\""); } #[test] fn test_chars() { assert_eq!(to_string(&'♠').unwrap(), "'♠'"); assert_eq!(to_string(&'ß').unwrap(), "'ß'"); assert_eq!(to_string(&'ä').unwrap(), "'ä'"); assert_eq!(to_string(&'ö').unwrap(), "'ö'"); assert_eq!(to_string(&'ü').unwrap(), "'ü'"); assert_eq!(to_string(&'\u{715}').unwrap(), "'\u{715}'"); assert_eq!( from_str::("'\u{715}'").unwrap(), from_str("'\\u{715}'").unwrap() ); } #[test] fn test_nul_in_string() { assert_eq!( from_str("\"Hello\0World!\""), Ok(String::from("Hello\0World!")) ); check_same("Hello\0World!".to_owned()); check_same("Hello\x00World!".to_owned()); check_same("Hello\u{0}World!".to_owned()); } ron-0.12.0/tests/extensions.rs000064400000000000000000000035071046102023000144160ustar 00000000000000use std::collections::HashMap; use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Deserialize, Serialize)] struct UnitStruct; #[derive(Debug, PartialEq, Deserialize, Serialize)] struct NewType(f32); #[derive(Debug, PartialEq, Deserialize, Serialize)] struct TupleStruct(UnitStruct, i8); #[derive(Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] struct Key(u32); #[derive(Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] enum Enum { Unit, Bool(bool), Chars(char, String), } #[derive(Debug, PartialEq, Deserialize, Serialize)] struct Struct { tuple: ((), NewType, TupleStruct), vec: Vec>, map: HashMap, } const CONFIG_U_NT: &str = " #![enable(unwrap_newtypes)] ( tuple: ((), 0.5, ((), -5)), vec: [ None, Some(()), ], map: { 7: Bool(true), 9: Chars('x', \"\"), 6: Bool(false), 5: Unit, }, ) "; #[test] fn unwrap_newtypes() { let d: Struct = ron::de::from_str(CONFIG_U_NT).expect("Failed to deserialize"); println!("unwrap_newtypes: {:#?}", d); let s = ron::ser::to_string_pretty( &d, ron::ser::PrettyConfig::default().extensions(ron::extensions::Extensions::UNWRAP_NEWTYPES), ) .expect("Failed to serialize"); let d2: Struct = ron::de::from_str(&s).expect("Failed to deserialize"); assert_eq!(d, d2); } const CONFIG_I_S: &str = " #![enable(implicit_some)] ( tuple: ((), (0.5), ((), -5)), vec: [ None, (), UnitStruct, None, (), ], map: { (7): Bool(true), (9): Chars('x', \"\"), (6): Bool(false), (5): Unit, }, ) "; #[test] fn implicit_some() { let d: Struct = ron::de::from_str(CONFIG_I_S).expect("Failed to deserialize"); println!("implicit_some: {:#?}", d); } ron-0.12.0/tests/floats.rs000064400000000000000000000012521046102023000135020ustar 00000000000000use ron::{ de::from_str, ser::{to_string, to_string_pretty, PrettyConfig}, }; #[test] fn test_inf_and_nan() { assert_eq!(from_str("inf"), Ok(std::f64::INFINITY)); assert_eq!(from_str("-inf"), Ok(std::f64::NEG_INFINITY)); assert_eq!(from_str::("NaN").map(|n| n.is_nan()), Ok(true)) } #[test] fn decimal_floats() { let non_pretty = to_string(&1.0).unwrap(); assert_eq!(non_pretty, "1.0"); let with_pretty = to_string_pretty(&1.0, PrettyConfig::new()).unwrap(); assert_eq!(with_pretty, "1.0"); let tiny_pretty = to_string_pretty(&0.00000000000000005, PrettyConfig::new()).unwrap(); assert_eq!(tiny_pretty, "0.00000000000000005"); } ron-0.12.0/tests/large_number.rs000064400000000000000000000016071046102023000146600ustar 00000000000000use ron::value::{Number, Value}; #[test] fn test_large_number() { let test_var = Value::Number(Number::new(10000000000000000000000.0f64)); let test_ser = ron::ser::to_string(&test_var).unwrap(); let test_deser = ron::de::from_str::(&test_ser); assert_eq!( test_deser.unwrap(), Value::Number(Number::new(10000000000000000000000.0)) ); } #[test] fn test_large_integer_to_float() { use ron::value::Number; let test_var = std::u64::MAX as u128 + 1; let test_ser = test_var.to_string(); let test_deser = ron::de::from_str::(&test_ser); #[cfg(not(feature = "integer128"))] assert_eq!( test_deser.unwrap(), Value::Number(Number::F32((test_var as f32).into())), // f64 representation matches f32 ); #[cfg(feature = "integer128")] assert_eq!(test_deser.unwrap(), Value::Number(Number::U128(test_var)),); } ron-0.12.0/tests/min_max.rs000064400000000000000000000024021046102023000136400ustar 00000000000000use ron::{de::*, ser::*}; #[test] fn test_i32_min() { assert_eq!( std::i32::MIN, from_str(&to_string(&std::i32::MIN).unwrap()).unwrap() ); } #[test] fn test_i32_max() { assert_eq!( std::i32::MAX, from_str(&to_string(&std::i32::MAX).unwrap()).unwrap() ); } #[test] fn test_i64_min() { assert_eq!( std::i64::MIN, from_str(&to_string(&std::i64::MIN).unwrap()).unwrap() ); } #[test] fn test_i64_max() { assert_eq!( std::i64::MAX, from_str(&to_string(&std::i64::MAX).unwrap()).unwrap() ); } #[cfg(feature = "integer128")] #[test] fn test_i128_min() { assert_eq!( std::i128::MIN, from_str(&to_string(&std::i128::MIN).unwrap()).unwrap() ); } #[cfg(feature = "integer128")] #[test] fn test_i128_max() { assert_eq!( std::i128::MAX, from_str(&to_string(&std::i128::MAX).unwrap()).unwrap() ); } #[cfg(feature = "integer128")] #[test] fn test_u128_min() { assert_eq!( std::u128::MIN, from_str(&to_string(&std::u128::MIN).unwrap()).unwrap() ); } #[cfg(feature = "integer128")] #[test] fn test_u128_max() { assert_eq!( std::u128::MAX, from_str(&to_string(&std::u128::MAX).unwrap()).unwrap() ); } ron-0.12.0/tests/non_identifier_identifier.rs000064400000000000000000000077471046102023000174270ustar 00000000000000macro_rules! test_non_identifier { ($test_name:ident => $deserialize_method:ident($($deserialize_param:expr),*)) => { #[test] fn $test_name() { use serde::{Deserialize, Deserializer, de::Visitor, de::MapAccess}; struct FieldVisitor; impl<'de> Visitor<'de> for FieldVisitor { type Value = FieldName; // GRCOV_EXCL_START fn expecting(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { fmt.write_str("an error") } // GRCOV_EXCL_STOP } struct FieldName; impl<'de> Deserialize<'de> for FieldName { fn deserialize>(deserializer: D) -> Result { deserializer.$deserialize_method($($deserialize_param,)* FieldVisitor) } } struct StructVisitor; impl<'de> Visitor<'de> for StructVisitor { type Value = Struct; // GRCOV_EXCL_START fn expecting(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { fmt.write_str("a struct") } // GRCOV_EXCL_STOP fn visit_map>(self, mut map: A) -> Result { map.next_key::().map(|_| Struct) } } #[derive(Debug)] struct Struct; impl<'de> Deserialize<'de> for Struct { fn deserialize>(deserializer: D) -> Result { deserializer.deserialize_struct("Struct", &[], StructVisitor) } } assert_eq!( ron::from_str::("(true: 4)").unwrap_err().code, ron::Error::ExpectedIdentifier ) } }; } test_non_identifier! { test_bool => deserialize_bool() } test_non_identifier! { test_i8 => deserialize_i8() } test_non_identifier! { test_i16 => deserialize_i16() } test_non_identifier! { test_i32 => deserialize_i32() } test_non_identifier! { test_i64 => deserialize_i64() } #[cfg(feature = "integer128")] test_non_identifier! { test_i128 => deserialize_i128() } test_non_identifier! { test_u8 => deserialize_u8() } test_non_identifier! { test_u16 => deserialize_u16() } test_non_identifier! { test_u32 => deserialize_u32() } test_non_identifier! { test_u64 => deserialize_u64() } #[cfg(feature = "integer128")] test_non_identifier! { test_u128 => deserialize_u128() } test_non_identifier! { test_f32 => deserialize_f32() } test_non_identifier! { test_f64 => deserialize_f64() } test_non_identifier! { test_char => deserialize_char() } // Removed due to fix for #511 - string keys are allowed. // test_non_identifier! { test_string => deserialize_string() } // See comment above. If deserialize_str is to be added, it should give the same expected result as // deserialize_string. deserialize_str and deserialize_string should be consistently implemented. // test_non_identifier! { test_str => deserialize_str() } test_non_identifier! { test_bytes => deserialize_bytes() } test_non_identifier! { test_byte_buf => deserialize_byte_buf() } test_non_identifier! { test_option => deserialize_option() } test_non_identifier! { test_unit => deserialize_unit() } test_non_identifier! { test_unit_struct => deserialize_unit_struct("") } test_non_identifier! { test_newtype_struct => deserialize_newtype_struct("") } test_non_identifier! { test_seq => deserialize_seq() } test_non_identifier! { test_tuple => deserialize_tuple(0) } test_non_identifier! { test_tuple_struct => deserialize_tuple_struct("", 0) } test_non_identifier! { test_map => deserialize_map() } test_non_identifier! { test_struct => deserialize_struct("", &[]) } test_non_identifier! { test_enum => deserialize_enum("", &[]) } ron-0.12.0/tests/numbers.rs000064400000000000000000000074221046102023000136720ustar 00000000000000use ron::{ de::from_str, error::{Error, Position, Span, SpannedError}, }; #[test] fn test_hex() { assert_eq!(from_str("0x507"), Ok(0x507)); assert_eq!(from_str("0x1A5"), Ok(0x1A5)); assert_eq!(from_str("0x53C537"), Ok(0x53C537)); assert_eq!( from_str::("0x"), Err(SpannedError { code: Error::ExpectedInteger, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 3 }, } }) ); assert_eq!( from_str::("0x_1"), Err(SpannedError { code: Error::UnderscoreAtBeginning, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 3 }, } }) ); assert_eq!( from_str::("0xFFF"), Err(SpannedError { code: Error::IntegerOutOfBounds, span: Span { start: Position { line: 1, col: 3 }, end: Position { line: 1, col: 6 }, } }) ); } #[test] fn test_bin() { assert_eq!(from_str("0b101"), Ok(0b101)); assert_eq!(from_str("0b001"), Ok(0b001)); assert_eq!(from_str("0b100100"), Ok(0b100100)); assert_eq!( from_str::("0b"), Err(SpannedError { code: Error::ExpectedInteger, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 3 }, } }) ); assert_eq!( from_str::("0b_1"), Err(SpannedError { code: Error::UnderscoreAtBeginning, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 3 }, } }) ); assert_eq!( from_str::("0b111111111"), Err(SpannedError { code: Error::IntegerOutOfBounds, span: Span { start: Position { line: 1, col: 3 }, end: Position { line: 1, col: 12 }, } }) ); } #[test] fn test_oct() { assert_eq!(from_str("0o1461"), Ok(0o1461)); assert_eq!(from_str("0o051"), Ok(0o051)); assert_eq!(from_str("0o150700"), Ok(0o150700)); assert_eq!( from_str::("0o"), Err(SpannedError { code: Error::ExpectedInteger, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 3 }, } }) ); assert_eq!( from_str::("0o_1"), Err(SpannedError { code: Error::UnderscoreAtBeginning, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 3 }, } }) ); assert_eq!( from_str::("0o77777"), Err(SpannedError { code: Error::IntegerOutOfBounds, span: Span { start: Position { line: 1, col: 3 }, end: Position { line: 1, col: 8 }, } }) ); } #[test] fn test_dec() { assert_eq!(from_str("1461"), Ok(1461)); assert_eq!(from_str("51"), Ok(51)); assert_eq!(from_str("150700"), Ok(150700)); assert_eq!( from_str::("-_1"), Err(SpannedError { code: Error::UnderscoreAtBeginning, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 2 }, } }) ); assert_eq!( from_str::("256"), Err(SpannedError { code: Error::IntegerOutOfBounds, span: Span { start: Position { line: 1, col: 1 }, end: Position { line: 1, col: 4 }, } }) ); } ron-0.12.0/tests/options.rs000064400000000000000000000066251046102023000137160ustar 00000000000000use ron::{extensions::Extensions, ser::PrettyConfig, Options}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] struct Newtype(f64); #[derive(Serialize, Deserialize)] struct Struct(Option, Newtype); #[test] fn default_options() { let ron = Options::default(); let de: Struct = ron.from_str("(Some(42),(4.2))").unwrap(); let ser = ron.to_string(&de).unwrap(); assert_eq!(ser, "(Some(42),(4.2))"); } #[test] fn without_any_options() { let mut ron = Options::default().with_default_extension(Extensions::all()); for extension in Extensions::all().iter() { ron = ron.without_default_extension(extension); } let de: Struct = ron.from_str("(Some(42),(4.2))").unwrap(); let ser = ron.to_string(&de).unwrap(); assert_eq!(ser, "(Some(42),(4.2))"); } #[test] fn single_default_extension() { let ron = Options::default().with_default_extension(Extensions::IMPLICIT_SOME); let de: Struct = ron.from_str("(42,(4.2))").unwrap(); let ser = ron.to_string(&de).unwrap(); assert_eq!(ser, "(42,(4.2))"); let de: Struct = ron.from_str("#![enable(implicit_some)](42,(4.2))").unwrap(); let ser = ron.to_string(&de).unwrap(); assert_eq!(ser, "(42,(4.2))"); let de: Struct = ron .from_str("#![enable(implicit_some)]#![enable(unwrap_newtypes)](42,4.2)") .unwrap(); let ser = ron.to_string(&de).unwrap(); assert_eq!(ser, "(42,(4.2))"); let de: Struct = ron .from_str("#![enable(implicit_some)]#![enable(unwrap_newtypes)](42,4.2)") .unwrap(); let ser = ron .to_string_pretty( &de, PrettyConfig::default().extensions(Extensions::UNWRAP_NEWTYPES), ) .unwrap(); assert_eq!(ser, "#![enable(unwrap_newtypes)]\n(42, 4.2)"); } #[cfg(feature = "std")] #[test] fn reader_io_error() { struct Reader<'a> { buf: &'a [u8], } impl<'a> std::io::Read for Reader<'a> { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { let written = self.buf.read(buf)?; if written == 0 { Err(std::io::Error::new(std::io::ErrorKind::BrokenPipe, "oh no")) } else { Ok(written) } } } assert_eq!( ron::de::from_reader::(Reader { buf: b"" }).unwrap_err(), ron::error::SpannedError { code: ron::Error::Io(String::from("oh no")), span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 1 }, } } ); assert_eq!( ron::de::from_reader::(Reader { buf: b"hello" }).unwrap_err(), ron::error::SpannedError { code: ron::Error::Io(String::from("oh no")), span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 1, col: 6 }, } } ); assert_eq!( ron::de::from_reader::(Reader { buf: b"hello\nmy \xff" }) .unwrap_err(), ron::error::SpannedError { code: ron::Error::Io(String::from("oh no")), span: ron::error::Span { start: ron::error::Position { line: 1, col: 1 }, end: ron::error::Position { line: 2, col: 4 }, } } ); } ron-0.12.0/tests/preserve_sequence.rs000064400000000000000000000021411046102023000157330ustar 00000000000000use std::collections::BTreeMap; use ron::{ de::from_str, ser::{to_string_pretty, PrettyConfig}, }; use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, Serialize)] struct Config { boolean: bool, float: f32, map: BTreeMap, nested: Nested, tuple: (u32, u32), } #[derive(Debug, Deserialize, Serialize)] struct Nested { a: String, b: char, } fn read_original(source: &str) -> String { source.to_string().replace("\r\n", "\n") } fn make_roundtrip(source: &str) -> String { let config: Config = from_str(source).unwrap(); let pretty = PrettyConfig::new() .depth_limit(3) .separate_tuple_members(true) .enumerate_arrays(true) .new_line("\n"); to_string_pretty(&config, pretty).expect("Serialization failed") } #[test] fn test_sequence_ex1() { let file = include_str!("preserve_sequence_ex1.ron"); assert_eq!(read_original(file), make_roundtrip(file)); } #[test] fn test_sequence_ex2() { let file = include_str!("preserve_sequence_ex2.ron"); assert_eq!(read_original(file), make_roundtrip(file)); } ron-0.12.0/tests/preserve_sequence_ex1.ron000064400000000000000000000004001046102023000166560ustar 00000000000000( boolean: true, float: 8.2, map: { 1: '1', 2: '4', 3: '9', 4: '1', 5: '2', 6: '3', }, nested: ( a: "Decode me!", b: 'z', ), tuple: ( 3, 7, ), )ron-0.12.0/tests/preserve_sequence_ex2.ron000064400000000000000000000002601046102023000166630ustar 00000000000000( boolean: true, float: 8.2, map: { 1: '1', }, nested: ( a: "Decode me!", b: 'z', ), tuple: ( 3, 7, ), )ron-0.12.0/tests/roundtrip.rs000064400000000000000000000060051046102023000142410ustar 00000000000000use std::collections::HashMap; use ron::extensions::Extensions; use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Deserialize, Serialize)] struct UnitStruct; #[derive(Debug, PartialEq, Deserialize, Serialize)] struct NewType(f32); #[derive(Debug, PartialEq, Deserialize, Serialize)] struct TupleStruct(UnitStruct, i8); #[derive(Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] struct Key(u32); #[derive(Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] enum Enum { Unit, Bool(bool), Chars(char, String), } #[derive(Debug, PartialEq, Deserialize, Serialize)] struct Struct { tuple: ((), NewType, TupleStruct), vec: Vec>, map: HashMap, } #[test] fn roundtrip() { let value = Struct { tuple: ((), NewType(0.5), TupleStruct(UnitStruct, -5)), vec: vec![None, Some(UnitStruct)], map: vec![ (Key(5), Enum::Unit), (Key(6), Enum::Bool(false)), (Key(7), Enum::Bool(true)), (Key(9), Enum::Chars('x', "".to_string())), ] .into_iter() .collect(), }; let serial = ron::ser::to_string(&value).unwrap(); println!("Serialized: {}", serial); let deserial = ron::de::from_str(&serial); assert_eq!(Ok(value), deserial); } #[test] fn roundtrip_pretty() { let value = Struct { tuple: ((), NewType(0.5), TupleStruct(UnitStruct, -5)), vec: vec![None, Some(UnitStruct)], map: vec![ (Key(5), Enum::Unit), (Key(6), Enum::Bool(false)), (Key(7), Enum::Bool(true)), (Key(9), Enum::Chars('x', "".to_string())), ] .into_iter() .collect(), }; let pretty = ron::ser::PrettyConfig::new() .enumerate_arrays(true) .extensions(Extensions::IMPLICIT_SOME); let serial = ron::ser::to_string_pretty(&value, pretty).unwrap(); println!("Serialized: {}", serial); let deserial = ron::de::from_str(&serial); assert_eq!(Ok(value), deserial); } #[test] fn roundtrip_sep_tuple_members() { #[derive(Debug, Deserialize, PartialEq, Eq, Serialize)] pub enum FileOrMem { File(String), Memory, } #[derive(Debug, Deserialize, PartialEq, Serialize)] struct Both { a: Struct, b: FileOrMem, } let a = Struct { tuple: ((), NewType(0.5), TupleStruct(UnitStruct, -5)), vec: vec![None, Some(UnitStruct)], map: vec![ (Key(5), Enum::Unit), (Key(6), Enum::Bool(false)), (Key(7), Enum::Bool(true)), (Key(9), Enum::Chars('x', "".to_string())), ] .into_iter() .collect(), }; let b = FileOrMem::File("foo".to_owned()); let value = Both { a, b }; let pretty = ron::ser::PrettyConfig::new().separate_tuple_members(true); let serial = ron::ser::to_string_pretty(&value, pretty).unwrap(); println!("Serialized: {}", serial); let deserial = ron::de::from_str(&serial); assert_eq!(Ok(value), deserial); } ron-0.12.0/tests/struct_integers.rs000064400000000000000000000015071046102023000154410ustar 00000000000000use serde::{Deserialize, Serialize}; #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] struct S { a: i8, b: i16, c: i32, d: i64, #[cfg(feature = "integer128")] e: i128, f: u8, g: u16, h: u32, i: u64, #[cfg(feature = "integer128")] j: u128, } #[test] fn roundtrip() { let s = S { a: std::i8::MIN, b: std::i16::MIN, c: std::i32::MIN, d: std::i64::MIN, #[cfg(feature = "integer128")] e: std::i128::MIN, f: std::u8::MAX, g: std::u16::MAX, h: std::u32::MAX, i: std::u64::MAX, #[cfg(feature = "integer128")] j: std::u128::MAX, }; let serialized = ron::ser::to_string(&s).unwrap(); let deserialized = ron::de::from_str(&serialized).unwrap(); assert_eq!(s, deserialized,); } ron-0.12.0/tests/to_string_pretty.rs000064400000000000000000000011141046102023000156260ustar 00000000000000use ron::{ ser::{to_string_pretty, PrettyConfig}, to_string, }; use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Deserialize, Serialize)] struct Point { x: f64, y: f64, } #[test] fn test_struct_names() { let value = Point { x: 1.0, y: 2.0 }; let struct_name = to_string_pretty(&value, PrettyConfig::default().struct_names(true)); assert_eq!( struct_name, Ok("Point(\n x: 1.0,\n y: 2.0,\n)".to_string()) ); let no_struct_name = to_string(&value); assert_eq!(no_struct_name, Ok("(x:1.0,y:2.0)".to_string())); } ron-0.12.0/tests/unicode.rs000064400000000000000000000033371046102023000136460ustar 00000000000000use ron::{ de::{from_bytes, from_str}, error::Position, Error, Value, }; #[test] fn test_char() { let de: char = from_str("'Փ'").unwrap(); assert_eq!(de, 'Փ'); } #[test] fn test_string() { let de: String = from_str("\"My string: ऄ\"").unwrap(); assert_eq!(de, "My string: ऄ"); } #[test] fn test_char_not_a_comment() { let _ = from_str::("A('/')").unwrap(); } #[test] fn ident_starts_with_non_ascii_byte() { let _ = from_str::("שּׁȬSSSSSSSSSSR").unwrap(); } #[test] fn test_file_invalid_unicode() { let error = from_bytes::(&[b'\n', b'a', 0b11000000, 0]).unwrap_err(); assert!(matches!(error.code, Error::Utf8Error(_))); assert_eq!(error.span.end, Position { line: 2, col: 2 }); let error = from_bytes::(&[b'\n', b'\n', 0b11000000]).unwrap_err(); assert!(matches!(error.code, Error::Utf8Error(_))); assert_eq!(error.span.end, Position { line: 3, col: 1 }); } #[test] fn serialize_invalid_whitespace() { assert_eq!( ron::ser::to_string_pretty(&42, ron::ser::PrettyConfig::default().new_line("a")) .unwrap_err(), Error::Message(String::from( "Invalid non-whitespace `PrettyConfig::new_line`" )) ); assert_eq!( ron::ser::to_string_pretty(&42, ron::ser::PrettyConfig::default().indentor("a")) .unwrap_err(), Error::Message(String::from( "Invalid non-whitespace `PrettyConfig::indentor`" )) ); assert_eq!( ron::ser::to_string_pretty(&42, ron::ser::PrettyConfig::default().separator("a")) .unwrap_err(), Error::Message(String::from( "Invalid non-whitespace `PrettyConfig::separator`" )) ); } ron-0.12.0/tests/value.rs000064400000000000000000000131141046102023000133260ustar 00000000000000use std::f64; use ron::{ error::Error, value::{Map, Number, Value}, }; use serde_derive::{Deserialize, Serialize}; #[test] fn bool() { assert_eq!("true".parse(), Ok(Value::Bool(true))); assert_eq!("false".parse(), Ok(Value::Bool(false))); assert_eq!(ron::to_string(&Value::Bool(true)).unwrap(), "true"); assert_eq!(ron::to_string(&Value::Bool(false)).unwrap(), "false"); } #[test] fn char() { assert_eq!("'a'".parse(), Ok(Value::Char('a'))); assert_eq!(ron::to_string(&Value::Char('a')).unwrap(), "'a'"); } #[test] fn map() { let mut map = Map::new(); map.insert(Value::Char('a'), Value::Number(Number::U8(1))); map.insert(Value::Char('b'), Value::Number(Number::new(2f32))); let map = Value::Map(map); assert_eq!(ron::to_string(&map).unwrap(), "{'a':1,'b':2.0}"); assert_eq!("{ 'a': 1, 'b': 2.0 }".parse(), Ok(map)); } #[test] fn number() { assert_eq!("42".parse(), Ok(Value::Number(Number::U8(42)))); assert_eq!( "3.141592653589793".parse(), Ok(Value::Number(Number::new(f64::consts::PI))) ); assert_eq!( ron::to_string(&Value::Number(Number::U8(42))).unwrap(), "42" ); assert_eq!( ron::to_string(&Value::Number(Number::F64(f64::consts::PI.into()))).unwrap(), "3.141592653589793" ); } #[test] fn option() { let opt = Some(Box::new(Value::Char('c'))); assert_eq!("Some('c')".parse(), Ok(Value::Option(opt))); assert_eq!("None".parse(), Ok(Value::Option(None))); assert_eq!( ron::to_string(&Value::Option(Some(Box::new(Value::Char('c'))))).unwrap(), "Some('c')" ); assert_eq!(ron::to_string(&Value::Option(None)).unwrap(), "None"); } #[test] fn string() { let normal = "\"String\""; assert_eq!(normal.parse(), Ok(Value::String("String".into()))); assert_eq!( ron::to_string(&Value::String("String".into())).unwrap(), "\"String\"" ); let raw = "r\"Raw String\""; assert_eq!(raw.parse(), Ok(Value::String("Raw String".into()))); let raw_hashes = "r#\"Raw String\"#"; assert_eq!(raw_hashes.parse(), Ok(Value::String("Raw String".into()))); let raw_escaped = "r##\"Contains \"#\"##"; assert_eq!( raw_escaped.parse(), Ok(Value::String("Contains \"#".into())) ); let raw_multi_line = "r\"Multi\nLine\""; assert_eq!( raw_multi_line.parse(), Ok(Value::String("Multi\nLine".into())) ); assert_eq!( ron::to_string(&Value::String("Multi\nLine".into())).unwrap(), "\"Multi\\nLine\"" ); } #[test] fn byte_string() { assert_eq!( "b\"\\x01\\u{2}\\0\\x04\"".parse(), Ok(Value::Bytes(vec![1, 2, 0, 4])) ); assert_eq!( ron::to_string(&Value::Bytes(vec![1, 2, 0, 4])).unwrap(), "b\"\\x01\\x02\\x00\\x04\"" ); } #[test] fn seq() { let seq = Value::Seq(vec![ Value::Number(Number::U8(1)), Value::Number(Number::new(2f32)), ]); assert_eq!(ron::to_string(&seq).unwrap(), "[1,2.0]"); assert_eq!("[1, 2.0]".parse(), Ok(seq)); let err = Value::Seq(vec![Value::Number(Number::new(1))]) .into_rust::<[i32; 2]>() .unwrap_err(); assert_eq!( err, Error::ExpectedDifferentLength { expected: String::from("an array of length 2"), found: 1, } ); let err = Value::Seq(vec![ Value::Number(Number::new(1)), Value::Number(Number::new(2)), Value::Number(Number::new(3)), ]) .into_rust::<[i32; 2]>() .unwrap_err(); assert_eq!( err, Error::ExpectedDifferentLength { expected: String::from("a sequence of length 2"), found: 3, } ); } #[test] fn unit() { use ron::error::{Position, Span, SpannedError}; assert_eq!("()".parse(), Ok(Value::Unit)); assert_eq!("Foo".parse(), Ok(Value::Unit)); assert_eq!( "".parse::(), Err(SpannedError { code: Error::Eof, span: Span { start: Position { line: 1, col: 1 }, end: Position { col: 1, line: 1 } } }) ); assert_eq!(ron::to_string(&Value::Unit).unwrap(), "()"); } #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] struct Scene(Option<(u32, u32)>); #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] struct Scene2 { foo: Option<(u32, u32)>, } #[test] fn roundtrip() { use ron::{de::from_str, ser::to_string}; { let v = Scene2 { foo: Some((122, 13)), }; let s = to_string(&v).unwrap(); println!("{}", s); let val: Value = from_str(&s).unwrap(); println!("{:?}", val); let v2 = val.into_rust::(); assert_eq!(v2, Ok(v)); } { let v = Scene(Some((13, 122))); let s = to_string(&v).unwrap(); println!("{}", s); let val: Value = from_str(&s).unwrap(); println!("{:?}", val); let v2 = val.into_rust::(); assert_eq!(v2, Ok(v)); } { let v = (42,); let s = to_string(&v).unwrap(); println!("{}", s); let val: Value = from_str(&s).unwrap(); println!("{:?}", val); let v2 = val.into_rust::<(i32,)>(); assert_eq!(v2, Ok(v)); } } #[test] fn map_roundtrip_338() { // https://github.com/ron-rs/ron/issues/338 let v: Value = ron::from_str("{}").unwrap(); println!("{:?}", v); let ser = ron::to_string(&v).unwrap(); println!("{:?}", ser); let roundtrip = ron::from_str(&ser).unwrap(); println!("{:?}", roundtrip); assert_eq!(v, roundtrip); }