├── .cargo └── config.toml ├── .github └── workflows │ ├── audit.yml │ ├── coverage.yml │ ├── docs.yml │ └── rust.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── Reference.md ├── deny.toml ├── examples ├── alltypes.rs ├── alltypes.rsn ├── basic.rs └── basic.rsn ├── rustfmt.toml ├── src ├── de.rs ├── lib.rs ├── parser.rs ├── ser.rs ├── tests.rs ├── tests │ └── serde.rs ├── tokenizer.rs ├── tokenizer │ └── char_iterator.rs ├── value.rs └── writer.rs └── xtask ├── Cargo.toml └── src └── main.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | 2 | [alias] 3 | xtask = "run --package xtask --" -------------------------------------------------------------------------------- /.github/workflows/audit.yml: -------------------------------------------------------------------------------- 1 | name: Audit 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | audit: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Install Rust 11 | uses: hecrj/setup-rust-action@v1 12 | - name: Cache 13 | uses: actions/cache@v3 14 | with: 15 | path: | 16 | ~/.cargo/.crates.toml 17 | ~/.cargo/.crates2.json 18 | ~/.cargo/bin/cargo-deny 19 | key: cargo-deny 20 | 21 | - name: Install cargo-deny 22 | run: cargo -v install cargo-deny 23 | 24 | - name: Checkout 25 | uses: actions/checkout@v3 26 | 27 | - name: Audit 28 | run: | 29 | cargo xtask audit 30 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: Coverage 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | coverage: 7 | runs-on: ubuntu-latest 8 | timeout-minutes: 30 9 | steps: 10 | - uses: actions/checkout@v3 11 | 12 | - name: Install Rust 13 | uses: hecrj/setup-rust-action@v1 14 | 15 | - name: Run code coverage 16 | run: | 17 | cargo xtask generate-code-coverage-report --install-dependencies 18 | 19 | - name: Deploy Docs 20 | if: github.ref == 'refs/heads/main' 21 | uses: JamesIves/github-pages-deploy-action@releases/v4 22 | with: 23 | branch: gh-pages 24 | folder: coverage/ 25 | git-config-name: kl-botsu 26 | git-config-email: botsu@khonsulabs.com 27 | target-folder: /coverage/ 28 | clean: true 29 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | docs: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Install Rust 10 | uses: hecrj/setup-rust-action@v1 11 | with: 12 | rust-version: nightly 13 | 14 | - uses: actions/checkout@v3 15 | - name: Generate Docs 16 | run: | 17 | cargo +nightly doc --no-deps --all-features 18 | 19 | - name: Deploy Docs 20 | if: github.ref == 'refs/heads/main' 21 | uses: JamesIves/github-pages-deploy-action@releases/v4 22 | with: 23 | branch: gh-pages 24 | folder: target/doc/ 25 | git-config-name: kl-botsu 26 | git-config-email: botsu@khonsulabs.com 27 | target-folder: /main/ 28 | clean: true 29 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | timeout-minutes: 30 9 | steps: 10 | - uses: actions/checkout@v3 11 | 12 | - name: Install Rust 13 | uses: hecrj/setup-rust-action@v1 14 | 15 | - name: Run clippy (no features) 16 | run: | 17 | cargo clippy --no-default-features 18 | 19 | - name: Run clippy (all features) 20 | run: | 21 | cargo clippy --features std,serde,integer128 22 | 23 | - name: Run clippy (std,serde) 24 | run: | 25 | cargo clippy --features std,serde 26 | 27 | - name: Run clippy (serde) 28 | run: | 29 | cargo clippy --no-default-features --features serde 30 | 31 | - name: Run clippy (std) 32 | run: | 33 | cargo clippy --no-default-features --features std 34 | 35 | - name: Run clippy (integer128) 36 | run: | 37 | cargo clippy --no-default-features --features integer128 38 | 39 | - name: Run unit tests 40 | run: | 41 | cargo test --all-targets --features std,serde 42 | 43 | - name: Run unit tests (integer128) 44 | run: | 45 | cargo test --all-targets --features std,serde,integer128 46 | 47 | build-msrv: 48 | name: Test on MSRV 49 | runs-on: ubuntu-latest 50 | steps: 51 | - uses: actions/checkout@v3 52 | - name: Install Rust 53 | uses: hecrj/setup-rust-action@v1 54 | with: 55 | rust-version: 1.65 56 | - name: Run unit tests 57 | run: cargo test --all-targets --features std,serde,integer128 58 | 59 | build-nostd: 60 | name: Build on no_std target (thumbv7em-none-eabi) 61 | runs-on: ubuntu-latest 62 | steps: 63 | - uses: actions/checkout@v3 64 | - uses: dtolnay/rust-toolchain@master 65 | with: 66 | toolchain: stable 67 | targets: thumbv7em-none-eabi 68 | - run: cargo build --target thumbv7em-none-eabi --lib --release --no-default-features 69 | 70 | build-serde: 71 | name: Build on no_std target (thumbv7em-none-eabi) 72 | runs-on: ubuntu-latest 73 | steps: 74 | - uses: actions/checkout@v3 75 | - uses: dtolnay/rust-toolchain@master 76 | with: 77 | toolchain: stable 78 | targets: thumbv7em-none-eabi 79 | - run: cargo build --target thumbv7em-none-eabi --lib --release --no-default-features --features serde,integer128 80 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | coverage/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | 9 | 10 | ## v0.2.0 11 | 12 | ### Breaking CHanges 13 | 14 | - `parser::Config::allow_implicit_map` has been renamed to 15 | `allow_implicit_map_at_root`. 16 | - These types are now marked as `#[non_exhaustive]`: 17 | - `parser::Config` 18 | - `ser::Config` 19 | - `writer::Config` 20 | 21 | ### Fixes 22 | 23 | - Raw strings and byte strings without any `#`s can now be used. E.g., `r"\"` 24 | - Implicit map support now supports serializing and deserializing any map-like 25 | type. 26 | 27 | ### Added 28 | 29 | - When the new flag `ser::Config::anonymous_structs` is enabled, structures will 30 | be written without their name. 31 | 32 | 33 | ## v0.1.0 34 | 35 | Initial release. -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rsn" 3 | description = "A Rust-inspired, human-readable object notation." 4 | version = "0.2.0" 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | repository = "https://github.com/khonsulabs/rsn" 8 | readme = "./README.md" 9 | rust-version = "1.65" 10 | categories = ["no-std", "parser-implementations", "encoding"] 11 | keywords = ["serde", "parser", "serialization"] 12 | 13 | [features] 14 | default = ["serde", "std"] 15 | integer128 = [] 16 | std = ["serde/std"] 17 | serde = ["dep:serde"] 18 | nightly = [] # Enables unstable documentation features outside of docs.rs 19 | 20 | [dependencies] 21 | serde = { version = "1.0.159", optional = true, default-features = false, features = [ 22 | "alloc", 23 | ] } 24 | unicode-ident = "1.0.8" 25 | 26 | [[example]] 27 | name = "basic" 28 | required-features = ["serde"] 29 | 30 | [dev-dependencies] 31 | serde = { version = "1.0.159", features = ["derive"] } 32 | serde_bytes = { version = "0.11.9" } 33 | 34 | [package.metadata.docs.rs] 35 | rustdoc-args = ["--cfg", "docsrs"] 36 | features = ["std,serde"] 37 | 38 | [workspace] 39 | members = ["xtask"] 40 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2021 Khonsu Labs LLC 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright 2021 Khonsu Labs LLC 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rsn - Rusty Notation 2 | 3 | **This crate is very early in development. Please report any issues [on our 4 | GitHub](https://github.com/khonsulabs/rsn).** 5 | 6 | ![rsn forbids unsafe code](https://img.shields.io/badge/unsafe-forbid-success) 7 | ![rsn is considered alpha](https://img.shields.io/badge/status-alpha-orange) 8 | [![crate version](https://img.shields.io/crates/v/rsn.svg)](https://crates.io/crates/rsn) 9 | [![Live Build Status](https://img.shields.io/github/actions/workflow/status/khonsulabs/rsn/rust.yml?branch=main)](https://github.com/khonsulabs/rsn/actions?query=workflow:Tests) 10 | [![HTML Coverage Report for `main`](https://khonsulabs.github.io/rsn/coverage/badge.svg)](https://khonsulabs.github.io/rsn/coverage/) 11 | [![Documentation for `main`](https://img.shields.io/badge/docs-main-informational)](https://khonsulabs.github.io/rsn/main/rsn/) 12 | 13 | A UTF-8 based text format that looks very similar to valid Rust code. This format adheres closely to [Rust's lexical rules][rust-lexer] 14 | 15 | ## `no_std` support 16 | 17 | This crate supports `no_std` targets that support the `alloc` crate. 18 | 19 | ## Data Types 20 | 21 | ```rsn 22 | ExampleStruct { 23 | integers: [42, 0xFF, 0o77, 0b101], 24 | floats: [42., 3.14, 1e10], 25 | bools: [true, false], 26 | chars: ['a', '\''], 27 | string: "Hello, World!", 28 | raw_string: r#"I said, "Hello, World!""#, 29 | bytes: [b'a', b'\''], 30 | byte_string: b"Hello, World!", 31 | raw_byte_string: br#"I said, "Hello, World!""#, 32 | named_map: StructLike { 33 | field: 42, 34 | }, 35 | named_tuple: TupleLike(42), 36 | r#raw_identifiers: true, 37 | array: [1, 2, 3], 38 | tuple: (1, 2, 3), 39 | map: { 40 | "a": 1, 41 | "b": 2, 42 | }, 43 | } 44 | ``` 45 | 46 | - Integers (`42`, `0xFF`, `0o77`, `0b101`) 47 | - Floats (`42.`, `3.14`) 48 | - Bool (`true`, `false`) 49 | - Character (`'a'`, `'\''`) 50 | - Byte (`b'a'`, `b'\''`) 51 | - String (`"hello, world"`) 52 | - Raw Strings (`r#"They said, "Hello World!""#`) 53 | - Byte Strings (`b"hello, world"`) 54 | - Named 55 | - Ident or Raw Ident (`r#foo`) 56 | - Map or Tuple 57 | - Map 58 | - List of `: ` pairs, delimited by comma 59 | - Trailing comma is optional 60 | - Tuple (empty tuple = Unit) 61 | - List of ``s, delimited by comma 62 | - Trailing comma is optional 63 | - Array 64 | - List of ``s, delimited by comma 65 | - Trailing comma is optional 66 | - Comments `//` and `/* */` 67 | 68 | - Potential Extensions via #[] syntax 69 | - Semi-strict comma-delimited list 70 | - `#[foo(...), bar = ...,]` 71 | - All braces/brackets/parens must be paired correctly? 72 | 73 | ## Other related projects 74 | 75 | - [`rsn-fmt`](https://github.com/ModProg/rsn-fmt): A formatter project for `rsn`. 76 | - [`rsn.vim`](https://github.com/ModProg/rsn.vim): A plugin for Vim/NeoVim. 77 | 78 | ## Why not Ron? 79 | 80 | [Ron](https://crates.io/crates/ron) is a great format. There were a few design 81 | decisions that led to this very-similar-yet-not-the-same format being invented: 82 | 83 | - `ron` differentiates between Tuples and Lists, while `rsn` treats all 84 | sequences the same. 85 | - `ron` uses a different syntax for structures and maps. `rsn` uses the same 86 | syntax for both concepts. 87 | - `ron` has special support for `Option`. `rsn` treats `Option` like any 88 | other enum. 89 | - `ron`'s parsing rules are close but not the same as Rust, while `rsn` attempts 90 | to match implementations: 91 | - Unicode white space and idents (added in 92 | [ron-rs/ron#444](https://github.com/ron-rs/ron/pull/444)) 93 | - Rust allows `_` in float literals 94 | - Rust allows for raw line endings to be escaped in string literals. 95 | - Rust supports byte strings and byte literals, while Ron elected to use 96 | `base64` encoded strings for byte values. 97 | 98 | ## Differences between Rust syntax and Rsn 99 | 100 | The syntax differs from valid Rust code for: 101 | 102 | - Map literals. Rust has no syntax for map literals. 103 | - Enum Variants being used without the type name -- `Red` vs `Color::Red` 104 | - This is technically valid Rust syntax if `use Color::*` is present. 105 | - Infinity and Not-A-Number floats are represented as 106 | `+inf`/`-inf`/`+NaN`/`-NaN`. 107 | - For compatibility with Rust syntax, support for 108 | [`f64::INFINITY`](https://github.com/khonsulabs/rsn/issues/3) is being 109 | considered. 110 | 111 | The rules for parsing literals should match Rust's rules as closely as possible. 112 | 113 | [rust-lexer]: https://doc.rust-lang.org/reference/lexical-structure.html 114 | -------------------------------------------------------------------------------- /Reference.md: -------------------------------------------------------------------------------- 1 | # `rsn` Syntax Reference 2 | 3 | This document aims to describe the accepted syntax for parsing `rsn`. Currently, it is more of a TODO list than actual documentation. 4 | 5 | An `rsn` payload contains a single `Value`, which can be one of these types: 6 | 7 | - [Integer](#integer): `123`; `-123_456`; `0x0123_aBc`; `0o123_777`; 8 | `0b1010_1111` 9 | - [Float](#float): `1.`; `-2_000.123_456`; `1e-2` 10 | - [Boolean](#boolean): `true`; `false` 11 | - [Character](#character): `'a'`; `'\''` 12 | - [Byte](#byte): `b'a'`; `b'\''` 13 | - [String](#string): `"hello, world"`; `r#"raw "strings""#` 14 | - [Byte String](#byte-string): `b"hello, world"`; `br#"raw "strings""#` 15 | - [Map](#map): `{key: "value"}`; `{a: 1, b: true,}` 16 | - [List](#list): `[1, 2, 3]`; `["a", "b",]` 17 | - [Tuple](#tuple): `(1, false)`; `(2, true,)` 18 | - [Identified](#identified): `Name`; `Name { a: 1 }`; `Name(1)` 19 | 20 | ## Integer 21 | 22 | Just like in Rust, integers can be represented in four different 23 | representations: decimal (base 10), binary (base 2), octal (base 8), and 24 | hexadecimal (base 16). Integer parsing ignores underscores (`_`) to allow large 25 | literals to be grouped however the user prefers. 26 | 27 | If an integer has no explicit sign, it will be parsed as a `usize`. If the value 28 | overflows, it will be promoted to the "large integer size". If the `integer128` 29 | feature is enabled, the "large integer size" is a `u128`. If the feature is not 30 | enabled, the "large integer size" is a `u64`. 31 | 32 | If either a `+` or `-` sign is present, the integer will be parsed as an `isize` 33 | with the appropriate sign applied. If the value overflows, it will be promoted 34 | to the "large integer size". If the `integer128` feature is enabled, the "large 35 | integer size" is a `i128`. If the feature is not enabled, the "large integer 36 | size" is a `i64`. 37 | 38 | ### Syntax 39 | 40 | Integer values always begin with a digit (`0-9`) or a sign (`+`/`-`). 41 | 42 | 1. If a sign (`+` or `-`) is encountered, the literal is parsed as a signed 43 | number. 44 | 2. At least one digit (`0-9`) must be present. 45 | 1. If the first digit is a `0` and it is followed by an `x` or 'X', parse the 46 | remainder of this literal as a hexadecimal number. 47 | 2. If the first digit is a `0` and it is followed by a `b` or 'B', parse the 48 | remainder of this literal as a binary number. 49 | 3. If the first digit is a `0` and it is followed by an `o` or 'O', parse the 50 | remainder of this literal as an octal number. 51 | 3. Continue reading digits (`0-9`) or underscores (`_`) until a non-matching 52 | character is encountered. 53 | 4. If the first non-matching character is either a `.`, `e` or `E`, switch to 54 | parsing this numerical value as a [float](#float). 55 | 56 | #### Hexadecimal Syntax 57 | 58 | Hexadecimal values are parsed after encountering `0x` while parsing an 59 | [integer](#integer). After this prefix, parsing is done by reading hexadecimal 60 | digits (`0-9`, `a-f`, `A-F`) or underscores (`_`) until a non-matching character 61 | is encountered. 62 | 63 | #### Octal Syntax 64 | 65 | Octal values are parsed after encountering `0o` while parsing an 66 | [integer](#integer). After this prefix, parsing is done by reading octal digits 67 | (`0-7`) or underscores (`_`) until a non-matching character is encountered. 68 | 69 | #### Binary Syntax 70 | 71 | Binary values are parsed after encountering `0b` while parsing an 72 | [integer](#integer). After this prefix, parsing is done by reading binary digits 73 | (`0` or `1`) or underscores (`_`) until a non-matching character is encountered. 74 | 75 | ## Float 76 | 77 | - [x] Tokenizer support 78 | - [x] `inf`/`NaN` support 79 | - [x] Parser support 80 | - [ ] Deserializer Support 81 | - [ ] Documentation 82 | - [ ] Grammar Spec 83 | 84 | ## Boolean 85 | 86 | - [x] Tokenizer support 87 | - [x] Parser support 88 | - [x] Deserializer Support 89 | - [ ] Documentation 90 | 91 | ## Character 92 | 93 | - [x] Tokenizer support 94 | - [x] Parser support 95 | - [x] Deserializer Support 96 | - [ ] Documentation 97 | 98 | ## Byte 99 | 100 | - [x] Tokenizer support 101 | - [x] Parser support 102 | - [x] Deserializer Support 103 | - [ ] Documentation 104 | 105 | ## String 106 | 107 | - [x] Tokenizer support 108 | - [x] Support same whitespace rules on raw line ending escaping. 109 | - [ ] Error-by-default on multiple line ending removal with raw line ending 110 | escaping, just like rustc, but allow a parsing option that prevents the 111 | errors. 112 | - [x] Parser support 113 | - [x] Deserializer Support 114 | - [ ] Documentation 115 | 116 | ## Byte String 117 | 118 | - [x] Tokenizer support 119 | - [ ] `b64` prefixed base64-encoded byte strings 120 | - [x] Parser support 121 | - [x] Deserializer Support 122 | - [ ] Documentation 123 | 124 | ## Map 125 | 126 | - [x] Tokenizer support 127 | - [x] Parser support 128 | - [x] Deserializer Support 129 | - [ ] Documentation 130 | 131 | ## List 132 | 133 | - [x] Tokenizer support 134 | - [x] Parser support 135 | - [x] Deserializer Support 136 | - [ ] Documentation 137 | 138 | ## Tuple 139 | 140 | - [x] Tokenizer support 141 | - [x] Parser support 142 | - [x] Deserializer Support 143 | - [ ] Documentation 144 | 145 | ## Identified 146 | 147 | - [x] Tokenizer support 148 | - [x] Parser support 149 | - [x] Deserializer Support 150 | - [ ] Documentation 151 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | [graph] 2 | targets = [] 3 | 4 | [advisories] 5 | db-path = "~/.cargo/advisory-db" 6 | db-urls = ["https://github.com/rustsec/advisory-db"] 7 | yanked = "warn" 8 | ignore = [] 9 | 10 | [licenses] 11 | allow = ["MIT", "Apache-2.0", "Unicode-DFS-2016"] 12 | confidence-threshold = 0.8 13 | exceptions = [] 14 | 15 | [[licenses.clarify]] 16 | name = "ring" 17 | version = "*" 18 | expression = "MIT AND ISC AND OpenSSL" 19 | license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] 20 | 21 | [licenses.private] 22 | ignore = true 23 | 24 | [bans] 25 | multiple-versions = "warn" 26 | wildcards = "allow" 27 | highlight = "all" 28 | -------------------------------------------------------------------------------- /examples/alltypes.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use serde_bytes::ByteBuf; 5 | 6 | #[derive(Serialize, Deserialize, Debug)] 7 | struct ExampleStruct { 8 | integers: Vec, 9 | floats: Vec, 10 | bools: Vec, 11 | chars: Vec, 12 | string: String, 13 | raw_string: String, 14 | bytes: Vec, 15 | byte_string: ByteBuf, 16 | raw_byte_string: ByteBuf, 17 | named_map: NamedExample, 18 | named_tuple: NamedExample, 19 | r#raw_identifiers: bool, 20 | array: Vec, 21 | tuple: Vec, 22 | map: HashMap, 23 | } 24 | 25 | #[derive(Serialize, Deserialize, Debug)] 26 | enum NamedExample { 27 | StructLike { field: usize }, 28 | TupleLike(usize), 29 | } 30 | 31 | fn main() { 32 | let example: ExampleStruct = 33 | rsn::from_str(include_str!("./alltypes.rsn")).expect("error deserializing alltypes.rsn"); 34 | 35 | println!("Loaded blog posts: {example:?}"); 36 | } 37 | 38 | #[test] 39 | fn runs() { 40 | main(); 41 | } 42 | -------------------------------------------------------------------------------- /examples/alltypes.rsn: -------------------------------------------------------------------------------- 1 | ExampleStruct { 2 | integers: [42, 0xFF, 0o77, 0b101], 3 | floats: [42., 3.14, 1e10], 4 | bools: [true, false], 5 | chars: ['a', '\''], 6 | string: "Hello, World!", 7 | raw_string: r#"I said, "Hello, World!""#, 8 | bytes: [b'a', b'\''], 9 | byte_string: b"Hello, World!", 10 | raw_byte_string: br#"I said, "Hello, World!""#, 11 | named_map: StructLike { 12 | field: 42, 13 | }, 14 | named_tuple: TupleLike(42), 15 | r#raw_identifiers: true, 16 | array: [1, 2, 3], 17 | tuple: (1, 2, 3), 18 | map: { 19 | "a": 1, 20 | "b": 2, 21 | }, 22 | } -------------------------------------------------------------------------------- /examples/basic.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Serialize, Deserialize)] 4 | struct BlogPost { 5 | id: u64, 6 | title: String, 7 | body: String, 8 | #[serde(default)] 9 | previous_in_series: Option, 10 | category: Category, 11 | } 12 | 13 | #[derive(Debug, Serialize, Deserialize)] 14 | enum Category { 15 | Rust, 16 | Custom(String), 17 | } 18 | 19 | fn main() { 20 | let posts: Vec = 21 | rsn::from_str(include_str!("./basic.rsn")).expect("valid rsn in basic.rsn"); 22 | 23 | println!("Loaded blog posts: {posts:?}"); 24 | 25 | let compact = rsn::to_string(&posts).expect("no errors"); 26 | println!("Compact form:\n{compact}"); 27 | let pretty = rsn::to_string_pretty(&posts).expect("no errors"); 28 | println!("Pretty form:\n{pretty}"); 29 | } 30 | 31 | #[test] 32 | fn runs() { 33 | main(); 34 | } 35 | -------------------------------------------------------------------------------- /examples/basic.rsn: -------------------------------------------------------------------------------- 1 | [ 2 | BlogPost { 3 | id: 0, 4 | title: "Hello, World!", 5 | body: "This is a blog post.", 6 | category: Custom("Welcome"), 7 | }, 8 | BlogPost { 9 | id: 1, 10 | title: "I love Rust", 11 | body: "Long live Ferris 🦀", 12 | category: Rust, 13 | previous_in_series: Some(0) 14 | }, 15 | ] -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | unstable_features = true 2 | use_field_init_shorthand = true 3 | imports_granularity = "Module" 4 | group_imports = "StdExternalCrate" 5 | format_code_in_doc_comments = true 6 | reorder_impl_items = true 7 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![doc = include_str!("../README.md")] 3 | #![warn(clippy::pedantic)] 4 | #![warn(missing_docs)] 5 | #![allow(clippy::module_name_repetitions)] 6 | 7 | extern crate alloc; 8 | 9 | #[cfg(feature = "std")] 10 | extern crate std; 11 | 12 | /// Serde deserialization support. 13 | #[cfg(feature = "serde")] 14 | pub mod de; 15 | /// Parse data or a reader into a sequence of Rsn events. 16 | pub mod parser; 17 | /// Serde serialization support. 18 | #[cfg(feature = "serde")] 19 | pub mod ser; 20 | /// Parse data or a reader into a sequence of tokens. 21 | pub mod tokenizer; 22 | /// Types for generically representing the parsed value from an Rsn document. 23 | pub mod value; 24 | /// Types for writing Rsn. 25 | pub mod writer; 26 | 27 | /// Deserializes `D` from `source` using the default Rsn 28 | /// [`Config`](parser::Config). 29 | /// 30 | /// ```rust 31 | /// let deserialized: Vec = rsn::from_str("[1, 2, 3]").unwrap(); 32 | /// assert_eq!(deserialized, vec![1, 2, 3]); 33 | /// ``` 34 | /// 35 | /// # Errors 36 | /// 37 | /// Returns an error if `source` isn't valid Rsn or cannot be deserialized as 38 | /// `D`. 39 | #[cfg(feature = "serde")] 40 | pub fn from_str<'de, D: serde::Deserialize<'de>>(source: &'de str) -> Result { 41 | parser::Config::default().deserialize(source) 42 | } 43 | 44 | /// Deserializes `D` from `slice` using the default Rsn 45 | /// [`Config`](parser::Config). 46 | /// 47 | /// ```rust 48 | /// let deserialized: Vec = rsn::from_slice(b"[1, 2, 3]").unwrap(); 49 | /// assert_eq!(deserialized, vec![1, 2, 3]); 50 | /// ``` 51 | /// 52 | /// # Errors 53 | /// 54 | /// Returns an error if `slice` isn't valid Rsn or cannot be deserialized as 55 | /// `D`. 56 | #[cfg(feature = "serde")] 57 | pub fn from_slice<'de, D: serde::Deserialize<'de>>(source: &'de [u8]) -> Result { 58 | parser::Config::default().deserialize_from_slice(source) 59 | } 60 | 61 | /// Deserializes `D` from `reader` using the default Rsn 62 | /// [`Config`](parser::Config). 63 | /// 64 | /// ```rust 65 | /// let deserialized: Vec = rsn::from_reader(&b"[1, 2, 3]"[..]).unwrap(); 66 | /// assert_eq!(deserialized, vec![1, 2, 3]); 67 | /// ``` 68 | /// 69 | /// # Errors 70 | /// 71 | /// Returns an error if `reader` returns an error while reading, doesn't contain 72 | /// valid Rsn, or cannot be deserialized as `D`. 73 | #[cfg(all(feature = "serde", feature = "std"))] 74 | pub fn from_reader( 75 | reader: R, 76 | ) -> Result { 77 | parser::Config::default().deserialize_from_reader(reader) 78 | } 79 | 80 | /// Serializes `value` into a `String` using the default Rsn 81 | /// [`Config`](ser::Config). 82 | /// 83 | /// ```rust 84 | /// let serialized = rsn::to_string(&vec![1, 2, 3]).unwrap(); 85 | /// assert_eq!(serialized, "[1,2,3]"); 86 | /// ``` 87 | /// 88 | /// # Errors 89 | /// 90 | /// Rsn itself does not produce any errors while serializing values. This 91 | /// function will return errors that arise within `Serialize` implementations 92 | /// encountered while serializing `value`. 93 | #[cfg(feature = "serde")] 94 | pub fn to_string( 95 | value: &S, 96 | ) -> Result { 97 | ser::Config::default().serialize(value) 98 | } 99 | 100 | /// Serializes `value` into a `Vec` using the default Rsn 101 | /// [`Config`](ser::Config). 102 | /// 103 | /// ```rust 104 | /// let serialized = rsn::to_vec(&vec![1, 2, 3]).unwrap(); 105 | /// assert_eq!(serialized, b"[1,2,3]"); 106 | /// ``` 107 | /// 108 | /// # Errors 109 | /// 110 | /// Rsn itself does not produce any errors while serializing values. This 111 | /// function will return errors that arise within `Serialize` implementations 112 | /// encountered while serializing `value`. 113 | #[cfg(feature = "serde")] 114 | pub fn to_vec(value: &S) -> Result, core::fmt::Error> { 115 | ser::Config::default().serialize_to_vec(value) 116 | } 117 | 118 | /// Serializes `value` into a writer using the default Rsn 119 | /// [`Config`](ser::Config). 120 | /// 121 | /// ```rust 122 | /// let mut serialized = Vec::new(); 123 | /// rsn::to_writer(&vec![1, 2, 3], &mut serialized).unwrap(); 124 | /// assert_eq!(serialized, b"[1,2,3]"); 125 | /// ``` 126 | /// 127 | /// # Errors 128 | /// 129 | /// Returns any errors occurring while serializing `value` or while writing to 130 | /// `writer`. 131 | #[cfg(all(feature = "serde", feature = "std"))] 132 | pub fn to_writer( 133 | value: &S, 134 | writer: W, 135 | ) -> std::io::Result { 136 | ser::Config::default().serialize_to_writer(value, writer) 137 | } 138 | 139 | /// Serializes `value` into a `String` using 140 | /// [`Config::pretty()`](ser::Config::pretty()). 141 | /// 142 | /// ```rust 143 | /// let input = vec![1, 2, 3]; 144 | /// let serialized = rsn::to_string_pretty(&input).unwrap(); 145 | /// assert_eq!(serialized, "[\n 1,\n 2,\n 3\n]"); 146 | /// ``` 147 | /// 148 | /// # Errors 149 | /// 150 | /// Rsn itself does not produce any errors while serializing values. This 151 | /// function will return errors that arise within `Serialize` implementations 152 | /// encountered while serializing `value`. 153 | #[cfg(feature = "serde")] 154 | pub fn to_string_pretty( 155 | value: &S, 156 | ) -> Result { 157 | ser::Config::pretty().serialize(value) 158 | } 159 | 160 | #[cfg(test)] 161 | mod tests; 162 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use alloc::borrow::Cow; 2 | use alloc::vec::Vec; 3 | use core::fmt::{Display, Formatter}; 4 | use core::mem; 5 | use core::ops::{Deref, Range}; 6 | 7 | use crate::tokenizer::{self, Balanced, Integer, Token, TokenKind, Tokenizer}; 8 | 9 | /// Parses input as a sequence of [`Event`]s. 10 | #[derive(Debug)] 11 | pub struct Parser<'s> { 12 | tokens: Tokenizer<'s, false>, 13 | peeked: Option, tokenizer::Error>>, 14 | nested: Vec<(usize, NestedState)>, 15 | root_state: State<'s>, 16 | config: Config, 17 | } 18 | 19 | impl<'s> Parser<'s> { 20 | /// Returns a parser that parses `source` using `configuration`. 21 | #[must_use] 22 | pub fn new(source: &'s str, configuration: Config) -> Self { 23 | Self { 24 | tokens: Tokenizer::minified(source), 25 | peeked: None, 26 | nested: Vec::new(), 27 | root_state: State::AtStart, 28 | config: configuration, 29 | } 30 | } 31 | 32 | /// Validates that `source` would parse successfully using `configuration`. 33 | #[must_use] 34 | pub fn validate(source: &'s str, configuration: Config) -> bool { 35 | Self::new(source, configuration).all(|result| result.is_ok()) 36 | } 37 | 38 | /// Returns the current byte offset of the parser. 39 | #[must_use] 40 | pub const fn current_offset(&self) -> usize { 41 | self.tokens.current_offset() 42 | } 43 | 44 | /// Returns the range between the start of the containing nested event and 45 | /// the current byte offset of the parser. 46 | #[must_use] 47 | pub fn current_range(&self) -> Range { 48 | let start = self.nested.last().map_or(0, |(offset, _)| *offset); 49 | start..self.tokens.current_offset() 50 | } 51 | 52 | fn peek(&mut self) -> Option<&Token<'s>> { 53 | if self.peeked.is_none() { 54 | self.peeked = self.tokens.next(); 55 | } 56 | 57 | self.peeked.as_ref().and_then(|r| r.as_ref().ok()) 58 | } 59 | 60 | fn next_token(&mut self) -> Option, tokenizer::Error>> { 61 | self.peeked.take().or_else(|| self.tokens.next()) 62 | } 63 | 64 | fn next_token_parts( 65 | &mut self, 66 | ) -> Result<(Range, Option>), tokenizer::Error> { 67 | Ok(match self.next_token().transpose()? { 68 | Some(token) => (token.location, Some(token.kind)), 69 | None => ( 70 | self.tokens.current_offset()..self.tokens.current_offset(), 71 | None, 72 | ), 73 | }) 74 | } 75 | 76 | fn next_or_eof(&mut self) -> Result, Error> { 77 | match self.next_token() { 78 | Some(Ok(token)) => Ok(token), 79 | Some(Err(err)) => Err(err.into()), 80 | None => Err(Error::new( 81 | self.tokens.current_offset()..self.tokens.current_offset(), 82 | ErrorKind::UnexpectedEof, 83 | )), 84 | } 85 | } 86 | 87 | fn parse_token( 88 | &mut self, 89 | token: Token<'s>, 90 | allowed_close: Option, 91 | ) -> Result, Error> { 92 | match token.kind { 93 | TokenKind::Integer(integer) => Ok(Event::new( 94 | token.location, 95 | EventKind::Primitive(Primitive::Integer(integer)), 96 | )), 97 | TokenKind::Float(float) => Ok(Event::new( 98 | token.location, 99 | EventKind::Primitive(Primitive::Float(float)), 100 | )), 101 | TokenKind::Bool(value) => Ok(Event::new( 102 | token.location, 103 | EventKind::Primitive(Primitive::Bool(value)), 104 | )), 105 | TokenKind::Character(value) => Ok(Event::new( 106 | token.location, 107 | EventKind::Primitive(Primitive::Char(value)), 108 | )), 109 | TokenKind::Byte(value) => Ok(Event::new( 110 | token.location, 111 | EventKind::Primitive(Primitive::Integer(Integer::Usize(value as usize))), 112 | )), 113 | TokenKind::String(value) => Ok(Event::new( 114 | token.location, 115 | EventKind::Primitive(Primitive::String(value)), 116 | )), 117 | TokenKind::Bytes(value) => Ok(Event::new( 118 | token.location, 119 | EventKind::Primitive(Primitive::Bytes(value)), 120 | )), 121 | TokenKind::Identifier(value) => self.parse_identifier(token, value), 122 | TokenKind::Open(Balanced::Paren) => { 123 | self.nested.push(( 124 | token.location.start, 125 | NestedState::Tuple(ListStateExpecting::Value), 126 | )); 127 | Ok(Event::new( 128 | token.location, 129 | EventKind::BeginNested { 130 | name: None, 131 | kind: Nested::Tuple, 132 | }, 133 | )) 134 | } 135 | TokenKind::Open(Balanced::Bracket) => { 136 | self.nested.push(( 137 | token.location.start, 138 | NestedState::List(ListStateExpecting::Value), 139 | )); 140 | Ok(Event::new( 141 | token.location, 142 | EventKind::BeginNested { 143 | name: None, 144 | kind: Nested::List, 145 | }, 146 | )) 147 | } 148 | TokenKind::Open(Balanced::Brace) => { 149 | self.nested.push(( 150 | token.location.start, 151 | NestedState::Map(MapStateExpecting::Key), 152 | )); 153 | Ok(Event::new( 154 | token.location, 155 | EventKind::BeginNested { 156 | name: None, 157 | kind: Nested::Map, 158 | }, 159 | )) 160 | } 161 | TokenKind::Close(closed) if Some(closed) == allowed_close => { 162 | self.nested.pop(); 163 | Ok(Event::new(token.location, EventKind::EndNested)) 164 | } 165 | TokenKind::Colon | TokenKind::Comma | TokenKind::Close(_) => { 166 | Err(Error::new(token.location, ErrorKind::ExpectedValue)) 167 | } 168 | TokenKind::Comment(comment) => { 169 | Ok(Event::new(token.location, EventKind::Comment(comment))) 170 | } 171 | TokenKind::Whitespace(_) => unreachable!("disabled"), 172 | } 173 | } 174 | 175 | fn parse_identifier(&mut self, token: Token<'s>, value: &'s str) -> Result, Error> { 176 | if matches!( 177 | self.peek(), 178 | Some(Token { 179 | kind: TokenKind::Open(Balanced::Brace | Balanced::Paren), 180 | .. 181 | }) 182 | ) { 183 | let Some(Ok(Token { 184 | kind: TokenKind::Open(balanced), 185 | location: open_location, 186 | })) = self.next_token() 187 | else { 188 | unreachable!("matched above") 189 | }; 190 | 191 | let kind = match balanced { 192 | Balanced::Paren => { 193 | self.nested.push(( 194 | open_location.start, 195 | NestedState::Tuple(ListStateExpecting::Value), 196 | )); 197 | Nested::Tuple 198 | } 199 | Balanced::Brace => { 200 | self.nested.push(( 201 | open_location.start, 202 | NestedState::Map(MapStateExpecting::Key), 203 | )); 204 | Nested::Map 205 | } 206 | Balanced::Bracket => { 207 | unreachable!("specifically excluded above") 208 | } 209 | }; 210 | 211 | Ok(Event::new( 212 | open_location, 213 | EventKind::BeginNested { 214 | name: Some(Name { 215 | location: token.location, 216 | name: value, 217 | }), 218 | kind, 219 | }, 220 | )) 221 | } else if matches!( 222 | self.peek(), 223 | Some(Token { 224 | kind: TokenKind::Open(Balanced::Bracket), 225 | .. 226 | }) 227 | ) { 228 | let location = self.peek().expect("just matched").location.clone(); 229 | return Err(Error::new(location, ErrorKind::ExpectedMapOrTuple)); 230 | } else { 231 | Ok(Event::new( 232 | token.location, 233 | EventKind::Primitive(Primitive::Identifier(value)), 234 | )) 235 | } 236 | } 237 | 238 | fn parse_sequence( 239 | &mut self, 240 | state: ListStateExpecting, 241 | end: Balanced, 242 | ) -> Result, Error> { 243 | match state { 244 | ListStateExpecting::Value => { 245 | let token = self.next_or_eof()?; 246 | if let TokenKind::Comment(comment) = &token.kind { 247 | Ok(Event::new(token.location, EventKind::Comment(comment))) 248 | } else { 249 | self.nested.last_mut().expect("required for this fn").1 = 250 | NestedState::list(end, ListStateExpecting::Comma); 251 | self.parse_token(token, Some(end)) 252 | } 253 | } 254 | ListStateExpecting::Comma => match self.next_token_parts()? { 255 | (location, Some(TokenKind::Close(closed))) if closed == end => { 256 | self.nested.pop(); 257 | Ok(Event::new(location, EventKind::EndNested)) 258 | } 259 | (_, Some(TokenKind::Comma)) => { 260 | self.nested.last_mut().expect("required for this fn").1 = 261 | NestedState::list(end, ListStateExpecting::Value); 262 | self.parse_sequence(ListStateExpecting::Value, end) 263 | } 264 | (location, Some(TokenKind::Comment(comment))) => { 265 | Ok(Event::new(location, EventKind::Comment(comment))) 266 | } 267 | (location, _) => Err(Error::new( 268 | location, 269 | ErrorKind::ExpectedCommaOrEnd(end.into()), 270 | )), 271 | }, 272 | } 273 | } 274 | 275 | fn map_state_mut(&mut self) -> &mut MapStateExpecting { 276 | let Some((_, NestedState::Map(map_state))) = self.nested.last_mut() else { 277 | unreachable!("not a map state") 278 | }; 279 | map_state 280 | } 281 | 282 | fn parse_map(&mut self, state: MapStateExpecting) -> Result, Error> { 283 | match state { 284 | MapStateExpecting::Key => match self.next_token().transpose()? { 285 | Some(Token { 286 | kind: TokenKind::Comment(comment), 287 | location, 288 | }) => Ok(Event::new(location, EventKind::Comment(comment))), 289 | Some(token) => { 290 | *self.map_state_mut() = MapStateExpecting::Colon; 291 | self.parse_token(token, Some(Balanced::Brace)) 292 | } 293 | None => Err(Error::new( 294 | self.tokens.current_offset()..self.tokens.current_offset(), 295 | ErrorKind::ExpectedKey, 296 | )), 297 | }, 298 | MapStateExpecting::Colon => match self.next_token_parts()? { 299 | (_, Some(TokenKind::Colon)) => { 300 | *self.map_state_mut() = MapStateExpecting::Value; 301 | self.parse_map(MapStateExpecting::Value) 302 | } 303 | (location, Some(TokenKind::Comment(comment))) => { 304 | Ok(Event::new(location, EventKind::Comment(comment))) 305 | } 306 | (location, _) => Err(Error::new(location, ErrorKind::ExpectedColon)), 307 | }, 308 | MapStateExpecting::Value => match self.next_token().transpose()? { 309 | Some(Token { 310 | kind: TokenKind::Comment(comment), 311 | location, 312 | }) => Ok(Event::new(location, EventKind::Comment(comment))), 313 | Some(token) => { 314 | *self.map_state_mut() = MapStateExpecting::Comma; 315 | self.parse_token(token, None) 316 | } 317 | None => Err(Error::new( 318 | self.tokens.current_offset()..self.tokens.current_offset(), 319 | ErrorKind::ExpectedValue, 320 | )), 321 | }, 322 | MapStateExpecting::Comma => match self.next_token_parts()? { 323 | (location, Some(TokenKind::Close(Balanced::Brace))) => { 324 | self.nested.pop(); 325 | Ok(Event::new(location, EventKind::EndNested)) 326 | } 327 | (_, Some(TokenKind::Comma)) => { 328 | *self.map_state_mut() = MapStateExpecting::Key; 329 | self.parse_map(MapStateExpecting::Key) 330 | } 331 | (location, Some(TokenKind::Comment(comment))) => { 332 | Ok(Event::new(location, EventKind::Comment(comment))) 333 | } 334 | (location, _) => Err(Error::new( 335 | location, 336 | ErrorKind::ExpectedCommaOrEnd(Nested::Map), 337 | )), 338 | }, 339 | } 340 | } 341 | 342 | fn parse_implicit_map(&mut self, state: MapStateExpecting) -> Result, Error> { 343 | match state { 344 | MapStateExpecting::Key => match self.next_token().transpose()? { 345 | Some(Token { 346 | location, 347 | kind: TokenKind::Comment(comment), 348 | }) => Ok(Event::new(location, EventKind::Comment(comment))), 349 | Some(token) => match self.parse_token(token, None)? { 350 | Event { 351 | kind: EventKind::Primitive(primitive), 352 | location, 353 | } => { 354 | self.root_state = State::ImplicitMap(MapStateExpecting::Colon); 355 | Ok(Event::new(location, EventKind::Primitive(primitive))) 356 | } 357 | Event { location, .. } => Err(Error::new(location, ErrorKind::ExpectedKey)), 358 | }, 359 | None => { 360 | self.root_state = State::Finished; 361 | Ok(Event::new(self.current_range(), EventKind::EndNested)) 362 | } 363 | }, 364 | MapStateExpecting::Colon => match self.next_token_parts()? { 365 | (_, Some(TokenKind::Colon)) => { 366 | self.root_state = State::ImplicitMap(MapStateExpecting::Value); 367 | self.parse_implicit_map(MapStateExpecting::Value) 368 | } 369 | (location, Some(TokenKind::Comment(comment))) => { 370 | Ok(Event::new(location, EventKind::Comment(comment))) 371 | } 372 | (location, _) => Err(Error::new(location, ErrorKind::ExpectedColon)), 373 | }, 374 | MapStateExpecting::Value => match self.next_token().transpose()? { 375 | Some(Token { 376 | kind: TokenKind::Comment(comment), 377 | location, 378 | }) => Ok(Event::new(location, EventKind::Comment(comment))), 379 | Some(token) => { 380 | self.root_state = State::ImplicitMap(MapStateExpecting::Comma); 381 | self.parse_token(token, None) 382 | } 383 | None => Err(Error::new( 384 | self.tokens.current_offset()..self.tokens.current_offset(), 385 | ErrorKind::ExpectedValue, 386 | )), 387 | }, 388 | MapStateExpecting::Comma => match self.next_token().transpose()? { 389 | Some(Token { 390 | location, 391 | kind: TokenKind::Comment(comment), 392 | }) => Ok(Event::new(location, EventKind::Comment(comment))), 393 | Some(Token { 394 | location, 395 | kind: TokenKind::Close(Balanced::Brace), 396 | }) => { 397 | self.root_state = State::Finished; 398 | Ok(Event::new(location, EventKind::EndNested)) 399 | } 400 | Some(Token { 401 | kind: TokenKind::Comma, 402 | .. 403 | }) => { 404 | self.root_state = State::ImplicitMap(MapStateExpecting::Key); 405 | self.parse_implicit_map(MapStateExpecting::Key) 406 | } 407 | Some(token) => { 408 | self.root_state = State::ImplicitMap(MapStateExpecting::Colon); 409 | match self.parse_token(token, None)? { 410 | Event { 411 | location, 412 | kind: EventKind::Primitive(primitive), 413 | } => Ok(Event::new(location, EventKind::Primitive(primitive))), 414 | Event { location, .. } => Err(Error::new(location, ErrorKind::ExpectedKey)), 415 | } 416 | } 417 | None => { 418 | self.root_state = State::Finished; 419 | Ok(Event::new(self.current_range(), EventKind::EndNested)) 420 | } 421 | }, 422 | } 423 | } 424 | 425 | fn next_event(&mut self) -> Option, Error>> { 426 | Some(match self.nested.last() { 427 | None => match &self.root_state { 428 | State::AtStart => { 429 | let token = match self.next_token()? { 430 | Ok(token) => token, 431 | Err(err) => return Some(Err(err.into())), 432 | }; 433 | match &token.kind { 434 | TokenKind::Comment(comment) => { 435 | Ok(Event::new(token.location, EventKind::Comment(comment))) 436 | } 437 | _ if self.config.allow_implicit_map_at_root 438 | && matches!( 439 | self.peek(), 440 | Some(Token { 441 | kind: TokenKind::Colon, 442 | .. 443 | }) 444 | ) => 445 | { 446 | match self.parse_token(token, None) { 447 | Ok(event) => { 448 | self.root_state = State::StartingImplicitMap(event); 449 | Ok(Event::new( 450 | 0..0, 451 | EventKind::BeginNested { 452 | name: None, 453 | kind: Nested::Map, 454 | }, 455 | )) 456 | } 457 | Err(err) => Err(err), 458 | } 459 | } 460 | _ => { 461 | self.root_state = State::Finished; 462 | self.parse_token(token, None) 463 | } 464 | } 465 | } 466 | State::StartingImplicitMap(_) => { 467 | let State::StartingImplicitMap(event) = mem::replace( 468 | &mut self.root_state, 469 | State::ImplicitMap(MapStateExpecting::Colon), 470 | ) else { 471 | unreachable!("just matched") 472 | }; 473 | Ok(event) 474 | } 475 | State::ImplicitMap(state) => self.parse_implicit_map(*state), 476 | State::Finished => match self.next_token()? { 477 | Ok(token) => match token.kind { 478 | TokenKind::Comment(comment) => { 479 | Ok(Event::new(token.location, EventKind::Comment(comment))) 480 | } 481 | TokenKind::Whitespace(_) => unreachable!("disabled"), 482 | _ => Err(Error::new(token.location, ErrorKind::TrailingData)), 483 | }, 484 | Err(err) => Err(err.into()), 485 | }, 486 | }, 487 | 488 | Some((_, NestedState::Tuple(list))) => self.parse_sequence(*list, Balanced::Paren), 489 | Some((_, NestedState::List(list))) => self.parse_sequence(*list, Balanced::Bracket), 490 | Some((_, NestedState::Map(map))) => self.parse_map(*map), 491 | }) 492 | } 493 | } 494 | 495 | impl<'s> Iterator for Parser<'s> { 496 | type Item = Result, Error>; 497 | 498 | fn next(&mut self) -> Option { 499 | loop { 500 | let event = self.next_event()?; 501 | if self.config.include_comments 502 | || !matches!( 503 | event, 504 | Ok(Event { 505 | kind: EventKind::Comment(_), 506 | .. 507 | }) 508 | ) 509 | { 510 | break Some(event); 511 | } 512 | 513 | // Eat the comment 514 | } 515 | } 516 | } 517 | 518 | /// The configuration of a [`Parser`]. 519 | #[derive(Default, Debug, Clone, Copy)] 520 | #[non_exhaustive] 521 | pub struct Config { 522 | /// Allows parsing an implicit map at the root of the Rsn document. 523 | /// 524 | /// Rsn allows root-level syntax that may be desirable when using it as a 525 | /// configuration-like file format. 526 | /// 527 | /// Implicit map: 528 | /// ```rsn 529 | /// name: "John Doe" 530 | /// age: 40 531 | /// ``` 532 | /// 533 | /// Normal map: 534 | /// ```rsn 535 | /// { 536 | /// name: "John Doe", 537 | /// age: 40, 538 | /// } 539 | /// ``` 540 | /// 541 | /// When set to true, the parser will allow both implicit and explicit 542 | /// syntaxes at the root of the document. When set to false, the parser will 543 | /// only allow explicit maps. 544 | pub allow_implicit_map_at_root: bool, 545 | /// When true, the parser will include [`EventKind::Comment`] events. 546 | pub include_comments: bool, 547 | } 548 | 549 | impl Config { 550 | /// Sets [`Config::allow_implicit_map_at_root`] to `allow` and returns self. 551 | #[must_use] 552 | pub const fn allow_implicit_map_at_root(mut self, allow: bool) -> Self { 553 | self.allow_implicit_map_at_root = allow; 554 | self 555 | } 556 | 557 | /// Sets [`Config::include_comments`] to `include` and returns self. 558 | #[must_use] 559 | pub const fn include_comments(mut self, include: bool) -> Self { 560 | self.include_comments = include; 561 | self 562 | } 563 | } 564 | 565 | #[derive(Debug, Clone, PartialEq)] 566 | enum State<'s> { 567 | AtStart, 568 | StartingImplicitMap(Event<'s>), 569 | ImplicitMap(MapStateExpecting), 570 | Finished, 571 | } 572 | 573 | /// An error that arose while parsing Rsn events. 574 | #[derive(Debug, Clone, Eq, PartialEq)] 575 | pub struct Error { 576 | /// The byte range of this error. 577 | pub location: Range, 578 | /// The kind of error that occurred. 579 | pub kind: ErrorKind, 580 | } 581 | 582 | impl Error { 583 | #[must_use] 584 | pub(crate) fn new(location: Range, kind: ErrorKind) -> Self { 585 | Self { location, kind } 586 | } 587 | } 588 | 589 | #[cfg(feature = "std")] 590 | impl std::error::Error for Error {} 591 | 592 | impl Display for Error { 593 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 594 | Display::fmt(&self.kind, f) 595 | } 596 | } 597 | 598 | impl From for Error { 599 | fn from(err: tokenizer::Error) -> Self { 600 | Self { 601 | location: err.location, 602 | kind: err.kind.into(), 603 | } 604 | } 605 | } 606 | 607 | /// A kind of error that arose while parsing Rsn events. 608 | #[derive(Debug, Clone, Eq, PartialEq)] 609 | #[non_exhaustive] 610 | pub enum ErrorKind { 611 | /// An error occurred tokenizing the input. 612 | Tokenizer(tokenizer::ErrorKind), 613 | /// An end-of-file error was encountered when data was still expected. 614 | UnexpectedEof, 615 | /// A key in a map was expected. 616 | ExpectedKey, 617 | /// A `:` was expected. 618 | ExpectedColon, 619 | /// A value was expected. 620 | /// 621 | /// This may be encountered in both sequence (list/tuple) parsing and map 622 | /// parsing. 623 | ExpectedValue, 624 | /// Expected a `,` or the end-variant of the specified [`Nested`]. 625 | ExpectedCommaOrEnd(Nested), 626 | /// Expected either a map or a tuple. 627 | ExpectedMapOrTuple, 628 | /// Additional data was found after a complete value was parsed. 629 | TrailingData, 630 | } 631 | 632 | impl From for ErrorKind { 633 | fn from(kind: tokenizer::ErrorKind) -> Self { 634 | Self::Tokenizer(kind) 635 | } 636 | } 637 | 638 | impl Display for ErrorKind { 639 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 640 | match self { 641 | ErrorKind::Tokenizer(err) => Display::fmt(err, f), 642 | ErrorKind::UnexpectedEof => f.write_str("unexpected end of file"), 643 | ErrorKind::ExpectedValue => f.write_str("a value was expected"), 644 | ErrorKind::ExpectedCommaOrEnd(nested) => { 645 | write!(f, "expected `,` or {}", nested.err_display()) 646 | } 647 | ErrorKind::ExpectedColon => f.write_str("expected `:`"), 648 | ErrorKind::ExpectedKey => f.write_str("expected map key"), 649 | ErrorKind::TrailingData => f.write_str( 650 | "source contained extra trailing data after a value was completely read", 651 | ), 652 | ErrorKind::ExpectedMapOrTuple => { 653 | f.write_str("[ is not valid for a named value, expected { or (") 654 | } 655 | } 656 | } 657 | } 658 | 659 | /// A Rsn event from parsing Rsn. 660 | #[derive(Debug, Clone, PartialEq)] 661 | pub struct Event<'s> { 662 | /// The byte offset of the source that produced this event. 663 | pub location: Range, 664 | /// The kind of this event. 665 | pub kind: EventKind<'s>, 666 | } 667 | 668 | impl<'s> Event<'s> { 669 | #[must_use] 670 | fn new(location: Range, kind: EventKind<'s>) -> Self { 671 | Self { location, kind } 672 | } 673 | } 674 | 675 | /// A kind of an event encountered when parsing Rsn. 676 | #[derive(Debug, PartialEq, Clone)] 677 | pub enum EventKind<'s> { 678 | /// A nested sequence of events has started. 679 | /// 680 | /// The next events "belong" to this nesting until a matching 681 | /// [`EventKind::EndNested`] is encountered. 682 | BeginNested { 683 | /// The name of this nested context, if encountered. 684 | name: Option>, 685 | /// The type of nesting. 686 | kind: Nested, 687 | }, 688 | /// A nested sequence of events has concluded. 689 | /// 690 | /// This event can only be encountered after a [`EventKind::BeginNested`] 691 | /// has been encountered. Only valid nesting equences can be encountered. If 692 | /// nesting cannot be matched, an error will be returned. 693 | EndNested, 694 | /// A primitive literal. 695 | Primitive(Primitive<'s>), 696 | /// A comment. 697 | Comment(&'s str), 698 | } 699 | 700 | /// A name/identifier. 701 | #[derive(Debug, PartialEq, Clone)] 702 | pub struct Name<'s> { 703 | /// The byte range of the name in the source. 704 | pub location: Range, 705 | /// The name/identifier. 706 | pub name: &'s str, 707 | } 708 | 709 | impl<'s> Deref for Name<'s> { 710 | type Target = str; 711 | 712 | fn deref(&self) -> &Self::Target { 713 | self.name 714 | } 715 | } 716 | 717 | impl<'s> PartialEq for Name<'s> { 718 | fn eq(&self, other: &str) -> bool { 719 | self.name == other 720 | } 721 | } 722 | 723 | impl<'a, 's> PartialEq<&'a str> for Name<'s> { 724 | fn eq(&self, other: &&'a str) -> bool { 725 | self.name == *other 726 | } 727 | } 728 | 729 | /// A kind of nestable types. 730 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 731 | pub enum Nested { 732 | /// A sequence of values enclosed by parentheses. 733 | Tuple, 734 | /// A sequence of key-value pairs enclosed by curly braces. 735 | Map, 736 | /// A sequence of values enclosed by square brackets. 737 | List, 738 | } 739 | 740 | impl Nested { 741 | fn err_display(self) -> &'static str { 742 | match self { 743 | Nested::Tuple => "`)`", 744 | Nested::Map => "`}`", 745 | Nested::List => "`]`", 746 | } 747 | } 748 | } 749 | 750 | impl From for Nested { 751 | fn from(kind: Balanced) -> Self { 752 | match kind { 753 | Balanced::Paren => Self::Tuple, 754 | Balanced::Bracket => Self::List, 755 | Balanced::Brace => Self::Map, 756 | } 757 | } 758 | } 759 | 760 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 761 | enum NestedState { 762 | Tuple(ListStateExpecting), 763 | List(ListStateExpecting), 764 | Map(MapStateExpecting), 765 | } 766 | 767 | impl NestedState { 768 | fn list(kind: Balanced, state: ListStateExpecting) -> Self { 769 | match kind { 770 | Balanced::Paren => Self::Tuple(state), 771 | Balanced::Bracket => Self::List(state), 772 | Balanced::Brace => unreachable!("Brace must receive a MapState"), 773 | } 774 | } 775 | } 776 | 777 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 778 | enum ListStateExpecting { 779 | Value, 780 | Comma, 781 | } 782 | 783 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 784 | enum MapStateExpecting { 785 | Key, 786 | Colon, 787 | Value, 788 | Comma, 789 | } 790 | 791 | /// A primitive literal. 792 | #[derive(Debug, PartialEq, Clone)] 793 | pub enum Primitive<'s> { 794 | /// A boolean literal. 795 | Bool(bool), 796 | /// An integer literal. 797 | Integer(Integer), 798 | /// A floating point literal. 799 | Float(f64), 800 | /// A character literal. 801 | Char(char), 802 | /// A string literal. 803 | String(Cow<'s, str>), 804 | /// An identifier. 805 | Identifier(&'s str), 806 | /// A byte string literal. 807 | Bytes(Cow<'s, [u8]>), 808 | } 809 | 810 | #[cfg(test)] 811 | mod tests { 812 | use super::*; 813 | #[test] 814 | fn number_array() { 815 | let events = Parser::new("[1,2,3]", Config::default()) 816 | .collect::, _>>() 817 | .unwrap(); 818 | assert_eq!( 819 | &events, 820 | &[ 821 | Event::new( 822 | 0..1, 823 | EventKind::BeginNested { 824 | name: None, 825 | kind: Nested::List 826 | } 827 | ), 828 | Event::new( 829 | 1..2, 830 | EventKind::Primitive(Primitive::Integer(Integer::Usize(1))) 831 | ), 832 | Event::new( 833 | 3..4, 834 | EventKind::Primitive(Primitive::Integer(Integer::Usize(2))) 835 | ), 836 | Event::new( 837 | 5..6, 838 | EventKind::Primitive(Primitive::Integer(Integer::Usize(3))) 839 | ), 840 | Event::new(6..7, EventKind::EndNested), 841 | ] 842 | ); 843 | let events = Parser::new("[1,2,3,]", Config::default()) 844 | .collect::, _>>() 845 | .unwrap(); 846 | assert_eq!( 847 | &events, 848 | &[ 849 | Event::new( 850 | 0..1, 851 | EventKind::BeginNested { 852 | name: None, 853 | kind: Nested::List 854 | } 855 | ), 856 | Event::new( 857 | 1..2, 858 | EventKind::Primitive(Primitive::Integer(Integer::Usize(1))) 859 | ), 860 | Event::new( 861 | 3..4, 862 | EventKind::Primitive(Primitive::Integer(Integer::Usize(2))) 863 | ), 864 | Event::new( 865 | 5..6, 866 | EventKind::Primitive(Primitive::Integer(Integer::Usize(3))) 867 | ), 868 | Event::new(7..8, EventKind::EndNested), 869 | ] 870 | ); 871 | } 872 | 873 | #[test] 874 | fn number_tuple() { 875 | let events = Parser::new("(1,2,3)", Config::default()) 876 | .collect::, _>>() 877 | .unwrap(); 878 | assert_eq!( 879 | &events, 880 | &[ 881 | Event::new( 882 | 0..1, 883 | EventKind::BeginNested { 884 | name: None, 885 | kind: Nested::Tuple 886 | } 887 | ), 888 | Event::new( 889 | 1..2, 890 | EventKind::Primitive(Primitive::Integer(Integer::Usize(1))) 891 | ), 892 | Event::new( 893 | 3..4, 894 | EventKind::Primitive(Primitive::Integer(Integer::Usize(2))) 895 | ), 896 | Event::new( 897 | 5..6, 898 | EventKind::Primitive(Primitive::Integer(Integer::Usize(3))) 899 | ), 900 | Event::new(6..7, EventKind::EndNested), 901 | ] 902 | ); 903 | let events = Parser::new("(1,2,3,)", Config::default()) 904 | .collect::, _>>() 905 | .unwrap(); 906 | assert_eq!( 907 | &events, 908 | &[ 909 | Event::new( 910 | 0..1, 911 | EventKind::BeginNested { 912 | name: None, 913 | kind: Nested::Tuple 914 | } 915 | ), 916 | Event::new( 917 | 1..2, 918 | EventKind::Primitive(Primitive::Integer(Integer::Usize(1))) 919 | ), 920 | Event::new( 921 | 3..4, 922 | EventKind::Primitive(Primitive::Integer(Integer::Usize(2))) 923 | ), 924 | Event::new( 925 | 5..6, 926 | EventKind::Primitive(Primitive::Integer(Integer::Usize(3))) 927 | ), 928 | Event::new(7..8, EventKind::EndNested), 929 | ] 930 | ); 931 | } 932 | 933 | #[test] 934 | fn number_map() { 935 | let events = Parser::new("{a:1,b:2}", Config::default()) 936 | .collect::, _>>() 937 | .unwrap(); 938 | assert_eq!( 939 | &events, 940 | &[ 941 | Event::new( 942 | 0..1, 943 | EventKind::BeginNested { 944 | name: None, 945 | kind: Nested::Map 946 | } 947 | ), 948 | Event::new(1..2, EventKind::Primitive(Primitive::Identifier("a"))), 949 | Event::new( 950 | 3..4, 951 | EventKind::Primitive(Primitive::Integer(Integer::Usize(1))) 952 | ), 953 | Event::new(5..6, EventKind::Primitive(Primitive::Identifier("b"))), 954 | Event::new( 955 | 7..8, 956 | EventKind::Primitive(Primitive::Integer(Integer::Usize(2))) 957 | ), 958 | Event::new(8..9, EventKind::EndNested), 959 | ] 960 | ); 961 | let events = Parser::new("{a:1,b:2,}", Config::default()) 962 | .collect::, _>>() 963 | .unwrap(); 964 | assert_eq!( 965 | &events, 966 | &[ 967 | Event::new( 968 | 0..1, 969 | EventKind::BeginNested { 970 | name: None, 971 | kind: Nested::Map 972 | } 973 | ), 974 | Event::new(1..2, EventKind::Primitive(Primitive::Identifier("a"))), 975 | Event::new( 976 | 3..4, 977 | EventKind::Primitive(Primitive::Integer(Integer::Usize(1))) 978 | ), 979 | Event::new(5..6, EventKind::Primitive(Primitive::Identifier("b"))), 980 | Event::new( 981 | 7..8, 982 | EventKind::Primitive(Primitive::Integer(Integer::Usize(2))) 983 | ), 984 | Event::new(9..10, EventKind::EndNested), 985 | ] 986 | ); 987 | } 988 | 989 | #[test] 990 | fn commented() { 991 | let events = Parser::new( 992 | "/**/{/**/a/**/:/**/1/**/,/**/b/**/:/**/[/**/2/**/,/**/3/**/]/**/}/**/", 993 | Config::default().include_comments(true), 994 | ) 995 | .collect::, _>>() 996 | .unwrap(); 997 | assert_eq!( 998 | &events, 999 | &[ 1000 | Event::new(0..4, EventKind::Comment("/**/")), 1001 | Event::new( 1002 | 4..5, 1003 | EventKind::BeginNested { 1004 | name: None, 1005 | kind: Nested::Map 1006 | } 1007 | ), 1008 | Event::new(5..9, EventKind::Comment("/**/")), 1009 | Event::new(9..10, EventKind::Primitive(Primitive::Identifier("a"))), 1010 | Event::new(10..14, EventKind::Comment("/**/")), 1011 | Event::new(15..19, EventKind::Comment("/**/")), 1012 | Event::new( 1013 | 19..20, 1014 | EventKind::Primitive(Primitive::Integer(Integer::Usize(1))) 1015 | ), 1016 | Event::new(20..24, EventKind::Comment("/**/")), 1017 | Event::new(25..29, EventKind::Comment("/**/")), 1018 | Event::new(29..30, EventKind::Primitive(Primitive::Identifier("b"))), 1019 | Event::new(30..34, EventKind::Comment("/**/")), 1020 | Event::new(35..39, EventKind::Comment("/**/")), 1021 | Event::new( 1022 | 39..40, 1023 | EventKind::BeginNested { 1024 | name: None, 1025 | kind: Nested::List 1026 | } 1027 | ), 1028 | Event::new(40..44, EventKind::Comment("/**/")), 1029 | Event::new( 1030 | 44..45, 1031 | EventKind::Primitive(Primitive::Integer(Integer::Usize(2))) 1032 | ), 1033 | Event::new(45..49, EventKind::Comment("/**/")), 1034 | Event::new(50..54, EventKind::Comment("/**/")), 1035 | Event::new( 1036 | 54..55, 1037 | EventKind::Primitive(Primitive::Integer(Integer::Usize(3))) 1038 | ), 1039 | Event::new(55..59, EventKind::Comment("/**/")), 1040 | Event::new(59..60, EventKind::EndNested), 1041 | Event::new(60..64, EventKind::Comment("/**/")), 1042 | Event::new(64..65, EventKind::EndNested), 1043 | Event::new(65..69, EventKind::Comment("/**/")), 1044 | ] 1045 | ); 1046 | } 1047 | 1048 | #[test] 1049 | fn array_named_error() { 1050 | let err = Parser::new("Foo[]", Config::default()) 1051 | .next() 1052 | .unwrap() 1053 | .unwrap_err(); 1054 | assert_eq!(err, Error::new(3..4, ErrorKind::ExpectedMapOrTuple)); 1055 | } 1056 | } 1057 | -------------------------------------------------------------------------------- /src/ser.rs: -------------------------------------------------------------------------------- 1 | use alloc::borrow::Cow; 2 | use alloc::string::String; 3 | use core::fmt::Write; 4 | 5 | use serde::ser::{ 6 | SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, 7 | SerializeTupleStruct, SerializeTupleVariant, 8 | }; 9 | use serde::Serialize; 10 | 11 | use crate::writer::{self, Writer}; 12 | 13 | /// A Serde serializer that generates Rsn. 14 | #[derive(Debug)] 15 | pub struct Serializer<'config, Output> { 16 | writer: Writer<'config, Output>, 17 | implicit_map_at_root: bool, 18 | anonymous_structs: bool, 19 | } 20 | 21 | impl Default for Serializer<'static, String> { 22 | fn default() -> Self { 23 | Self { 24 | writer: Writer::default(), 25 | implicit_map_at_root: false, 26 | anonymous_structs: false, 27 | } 28 | } 29 | } 30 | 31 | impl<'config, Output> Serializer<'config, Output> 32 | where 33 | Output: Write, 34 | { 35 | /// Returns a new serializer that writes to `output` using `configuration`. 36 | pub fn new(output: Output, configuration: &'config Config) -> Self { 37 | Self { 38 | writer: Writer::new(output, &configuration.writer), 39 | implicit_map_at_root: configuration.implicit_map_at_root, 40 | anonymous_structs: configuration.anonymous_structs, 41 | } 42 | } 43 | 44 | /// Finishes writing to the output and returns the output. 45 | pub fn finish(self) -> Output { 46 | self.writer.finish() 47 | } 48 | 49 | fn mark_value_seen(&mut self) { 50 | self.implicit_map_at_root = false; 51 | } 52 | } 53 | 54 | impl<'a, 'config, Output> serde::Serializer for &'a mut Serializer<'config, Output> 55 | where 56 | Output: Write, 57 | { 58 | type Error = core::fmt::Error; 59 | type Ok = (); 60 | type SerializeMap = sealed::MapSerializer<'a, 'config, Output>; 61 | type SerializeSeq = Self; 62 | type SerializeStruct = sealed::MapSerializer<'a, 'config, Output>; 63 | type SerializeStructVariant = Self; 64 | type SerializeTuple = Self; 65 | type SerializeTupleStruct = Self; 66 | type SerializeTupleVariant = Self; 67 | 68 | fn serialize_bool(self, v: bool) -> Result { 69 | self.mark_value_seen(); 70 | self.writer.write_primitive(&v) 71 | } 72 | 73 | fn serialize_i8(self, v: i8) -> Result { 74 | self.mark_value_seen(); 75 | self.writer.write_primitive(&v) 76 | } 77 | 78 | fn serialize_i16(self, v: i16) -> Result { 79 | self.mark_value_seen(); 80 | self.writer.write_primitive(&v) 81 | } 82 | 83 | fn serialize_i32(self, v: i32) -> Result { 84 | self.mark_value_seen(); 85 | self.writer.write_primitive(&v) 86 | } 87 | 88 | fn serialize_i64(self, v: i64) -> Result { 89 | self.mark_value_seen(); 90 | self.writer.write_primitive(&v) 91 | } 92 | 93 | fn serialize_i128(self, v: i128) -> Result { 94 | self.mark_value_seen(); 95 | self.writer.write_primitive(&v) 96 | } 97 | 98 | fn serialize_u8(self, v: u8) -> Result { 99 | self.mark_value_seen(); 100 | self.writer.write_primitive(&v) 101 | } 102 | 103 | fn serialize_u16(self, v: u16) -> Result { 104 | self.mark_value_seen(); 105 | self.writer.write_primitive(&v) 106 | } 107 | 108 | fn serialize_u32(self, v: u32) -> Result { 109 | self.mark_value_seen(); 110 | self.writer.write_primitive(&v) 111 | } 112 | 113 | fn serialize_u64(self, v: u64) -> Result { 114 | self.mark_value_seen(); 115 | self.writer.write_primitive(&v) 116 | } 117 | 118 | fn serialize_u128(self, v: u128) -> Result { 119 | self.mark_value_seen(); 120 | self.writer.write_primitive(&v) 121 | } 122 | 123 | fn serialize_f32(self, v: f32) -> Result { 124 | self.mark_value_seen(); 125 | self.writer.write_primitive(&v) 126 | } 127 | 128 | fn serialize_f64(self, v: f64) -> Result { 129 | self.mark_value_seen(); 130 | self.writer.write_primitive(&v) 131 | } 132 | 133 | fn serialize_char(self, v: char) -> Result { 134 | self.mark_value_seen(); 135 | self.writer.write_primitive(&v) 136 | } 137 | 138 | fn serialize_str(self, v: &str) -> Result { 139 | self.mark_value_seen(); 140 | self.writer.write_primitive(v) 141 | } 142 | 143 | fn serialize_bytes(self, v: &[u8]) -> Result { 144 | self.mark_value_seen(); 145 | self.writer.write_primitive(v) 146 | } 147 | 148 | fn serialize_none(self) -> Result { 149 | self.mark_value_seen(); 150 | self.writer.write_raw_value("None") 151 | } 152 | 153 | fn serialize_some(self, value: &T) -> Result 154 | where 155 | T: serde::Serialize + ?Sized, 156 | { 157 | self.mark_value_seen(); 158 | self.writer.begin_named_tuple("Some")?; 159 | value.serialize(&mut *self)?; 160 | self.writer.finish_nested()?; 161 | Ok(()) 162 | } 163 | 164 | fn serialize_unit(self) -> Result { 165 | self.mark_value_seen(); 166 | self.writer.write_raw_value("()") 167 | } 168 | 169 | fn serialize_unit_struct(self, name: &'static str) -> Result { 170 | self.mark_value_seen(); 171 | self.writer.write_raw_value(name) 172 | } 173 | 174 | fn serialize_unit_variant( 175 | self, 176 | _name: &'static str, 177 | _variant_index: u32, 178 | variant: &'static str, 179 | ) -> Result { 180 | self.mark_value_seen(); 181 | self.writer.write_raw_value(variant) 182 | } 183 | 184 | fn serialize_newtype_struct( 185 | self, 186 | name: &'static str, 187 | value: &T, 188 | ) -> Result 189 | where 190 | T: serde::Serialize + ?Sized, 191 | { 192 | self.mark_value_seen(); 193 | self.writer.begin_named_tuple(name)?; 194 | value.serialize(&mut *self)?; 195 | self.writer.finish_nested() 196 | } 197 | 198 | fn serialize_newtype_variant( 199 | self, 200 | _name: &'static str, 201 | _variant_index: u32, 202 | variant: &'static str, 203 | value: &T, 204 | ) -> Result 205 | where 206 | T: serde::Serialize + ?Sized, 207 | { 208 | self.mark_value_seen(); 209 | self.writer.begin_named_tuple(variant)?; 210 | value.serialize(&mut *self)?; 211 | self.writer.finish_nested() 212 | } 213 | 214 | fn serialize_seq(self, _len: Option) -> Result { 215 | self.mark_value_seen(); 216 | self.writer.begin_list()?; 217 | Ok(self) 218 | } 219 | 220 | fn serialize_tuple(self, _len: usize) -> Result { 221 | self.mark_value_seen(); 222 | self.writer.begin_tuple()?; 223 | Ok(self) 224 | } 225 | 226 | fn serialize_tuple_struct( 227 | self, 228 | name: &'static str, 229 | _len: usize, 230 | ) -> Result { 231 | self.mark_value_seen(); 232 | self.writer.begin_named_tuple(name)?; 233 | Ok(self) 234 | } 235 | 236 | fn serialize_tuple_variant( 237 | self, 238 | _name: &'static str, 239 | _variant_index: u32, 240 | variant: &'static str, 241 | _len: usize, 242 | ) -> Result { 243 | self.mark_value_seen(); 244 | self.writer.begin_named_tuple(variant)?; 245 | Ok(self) 246 | } 247 | 248 | fn serialize_map(self, _len: Option) -> Result { 249 | let is_implicit_map = self.implicit_map_at_root; 250 | self.mark_value_seen(); 251 | if !is_implicit_map { 252 | self.writer.begin_map()?; 253 | } 254 | Ok(sealed::MapSerializer { 255 | serializer: self, 256 | is_implicit_map, 257 | }) 258 | } 259 | 260 | fn serialize_struct( 261 | self, 262 | name: &'static str, 263 | _len: usize, 264 | ) -> Result { 265 | let is_implicit_map = self.implicit_map_at_root; 266 | self.mark_value_seen(); 267 | 268 | if !is_implicit_map { 269 | if self.anonymous_structs { 270 | self.writer.begin_map()?; 271 | } else { 272 | self.writer.begin_named_map(name)?; 273 | } 274 | } 275 | 276 | Ok(sealed::MapSerializer { 277 | serializer: self, 278 | is_implicit_map, 279 | }) 280 | } 281 | 282 | fn serialize_struct_variant( 283 | self, 284 | _name: &'static str, 285 | _variant_index: u32, 286 | variant: &'static str, 287 | _len: usize, 288 | ) -> Result { 289 | self.writer.begin_named_map(variant)?; 290 | Ok(self) 291 | } 292 | } 293 | 294 | impl<'a, 'config, Output> SerializeSeq for &'a mut Serializer<'config, Output> 295 | where 296 | Output: Write, 297 | { 298 | type Error = core::fmt::Error; 299 | type Ok = (); 300 | 301 | fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> 302 | where 303 | T: serde::Serialize + ?Sized, 304 | { 305 | value.serialize(&mut **self) 306 | } 307 | 308 | fn end(self) -> Result { 309 | self.writer.finish_nested() 310 | } 311 | } 312 | 313 | impl<'a, 'config, Output> SerializeTuple for &'a mut Serializer<'config, Output> 314 | where 315 | Output: Write, 316 | { 317 | type Error = core::fmt::Error; 318 | type Ok = (); 319 | 320 | fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> 321 | where 322 | T: serde::Serialize + ?Sized, 323 | { 324 | value.serialize(&mut **self) 325 | } 326 | 327 | fn end(self) -> Result { 328 | self.writer.finish_nested() 329 | } 330 | } 331 | 332 | impl<'a, 'config, Output> SerializeTupleStruct for &'a mut Serializer<'config, Output> 333 | where 334 | Output: Write, 335 | { 336 | type Error = core::fmt::Error; 337 | type Ok = (); 338 | 339 | fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> 340 | where 341 | T: serde::Serialize + ?Sized, 342 | { 343 | value.serialize(&mut **self) 344 | } 345 | 346 | fn end(self) -> Result { 347 | self.writer.finish_nested() 348 | } 349 | } 350 | 351 | impl<'a, 'config, Output> SerializeTupleVariant for &'a mut Serializer<'config, Output> 352 | where 353 | Output: Write, 354 | { 355 | type Error = core::fmt::Error; 356 | type Ok = (); 357 | 358 | fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> 359 | where 360 | T: serde::Serialize + ?Sized, 361 | { 362 | value.serialize(&mut **self) 363 | } 364 | 365 | fn end(self) -> Result { 366 | self.writer.finish_nested() 367 | } 368 | } 369 | 370 | mod sealed { 371 | use super::{SerializeMap, SerializeStruct, SerializeStructVariant, Serializer, Write}; 372 | 373 | pub struct MapSerializer<'a, 'config, Output> { 374 | pub serializer: &'a mut Serializer<'config, Output>, 375 | pub is_implicit_map: bool, 376 | } 377 | 378 | impl<'a, 'config, Output> SerializeStruct for MapSerializer<'a, 'config, Output> 379 | where 380 | Output: Write, 381 | { 382 | type Error = core::fmt::Error; 383 | type Ok = (); 384 | 385 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> 386 | where 387 | T: serde::Serialize + ?Sized, 388 | { 389 | if self.is_implicit_map { 390 | self.serializer.writer.write_raw_value(key)?; 391 | self.serializer.writer.write_raw_value(": ")?; 392 | value.serialize(&mut *self.serializer)?; 393 | self.serializer.writer.insert_newline()?; 394 | } else { 395 | self.serializer.writer.write_raw_value(key)?; 396 | value.serialize(&mut *self.serializer)?; 397 | } 398 | Ok(()) 399 | } 400 | 401 | fn end(self) -> Result { 402 | if !self.is_implicit_map { 403 | self.serializer.writer.finish_nested()?; 404 | } 405 | Ok(()) 406 | } 407 | } 408 | 409 | impl<'a, 'config, Output> SerializeStructVariant for &'a mut Serializer<'config, Output> 410 | where 411 | Output: Write, 412 | { 413 | type Error = core::fmt::Error; 414 | type Ok = (); 415 | 416 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> 417 | where 418 | T: serde::Serialize + ?Sized, 419 | { 420 | self.writer.write_raw_value(key)?; 421 | value.serialize(&mut **self) 422 | } 423 | 424 | fn end(self) -> Result { 425 | self.writer.finish_nested() 426 | } 427 | } 428 | 429 | impl<'a, 'config, Output> SerializeMap for MapSerializer<'a, 'config, Output> 430 | where 431 | Output: Write, 432 | { 433 | type Error = core::fmt::Error; 434 | type Ok = (); 435 | 436 | fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> 437 | where 438 | T: serde::Serialize + ?Sized, 439 | { 440 | key.serialize(&mut *self.serializer) 441 | } 442 | 443 | fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> 444 | where 445 | T: serde::Serialize + ?Sized, 446 | { 447 | if self.is_implicit_map { 448 | self.serializer.writer.write_raw_value(": ")?; 449 | value.serialize(&mut *self.serializer)?; 450 | self.serializer.writer.insert_newline() 451 | } else { 452 | value.serialize(&mut *self.serializer) 453 | } 454 | } 455 | 456 | fn end(self) -> Result { 457 | if !self.is_implicit_map { 458 | self.serializer.writer.finish_nested()?; 459 | } 460 | Ok(()) 461 | } 462 | } 463 | } 464 | 465 | /// The configuration for a [`Serializer`]. 466 | #[derive(Default, Debug, Clone)] 467 | #[non_exhaustive] 468 | pub struct Config { 469 | /// The writer configuration. 470 | pub writer: writer::Config, 471 | /// Whether a map-like type at the root of the document should use the 472 | /// implicit map syntax. 473 | pub implicit_map_at_root: bool, 474 | /// Whether to include the names of structures in. 475 | pub anonymous_structs: bool, 476 | } 477 | 478 | impl Config { 479 | /// Returns the default configuration. 480 | /// 481 | /// The default configuration uses: 482 | /// 483 | /// - `writer`: [`writer::Config::Compact`] 484 | /// - `implicit_map_at_root`: `false` 485 | /// - `anonymous_structs`: `false` 486 | /// 487 | /// ```rust 488 | /// use std::collections::HashMap; 489 | /// 490 | /// let serialized = rsn::ser::Config::new() 491 | /// .serialize(&HashMap::from([("hello", "world")])) 492 | /// .unwrap(); 493 | /// assert_eq!(serialized, r#"{"hello":"world"}"#) 494 | /// ``` 495 | #[must_use] 496 | pub const fn new() -> Self { 497 | Self { 498 | writer: writer::Config::Compact, 499 | implicit_map_at_root: false, 500 | anonymous_structs: false, 501 | } 502 | } 503 | 504 | /// Returns the default configuration with nested indentation and newlines. 505 | /// 506 | /// ```rust 507 | /// use std::collections::HashMap; 508 | /// 509 | /// let serialized = rsn::ser::Config::pretty() 510 | /// .serialize(&HashMap::from([("hello", "world")])) 511 | /// .unwrap(); 512 | /// assert_eq!( 513 | /// serialized, 514 | /// r#"{ 515 | /// "hello": "world" 516 | /// }"# 517 | /// ); 518 | /// ``` 519 | #[must_use] 520 | pub fn pretty() -> Self { 521 | Self { 522 | writer: writer::Config::Pretty { 523 | indentation: Cow::Borrowed(" "), 524 | newline: Cow::Borrowed("\n"), 525 | }, 526 | ..Default::default() 527 | } 528 | } 529 | 530 | /// Sets [`Config::implicit_map_at_root`] and returns self. 531 | /// 532 | /// ```rust 533 | /// use std::collections::HashMap; 534 | /// 535 | /// let serialized = rsn::ser::Config::pretty() 536 | /// .implicit_map_at_root(true) 537 | /// .serialize(&HashMap::from([("hello", "world")])) 538 | /// .unwrap(); 539 | /// assert_eq!(serialized, "\"hello\": \"world\"\n"); 540 | /// ``` 541 | #[must_use] 542 | pub const fn implicit_map_at_root(mut self, implicit_map_at_root: bool) -> Self { 543 | self.implicit_map_at_root = implicit_map_at_root; 544 | self 545 | } 546 | 547 | /// Sets [`Config::anonymous_structs`] and returns self. 548 | /// 549 | /// ```rust 550 | /// use serde::Serialize; 551 | /// 552 | /// #[derive(Serialize)] 553 | /// struct Person { 554 | /// name: &'static str, 555 | /// } 556 | /// 557 | /// // With anonymous structures enabled 558 | /// let anonymous_structs = rsn::ser::Config::new() 559 | /// .anonymous_structs(true) 560 | /// .serialize(&Person { name: "Bob" }) 561 | /// .unwrap(); 562 | /// assert_eq!(anonymous_structs, r#"{name:"Bob"}"#); 563 | /// 564 | /// // The default configuration 565 | /// let default_config = rsn::ser::Config::new() 566 | /// .serialize(&Person { name: "Bob" }) 567 | /// .unwrap(); 568 | /// assert_eq!(default_config, r#"Person{name:"Bob"}"#); 569 | /// ``` 570 | #[must_use] 571 | pub const fn anonymous_structs(mut self, anonymous_structs: bool) -> Self { 572 | self.anonymous_structs = anonymous_structs; 573 | self 574 | } 575 | 576 | /// Returns `value` serialized as Rsn with this configuration. 577 | /// 578 | /// ```rust 579 | /// let serialized = rsn::ser::Config::new().serialize(&vec![1, 2, 3]).unwrap(); 580 | /// assert_eq!(serialized, "[1,2,3]"); 581 | /// ``` 582 | /// 583 | /// # Errors 584 | /// 585 | /// Rsn itself does not produce any errors while serializing values. This 586 | /// function will return errors that arise within `Serialize` implementations 587 | /// encountered while serializing `value`. 588 | pub fn serialize(&self, value: &S) -> Result { 589 | let mut serializer = Serializer::new(String::new(), self); 590 | value.serialize(&mut serializer)?; 591 | Ok(serializer.finish()) 592 | } 593 | 594 | /// Returns `value` serialized as Rsn with this configuration. 595 | /// 596 | /// ```rust 597 | /// let serialized = rsn::ser::Config::new() 598 | /// .serialize_to_vec(&vec![1, 2, 3]) 599 | /// .unwrap(); 600 | /// assert_eq!(serialized, b"[1,2,3]"); 601 | /// ``` 602 | /// 603 | /// # Errors 604 | /// 605 | /// Rsn itself does not produce any errors while serializing values. This 606 | /// function will return errors that arise within `Serialize` implementations 607 | /// encountered while serializing `value`. 608 | pub fn serialize_to_vec( 609 | &self, 610 | value: &S, 611 | ) -> Result, core::fmt::Error> { 612 | self.serialize(value).map(String::into_bytes) 613 | } 614 | } 615 | 616 | #[cfg(feature = "std")] 617 | mod serialize_writer { 618 | use super::{Config, Serialize, Serializer, Write}; 619 | 620 | struct Writer { 621 | inner: T, 622 | written: usize, 623 | error: Option, 624 | } 625 | impl Write for Writer { 626 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 627 | match self.inner.write(s.as_bytes()) { 628 | Ok(written) => { 629 | self.written += written; 630 | Ok(()) 631 | } 632 | Err(error) => { 633 | assert!( 634 | self.error.is_none(), 635 | "should not have continued on write error" 636 | ); 637 | self.error = Some(error); 638 | Err(core::fmt::Error) 639 | } 640 | } 641 | } 642 | } 643 | impl Config { 644 | /// Serializes `value` into `writer` using this configuration. 645 | /// 646 | /// ```rust 647 | /// let mut serialized = Vec::new(); 648 | /// rsn::ser::Config::new() 649 | /// .serialize_to_writer(&vec![1, 2, 3], &mut serialized) 650 | /// .unwrap(); 651 | /// assert_eq!(serialized, b"[1,2,3]"); 652 | /// ``` 653 | /// 654 | /// # Errors 655 | /// 656 | /// Returns any errors occurring while serializing `value` or while 657 | /// writing to `writer`. 658 | #[allow(clippy::missing_panics_doc)] 659 | pub fn serialize_to_writer( 660 | &self, 661 | value: &S, 662 | writer: W, 663 | ) -> std::io::Result { 664 | let mut writer = Writer { 665 | inner: writer, 666 | written: 0, 667 | error: None, 668 | }; 669 | let mut serializer = Serializer::new(&mut writer, self); 670 | value 671 | .serialize(&mut serializer) 672 | .map_err(|_| writer.error.expect("should store error on error"))?; 673 | Ok(writer.written) 674 | } 675 | } 676 | } 677 | 678 | #[test] 679 | fn serialization_test() { 680 | #[derive(Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)] 681 | struct BasicNamed { 682 | a: u32, 683 | b: i32, 684 | } 685 | 686 | let rendered = crate::to_string(&BasicNamed { a: 1, b: -1 }).expect("no errors"); 687 | assert_eq!(rendered, "BasicNamed{a:1,b:-1}"); 688 | } 689 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "serde")] 2 | mod serde; 3 | 4 | #[macro_export] 5 | macro_rules! dbg { 6 | () => {{ 7 | #[cfg(feature = "std")] 8 | { 9 | ::std::dbg!() 10 | } 11 | }}; 12 | ($val:expr $(,)?) => {{ 13 | #[cfg(feature = "std")] 14 | { 15 | ::std::dbg!($val) 16 | } 17 | }}; 18 | ($($val:expr),+ $(,)?) => {{ 19 | #[cfg(feature = "std")] 20 | ::std::dbg!($($val),+) 21 | }}; 22 | } 23 | 24 | #[macro_export] 25 | macro_rules! println { 26 | () => {{ 27 | #[cfg(feature = "std")] 28 | { 29 | ::std::prinltln!() 30 | } 31 | }}; 32 | ($($arg:tt)*) => {{ 33 | #[cfg(feature = "std")] 34 | ::std::println!($($arg)*) 35 | }}; 36 | } 37 | -------------------------------------------------------------------------------- /src/tests/serde.rs: -------------------------------------------------------------------------------- 1 | use alloc::borrow::Cow; 2 | use alloc::vec; 3 | use core::fmt::Debug; 4 | use std::collections::BTreeMap; 5 | use std::string::String; 6 | 7 | use serde::{Deserialize, Serialize}; 8 | 9 | use crate::value::Value; 10 | use crate::{dbg, println}; 11 | 12 | #[derive(Serialize, Deserialize, Default, Debug, PartialEq)] 13 | struct StructOfEverything<'a> { 14 | str: Cow<'a, str>, 15 | bytes: serde_bytes::ByteBuf, 16 | char: char, 17 | u8: u8, 18 | u16: u16, 19 | u32: u32, 20 | u64: u64, 21 | u128: u128, 22 | usize: usize, 23 | i8: i8, 24 | i16: i16, 25 | i32: i32, 26 | i64: i64, 27 | i128: i128, 28 | isize: isize, 29 | bool: bool, 30 | } 31 | 32 | impl<'a> StructOfEverything<'a> { 33 | fn min() -> Self { 34 | Self { 35 | str: Cow::Borrowed("\0"), 36 | bytes: serde_bytes::ByteBuf::from(vec![0]), 37 | char: '\0', 38 | u8: 0, 39 | u16: 0, 40 | u32: 0, 41 | u64: 0, 42 | u128: 0, 43 | usize: 0, 44 | i8: i8::MIN, 45 | i16: i16::MIN, 46 | i32: i32::MIN, 47 | i64: i64::MIN, 48 | i128: i128::from(i64::MIN), /* To make deserialization strings consistent and compatible across feature flags */ 49 | isize: isize::MIN, 50 | bool: false, 51 | } 52 | } 53 | 54 | fn max() -> Self { 55 | Self { 56 | str: Cow::Borrowed("hello \u{1_F980}"), 57 | bytes: serde_bytes::ByteBuf::from(b"hello, world".to_vec()), 58 | char: '\u{1_F980}', 59 | u8: u8::MAX, 60 | u16: u16::MAX, 61 | u32: u32::MAX, 62 | u64: u64::MAX, 63 | u128: u128::from(u64::MAX), /* To make deserialization strings consistent and compatible across feature flags */ 64 | usize: usize::MAX, 65 | i8: i8::MAX, 66 | i16: i16::MAX, 67 | i32: i32::MAX, 68 | i64: i64::MAX, 69 | i128: i128::from(i64::MAX), /* To make deserialization strings consistent and compatible across feature flags */ 70 | isize: isize::MAX, 71 | bool: true, 72 | } 73 | } 74 | } 75 | 76 | #[track_caller] 77 | fn roundtrip Deserialize<'de> + PartialEq>(value: &T, check: &str) { 78 | let rendered = dbg!(crate::to_string(value).expect("no errors")); 79 | assert_eq!(rendered, check); 80 | let restored: T = crate::from_str(&rendered).expect("deserialization failed"); 81 | assert_eq!(&restored, value); 82 | } 83 | 84 | #[track_caller] 85 | fn roundtrip_pretty Deserialize<'de> + PartialEq>( 86 | value: &T, 87 | check: &str, 88 | ) { 89 | let rendered = crate::to_string_pretty(value).expect("no errors"); 90 | println!("{rendered}"); 91 | assert_eq!(rendered, check); 92 | let restored: T = crate::from_str(&rendered).expect("deserialization failed"); 93 | assert_eq!(&restored, value); 94 | } 95 | 96 | #[track_caller] 97 | fn roundtrip_implicit_map Deserialize<'de> + PartialEq>( 98 | value: &T, 99 | check: &str, 100 | ) { 101 | let rendered = crate::ser::Config::pretty() 102 | .implicit_map_at_root(true) 103 | .serialize(value) 104 | .expect("no errors"); 105 | println!("{rendered}"); 106 | assert_eq!(rendered, check); 107 | let restored: T = crate::parser::Config::default() 108 | .allow_implicit_map_at_root(true) 109 | .deserialize(&rendered) 110 | .expect("deserialization failed"); 111 | assert_eq!(&restored, value); 112 | } 113 | 114 | #[track_caller] 115 | fn roundtrip_anonymous_structs Deserialize<'de> + PartialEq>( 116 | value: &T, 117 | check: &str, 118 | ) { 119 | let rendered = crate::ser::Config::new() 120 | .anonymous_structs(true) 121 | .serialize(value) 122 | .expect("no errors"); 123 | println!("{rendered}"); 124 | assert_eq!(rendered, check); 125 | let restored: T = crate::parser::Config::default() 126 | .allow_implicit_map_at_root(true) 127 | .deserialize(&rendered) 128 | .expect("deserialization failed"); 129 | assert_eq!(&restored, value); 130 | } 131 | 132 | #[test] 133 | fn struct_of_everything() { 134 | roundtrip(&StructOfEverything::default(), "StructOfEverything{str:\"\",bytes:b\"\",char:'\\0',u8:0,u16:0,u32:0,u64:0,u128:0,usize:0,i8:0,i16:0,i32:0,i64:0,i128:0,isize:0,bool:false}"); 135 | roundtrip(&StructOfEverything::min(), "StructOfEverything{str:\"\\0\",bytes:b\"\\0\",char:'\\0',u8:0,u16:0,u32:0,u64:0,u128:0,usize:0,i8:-128,i16:-32768,i32:-2147483648,i64:-9223372036854775808,i128:-9223372036854775808,isize:-9223372036854775808,bool:false}"); 136 | roundtrip(&StructOfEverything::max(), "StructOfEverything{str:\"hello 🦀\",bytes:b\"hello, world\",char:'🦀',u8:255,u16:65535,u32:4294967295,u64:18446744073709551615,u128:18446744073709551615,usize:18446744073709551615,i8:127,i16:32767,i32:2147483647,i64:9223372036854775807,i128:9223372036854775807,isize:9223372036854775807,bool:true}"); 137 | } 138 | 139 | #[test] 140 | fn struct_of_everything_pretty() { 141 | roundtrip_pretty(&StructOfEverything::default(), "StructOfEverything {\n str: \"\",\n bytes: b\"\",\n char: '\\0',\n u8: 0,\n u16: 0,\n u32: 0,\n u64: 0,\n u128: 0,\n usize: 0,\n i8: 0,\n i16: 0,\n i32: 0,\n i64: 0,\n i128: 0,\n isize: 0,\n bool: false\n}"); 142 | roundtrip_pretty(&StructOfEverything::min(), "StructOfEverything {\n str: \"\\0\",\n bytes: b\"\\0\",\n char: '\\0',\n u8: 0,\n u16: 0,\n u32: 0,\n u64: 0,\n u128: 0,\n usize: 0,\n i8: -128,\n i16: -32768,\n i32: -2147483648,\n i64: -9223372036854775808,\n i128: -9223372036854775808,\n isize: -9223372036854775808,\n bool: false\n}"); 143 | roundtrip_pretty(&StructOfEverything::max(), "StructOfEverything {\n str: \"hello 🦀\",\n bytes: b\"hello, world\",\n char: '🦀',\n u8: 255,\n u16: 65535,\n u32: 4294967295,\n u64: 18446744073709551615,\n u128: 18446744073709551615,\n usize: 18446744073709551615,\n i8: 127,\n i16: 32767,\n i32: 2147483647,\n i64: 9223372036854775807,\n i128: 9223372036854775807,\n isize: 9223372036854775807,\n bool: true\n}"); 144 | } 145 | 146 | #[test] 147 | fn struct_of_everything_implicit() { 148 | roundtrip_implicit_map(&StructOfEverything::default(), "str: \"\"\nbytes: b\"\"\nchar: '\\0'\nu8: 0\nu16: 0\nu32: 0\nu64: 0\nu128: 0\nusize: 0\ni8: 0\ni16: 0\ni32: 0\ni64: 0\ni128: 0\nisize: 0\nbool: false\n"); 149 | roundtrip_implicit_map(&StructOfEverything::min(), "str: \"\\0\"\nbytes: b\"\\0\"\nchar: '\\0'\nu8: 0\nu16: 0\nu32: 0\nu64: 0\nu128: 0\nusize: 0\ni8: -128\ni16: -32768\ni32: -2147483648\ni64: -9223372036854775808\ni128: -9223372036854775808\nisize: -9223372036854775808\nbool: false\n"); 150 | roundtrip_implicit_map(&StructOfEverything::max(), "str: \"hello 🦀\"\nbytes: b\"hello, world\"\nchar: '🦀'\nu8: 255\nu16: 65535\nu32: 4294967295\nu64: 18446744073709551615\nu128: 18446744073709551615\nusize: 18446744073709551615\ni8: 127\ni16: 32767\ni32: 2147483647\ni64: 9223372036854775807\ni128: 9223372036854775807\nisize: 9223372036854775807\nbool: true\n"); 151 | } 152 | 153 | #[test] 154 | fn struct_of_everything_anonymous() { 155 | roundtrip_anonymous_structs(&StructOfEverything::default(), "{str:\"\",bytes:b\"\",char:'\\0',u8:0,u16:0,u32:0,u64:0,u128:0,usize:0,i8:0,i16:0,i32:0,i64:0,i128:0,isize:0,bool:false}"); 156 | roundtrip_anonymous_structs(&StructOfEverything::min(), "{str:\"\\0\",bytes:b\"\\0\",char:'\\0',u8:0,u16:0,u32:0,u64:0,u128:0,usize:0,i8:-128,i16:-32768,i32:-2147483648,i64:-9223372036854775808,i128:-9223372036854775808,isize:-9223372036854775808,bool:false}"); 157 | roundtrip_anonymous_structs(&StructOfEverything::max(), "{str:\"hello 🦀\",bytes:b\"hello, world\",char:'🦀',u8:255,u16:65535,u32:4294967295,u64:18446744073709551615,u128:18446744073709551615,usize:18446744073709551615,i8:127,i16:32767,i32:2147483647,i64:9223372036854775807,i128:9223372036854775807,isize:9223372036854775807,bool:true}"); 158 | } 159 | 160 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 161 | #[serde(untagged)] 162 | enum UntaggedEnum { 163 | Simple(SimpleStruct), 164 | NewtypeBool(NewtypeBool), 165 | Unit(UnitStruct), 166 | } 167 | 168 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 169 | enum TaggedEnum { 170 | Tuple(bool, bool), 171 | Struct { a: u64 }, 172 | NewtypeStruct(SimpleStruct), 173 | NewtypeTuple(SimpleTuple), 174 | NewtypeBool(bool), 175 | Unit, 176 | } 177 | 178 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 179 | struct SimpleStruct { 180 | a: u64, 181 | } 182 | 183 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 184 | struct SimpleTuple(u64, bool); 185 | 186 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 187 | struct NewtypeBool(bool); 188 | 189 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 190 | struct UnitStruct; 191 | 192 | #[test] 193 | fn deserialize_any() { 194 | let untagged: UntaggedEnum = crate::from_str("()").unwrap(); 195 | assert_eq!(untagged, UntaggedEnum::Unit(UnitStruct)); 196 | let untagged: UntaggedEnum = crate::from_str("true").unwrap(); 197 | assert_eq!(untagged, UntaggedEnum::NewtypeBool(NewtypeBool(true))); 198 | 199 | let untagged: UntaggedEnum = crate::from_str("{a:0}").unwrap(); 200 | assert_eq!(untagged, UntaggedEnum::Simple(SimpleStruct { a: 0 })); 201 | // Serde doesn't support tagged in an untagged context, which makes sense 202 | // given what it's named. We can't pass the C to the visitor without causing 203 | // an error within deserialize_any() or causing it to think we're 204 | // deserializing only a string. 205 | let untagged: UntaggedEnum = crate::from_str("C{a:0}").unwrap(); 206 | assert_eq!(untagged, UntaggedEnum::Simple(SimpleStruct { a: 0 })); 207 | 208 | // Some and None are special cases 209 | let untagged: Option = crate::from_str("None").unwrap(); 210 | assert_eq!(untagged, None); 211 | let untagged: Option = crate::from_str("Some(())").unwrap(); 212 | assert_eq!(untagged, Some(UntaggedEnum::Unit(UnitStruct))); 213 | } 214 | 215 | #[test] 216 | fn deserialize_tagged() { 217 | let tagged: TaggedEnum = crate::from_str("Tuple (true, false)").unwrap(); 218 | assert_eq!(tagged, TaggedEnum::Tuple(true, false)); 219 | let tagged: TaggedEnum = crate::from_str("Struct {a: 1}").unwrap(); 220 | assert_eq!(tagged, TaggedEnum::Struct { a: 1 }); 221 | 222 | let tagged: TaggedEnum = crate::from_str("NewtypeStruct {a: 1}").unwrap(); 223 | assert_eq!(tagged, TaggedEnum::NewtypeStruct(SimpleStruct { a: 1 })); 224 | let tagged: TaggedEnum = crate::from_str("NewtypeStruct({a: 1})").unwrap(); 225 | assert_eq!(tagged, TaggedEnum::NewtypeStruct(SimpleStruct { a: 1 })); 226 | let tagged: TaggedEnum = crate::from_str("NewtypeTuple(1, false)").unwrap(); 227 | assert_eq!(tagged, TaggedEnum::NewtypeTuple(SimpleTuple(1, false))); 228 | let tagged: TaggedEnum = crate::from_str("NewtypeTuple((1, false))").unwrap(); 229 | assert_eq!(tagged, TaggedEnum::NewtypeTuple(SimpleTuple(1, false))); 230 | let tagged: TaggedEnum = crate::from_str("NewtypeBool(true)").unwrap(); 231 | assert_eq!(tagged, TaggedEnum::NewtypeBool(true)); 232 | let tagged: TaggedEnum = crate::from_str("Unit").unwrap(); 233 | assert_eq!(tagged, TaggedEnum::Unit); 234 | } 235 | 236 | #[test] 237 | fn value_from_serialize() { 238 | let original = StructOfEverything::default(); 239 | let value = dbg!(Value::from_serialize(&original).unwrap()); 240 | let from_value: StructOfEverything = value.to_deserialize().unwrap(); 241 | assert_eq!(original, from_value); 242 | } 243 | 244 | #[test] 245 | fn implicit_btree_map() { 246 | roundtrip_implicit_map( 247 | &BTreeMap::from([(String::from("Hello"), 1), (String::from("World"), 2)]), 248 | "\"Hello\": 1\n\"World\": 2\n", 249 | ); 250 | } 251 | -------------------------------------------------------------------------------- /src/tokenizer/char_iterator.rs: -------------------------------------------------------------------------------- 1 | use core::iter::Peekable; 2 | use core::ops::Range; 3 | use core::str::CharIndices; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct CharIterator<'a> { 7 | pub source: &'a str, 8 | chars: Peekable>, 9 | last: Option<(usize, char)>, 10 | marked_start: usize, 11 | } 12 | 13 | impl<'a> CharIterator<'a> { 14 | #[inline] 15 | pub fn new(source: &'a str) -> Self { 16 | Self { 17 | source, 18 | chars: source.char_indices().peekable(), 19 | last: None, 20 | marked_start: 0, 21 | } 22 | } 23 | 24 | #[inline] 25 | pub const fn last_offset(&self) -> usize { 26 | if let Some((offset, _)) = self.last { 27 | offset 28 | } else { 29 | 0 30 | } 31 | } 32 | 33 | #[inline] 34 | pub const fn current_offset(&self) -> usize { 35 | if let Some((offset, ch)) = self.last { 36 | offset + ch.len_utf8() 37 | } else { 38 | 0 39 | } 40 | } 41 | 42 | #[inline] 43 | pub fn mark_start(&mut self) { 44 | self.marked_start = self.current_offset(); 45 | } 46 | 47 | #[inline] 48 | pub const fn marked_range(&self) -> Range { 49 | self.marked_start..self.current_offset() 50 | } 51 | 52 | pub fn marked_str(&self) -> &'a str { 53 | &self.source[self.marked_range()] 54 | } 55 | 56 | #[inline] 57 | pub const fn last_char_range(&self) -> Range { 58 | if let Some((offset, ch)) = self.last { 59 | offset..(offset + ch.len_utf8()) 60 | } else { 61 | 0..0 62 | } 63 | } 64 | 65 | #[inline] 66 | pub fn next_char_and_index(&mut self) -> Option<(usize, char)> { 67 | let current = self.chars.next()?; 68 | self.last = Some(current); 69 | Some(current) 70 | } 71 | 72 | #[inline] 73 | pub fn peek(&mut self) -> Option { 74 | self.peek_full().map(|(_, ch)| ch) 75 | } 76 | 77 | #[inline] 78 | pub fn peek_full(&mut self) -> Option<(usize, char)> { 79 | self.chars.peek().copied() 80 | } 81 | } 82 | 83 | impl<'a> Iterator for CharIterator<'a> { 84 | type Item = char; 85 | 86 | #[inline] 87 | fn next(&mut self) -> Option { 88 | self.next_char_and_index().map(|(_, ch)| ch) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/value.rs: -------------------------------------------------------------------------------- 1 | use alloc::borrow::Cow; 2 | use alloc::vec::Vec; 3 | use core::fmt::Display; 4 | use core::str::{self, FromStr}; 5 | 6 | use crate::parser::{Config, Error, ErrorKind, Event, EventKind, Name, Nested, Parser, Primitive}; 7 | use crate::tokenizer::Integer; 8 | use crate::writer::{self, Writer}; 9 | 10 | /// A value with a static lifetime. 11 | pub type OwnedValue = Value<'static>; 12 | 13 | /// A value representable by Rsn. 14 | #[derive(Debug, Clone, PartialEq)] 15 | pub enum Value<'a> { 16 | /// An integer. 17 | Integer(Integer), 18 | /// A floating point number. 19 | Float(f64), 20 | /// A boolean. 21 | Bool(bool), 22 | /// A character. 23 | Char(char), 24 | /// A byte. 25 | Byte(u8), 26 | /// An identifier (name). 27 | Identifier(Cow<'a, str>), 28 | /// A string. 29 | String(Cow<'a, str>), 30 | /// A byte string. 31 | Bytes(Cow<'a, [u8]>), 32 | /// A named structure. 33 | Named(Named<'a>), 34 | /// A tuple of values. 35 | Tuple(List<'a>), 36 | /// An array of values. 37 | Array(List<'a>), 38 | /// A collection of key-value pairs. 39 | Map(Map<'a>), 40 | } 41 | 42 | macro_rules! as_integer { 43 | ($name:ident, $ty:ty) => { 44 | /// Returns this value as a 45 | #[doc = stringify!($type)] 46 | /// if the value is an integer that can fit in a 47 | #[doc = stringify!($type)] 48 | #[must_use] 49 | pub fn $name(&self) -> Option<$ty> { 50 | let Self::Integer(value) = self else { 51 | return None; 52 | }; 53 | 54 | value.$name() 55 | } 56 | }; 57 | } 58 | 59 | impl<'a> Value<'a> { 60 | as_integer!(as_u8, u8); 61 | 62 | as_integer!(as_u16, u16); 63 | 64 | as_integer!(as_u32, u32); 65 | 66 | as_integer!(as_u64, u64); 67 | 68 | as_integer!(as_u128, u128); 69 | 70 | as_integer!(as_usize, usize); 71 | 72 | as_integer!(as_i8, i8); 73 | 74 | as_integer!(as_i16, i16); 75 | 76 | as_integer!(as_i32, i32); 77 | 78 | as_integer!(as_i64, i64); 79 | 80 | as_integer!(as_i128, i128); 81 | 82 | as_integer!(as_isize, isize); 83 | 84 | /// Parses `source` as a [`Value`]. 85 | /// 86 | /// # Errors 87 | /// 88 | /// Returns any error encountered while parsing `source`. 89 | pub fn from_str(source: &'a str, config: Config) -> Result { 90 | let mut parser = Parser::new(source, config.include_comments(false)); 91 | Self::parse(&mut parser) 92 | } 93 | 94 | /// Returns a value representing the unit type. 95 | #[must_use] 96 | pub const fn unit() -> Self { 97 | Self::Tuple(List::new()) 98 | } 99 | 100 | fn parse(parser: &mut Parser<'a>) -> Result { 101 | let event = parser.next().transpose()?.ok_or_else(|| { 102 | Error::new( 103 | parser.current_offset()..parser.current_offset(), 104 | ErrorKind::UnexpectedEof, 105 | ) 106 | })?; 107 | Self::from_parser_event(event, parser) 108 | } 109 | 110 | fn from_parser_event(event: Event<'a>, parser: &mut Parser<'a>) -> Result { 111 | match event.kind { 112 | EventKind::BeginNested { 113 | name, 114 | kind: kind @ (Nested::Tuple | Nested::List), 115 | } => Self::parse_sequence(name, parser, kind), 116 | EventKind::BeginNested { 117 | name, 118 | kind: Nested::Map, 119 | } => Self::parse_map(name, parser), 120 | EventKind::Primitive(primitive) => match primitive { 121 | Primitive::Bool(value) => Ok(Value::Bool(value)), 122 | Primitive::Integer(value) => Ok(Value::Integer(value)), 123 | Primitive::Float(value) => Ok(Value::Float(value)), 124 | Primitive::Char(value) => Ok(Value::Char(value)), 125 | Primitive::String(value) => Ok(Value::String(value)), 126 | Primitive::Identifier(value) => Ok(Value::Identifier(Cow::Borrowed(value))), 127 | Primitive::Bytes(value) => Ok(Value::Bytes(value)), 128 | }, 129 | EventKind::Comment(_) => unreachable!("disabled in parser"), 130 | EventKind::EndNested => unreachable!("Parser would error"), 131 | } 132 | } 133 | 134 | fn parse_sequence( 135 | name: Option>, 136 | parser: &mut Parser<'a>, 137 | kind: Nested, 138 | ) -> Result { 139 | let mut list = List::default(); 140 | loop { 141 | let event = parser.next().expect("will error or have another event")?; 142 | if matches!(event.kind, EventKind::EndNested) { 143 | if let Some(name) = name { 144 | return Ok(Self::Named(Named { 145 | name: Cow::Borrowed(name.name), 146 | contents: StructContents::Tuple(list), 147 | })); 148 | } 149 | 150 | match kind { 151 | Nested::List => return Ok(Self::Array(list)), 152 | Nested::Tuple => return Ok(Self::Tuple(list)), 153 | Nested::Map => unreachable!("parse_sequence isn't called on maps"), 154 | } 155 | } else { 156 | list.0.push(Self::from_parser_event(event, parser)?); 157 | } 158 | } 159 | } 160 | 161 | fn parse_map(name: Option>, parser: &mut Parser<'a>) -> Result { 162 | let mut map = Map::default(); 163 | loop { 164 | let event = parser.next().expect("will error or have another event")?; 165 | if matches!(event.kind, EventKind::EndNested) { 166 | if let Some(name) = name { 167 | return Ok(Self::Named(Named { 168 | name: Cow::Borrowed(name.name), 169 | contents: StructContents::Map(map), 170 | })); 171 | } 172 | 173 | return Ok(Self::Map(map)); 174 | } 175 | 176 | let key = Self::from_parser_event(event, parser)?; 177 | let value = Self::from_parser_event( 178 | parser.next().expect("will error or have another event")?, 179 | parser, 180 | )?; 181 | 182 | map.0.push((key, value)); 183 | } 184 | } 185 | 186 | /// Creates a value by serializing `value` using Serde. 187 | /// 188 | /// # Errors 189 | /// 190 | /// Returns an error if the `value` cannot be represented losslessly or if 191 | /// any errors occur from `Serializer` implementations. 192 | #[cfg(feature = "serde")] 193 | pub fn from_serialize(value: &S) -> Result { 194 | value.serialize(serde::ValueSerializer) 195 | } 196 | 197 | /// Deserializes `self` as `D` using Serde. 198 | /// 199 | /// # Errors 200 | /// 201 | /// Returns an error if `self` cannot be deserialized as `D`. 202 | #[cfg(feature = "serde")] 203 | pub fn to_deserialize>(&self) -> Result { 204 | D::deserialize(serde::ValueDeserializer(self)) 205 | } 206 | 207 | /// Returns the owned version of `self`, copying any borrowed data to the 208 | /// heap. 209 | #[must_use] 210 | pub fn into_owned(self) -> Value<'static> { 211 | match self { 212 | Value::Integer(value) => Value::Integer(value), 213 | Value::Float(value) => Value::Float(value), 214 | Value::Bool(value) => Value::Bool(value), 215 | Value::Char(value) => Value::Char(value), 216 | Value::Byte(value) => Value::Byte(value), 217 | Value::Identifier(value) => Value::Identifier(Cow::Owned(value.into_owned())), 218 | Value::String(value) => Value::String(Cow::Owned(value.into_owned())), 219 | Value::Bytes(value) => Value::Bytes(Cow::Owned(value.into_owned())), 220 | Value::Named(value) => Value::Named(value.into_owned()), 221 | Value::Tuple(value) => Value::Tuple(value.into_owned()), 222 | Value::Array(value) => Value::Array(value.into_owned()), 223 | Value::Map(value) => Value::Map(value.into_owned()), 224 | } 225 | } 226 | 227 | /// Returns this value as a floating point number. 228 | /// 229 | /// If this is an integer, this will cast the integer to an f64. 230 | #[must_use] 231 | pub fn as_f64(&self) -> Option { 232 | match self { 233 | Value::Integer(integer) => Some(integer.as_f64()), 234 | Value::Float(float) => Some(*float), 235 | _ => None, 236 | } 237 | } 238 | 239 | /// Returns this value as a str, if possible. 240 | /// 241 | /// `Identifier`, `String`, and `Bytes` bytes all can be returned from this 242 | /// function. 243 | #[must_use] 244 | pub fn as_str(&self) -> Option<&str> { 245 | match self { 246 | Value::Identifier(str) | Value::String(str) => Some(str), 247 | Value::Bytes(bytes) => str::from_utf8(bytes).ok(), 248 | _ => None, 249 | } 250 | } 251 | 252 | /// Returns the underlying bytes for this value, if it can be represented as 253 | /// a byte slice. 254 | /// 255 | /// `Identifier`, `String`, and `Bytes` bytes all can be returned from this 256 | /// function. 257 | #[must_use] 258 | pub fn as_bytes(&self) -> Option<&[u8]> { 259 | match self { 260 | Value::Identifier(str) | Value::String(str) => Some(str.as_bytes()), 261 | Value::Bytes(bytes) => Some(bytes), 262 | _ => None, 263 | } 264 | } 265 | } 266 | 267 | impl FromStr for Value<'static> { 268 | type Err = Error; 269 | 270 | fn from_str(s: &str) -> Result { 271 | Value::from_str(s, Config::default()).map(Value::into_owned) 272 | } 273 | } 274 | 275 | impl<'a> Display for Value<'a> { 276 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 277 | let mut writer = if f.alternate() { 278 | Writer::new( 279 | f, 280 | &writer::Config::Pretty { 281 | indentation: Cow::Borrowed(" "), 282 | newline: Cow::Borrowed("\n"), 283 | }, 284 | ) 285 | } else { 286 | Writer::new(f, &writer::Config::Compact) 287 | }; 288 | writer.write_value(self)?; 289 | writer.finish(); 290 | Ok(()) 291 | } 292 | } 293 | 294 | /// A named structure. 295 | #[derive(Debug, Clone, PartialEq)] 296 | pub struct Named<'a> { 297 | /// The name of the structure. 298 | pub name: Cow<'a, str>, 299 | /// The contents of the structure. 300 | pub contents: StructContents<'a>, 301 | } 302 | 303 | impl<'a> Named<'a> { 304 | /// Returns an owned representation of this name, copying to the stack if 305 | /// needed. 306 | #[must_use] 307 | pub fn into_owned(self) -> Named<'static> { 308 | Named { 309 | name: Cow::Owned(self.name.into_owned()), 310 | contents: self.contents.into_owned(), 311 | } 312 | } 313 | } 314 | 315 | /// The contents of a structure. 316 | #[derive(Debug, Clone, PartialEq)] 317 | pub enum StructContents<'a> { 318 | /// Named fields, represented as a map. 319 | Map(Map<'a>), 320 | /// A tuple of valuees. 321 | Tuple(List<'a>), 322 | } 323 | 324 | impl<'a> StructContents<'a> { 325 | /// Returns an owned representation, copying to the heap if needed. 326 | #[must_use] 327 | pub fn into_owned(self) -> StructContents<'static> { 328 | match self { 329 | StructContents::Map(contents) => StructContents::Map(contents.into_owned()), 330 | StructContents::Tuple(contents) => StructContents::Tuple(contents.into_owned()), 331 | } 332 | } 333 | } 334 | 335 | /// A list of key-value pairs. 336 | #[derive(Default, Debug, Clone, PartialEq)] 337 | pub struct Map<'a>(pub Vec<(Value<'a>, Value<'a>)>); 338 | 339 | impl<'a> Map<'a> { 340 | /// Returns an owned representation, copying to the heap if needed. 341 | #[must_use] 342 | pub fn into_owned(self) -> Map<'static> { 343 | Map(self 344 | .0 345 | .into_iter() 346 | .map(|(key, value)| (key.into_owned(), value.into_owned())) 347 | .collect()) 348 | } 349 | } 350 | 351 | /// A list of values. 352 | #[derive(Default, Debug, Clone, PartialEq)] 353 | pub struct List<'a>(pub Vec>); 354 | 355 | impl<'a> List<'a> { 356 | /// Returns an empty list. 357 | #[must_use] 358 | pub const fn new() -> Self { 359 | Self(Vec::new()) 360 | } 361 | 362 | /// Returns an owned representation, copying to the heap if needed. 363 | #[must_use] 364 | pub fn into_owned(self) -> List<'static> { 365 | List(self.0.into_iter().map(Value::into_owned).collect()) 366 | } 367 | } 368 | 369 | #[cfg(feature = "serde")] 370 | mod serde { 371 | use alloc::borrow::Cow; 372 | use alloc::string::{String, ToString}; 373 | use alloc::{slice, vec}; 374 | use core::fmt::Display; 375 | use core::num::TryFromIntError; 376 | use core::str::{self, Utf8Error}; 377 | 378 | use serde::de::{EnumAccess, MapAccess, SeqAccess, VariantAccess}; 379 | use serde::ser::{ 380 | SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, 381 | SerializeTupleStruct, SerializeTupleVariant, 382 | }; 383 | use serde::{Deserializer, Serializer}; 384 | 385 | use super::{List, StructContents}; 386 | use crate::parser::Nested; 387 | use crate::tokenizer::Integer; 388 | use crate::value::{Map, Named, OwnedValue, Value}; 389 | 390 | pub struct ValueSerializer; 391 | 392 | impl Serializer for ValueSerializer { 393 | type Error = ToValueError; 394 | type Ok = OwnedValue; 395 | type SerializeMap = MapSerializer; 396 | type SerializeSeq = SequenceSerializer; 397 | type SerializeStruct = MapSerializer; 398 | type SerializeStructVariant = MapSerializer; 399 | type SerializeTuple = SequenceSerializer; 400 | type SerializeTupleStruct = SequenceSerializer; 401 | type SerializeTupleVariant = SequenceSerializer; 402 | 403 | fn serialize_bool(self, v: bool) -> Result { 404 | Ok(Value::Bool(v)) 405 | } 406 | 407 | fn serialize_i8(self, v: i8) -> Result { 408 | Ok(Value::Integer(Integer::from(v))) 409 | } 410 | 411 | fn serialize_i16(self, v: i16) -> Result { 412 | Ok(Value::Integer(Integer::from(v))) 413 | } 414 | 415 | fn serialize_i32(self, v: i32) -> Result { 416 | Ok(Value::Integer(Integer::from(v))) 417 | } 418 | 419 | fn serialize_i64(self, v: i64) -> Result { 420 | Ok(Value::Integer(Integer::from(v))) 421 | } 422 | 423 | fn serialize_u8(self, v: u8) -> Result { 424 | Ok(Value::Integer(Integer::from(v))) 425 | } 426 | 427 | fn serialize_u16(self, v: u16) -> Result { 428 | Ok(Value::Integer(Integer::from(v))) 429 | } 430 | 431 | fn serialize_u32(self, v: u32) -> Result { 432 | Ok(Value::Integer(Integer::from(v))) 433 | } 434 | 435 | fn serialize_u64(self, v: u64) -> Result { 436 | Ok(Value::Integer(Integer::from(v))) 437 | } 438 | 439 | fn serialize_f32(self, v: f32) -> Result { 440 | Ok(Value::Float(f64::from(v))) 441 | } 442 | 443 | fn serialize_f64(self, v: f64) -> Result { 444 | Ok(Value::Float(v)) 445 | } 446 | 447 | fn serialize_char(self, v: char) -> Result { 448 | Ok(Value::Char(v)) 449 | } 450 | 451 | fn serialize_str(self, v: &str) -> Result { 452 | Ok(Value::String(Cow::Owned(v.to_string()))) 453 | } 454 | 455 | fn serialize_bytes(self, v: &[u8]) -> Result { 456 | Ok(Value::Bytes(Cow::Owned(v.to_vec()))) 457 | } 458 | 459 | fn serialize_none(self) -> Result { 460 | Ok(Value::Identifier(Cow::Borrowed("None"))) 461 | } 462 | 463 | fn serialize_some(self, value: &T) -> Result 464 | where 465 | T: serde::Serialize + ?Sized, 466 | { 467 | Ok(Value::Named(Named { 468 | name: Cow::Borrowed("Some"), 469 | contents: StructContents::Tuple(List(vec![value.serialize(ValueSerializer)?])), 470 | })) 471 | } 472 | 473 | fn serialize_unit(self) -> Result { 474 | Ok(Value::Tuple(List::default())) 475 | } 476 | 477 | fn serialize_unit_struct(self, name: &'static str) -> Result { 478 | Ok(Value::Identifier(Cow::Owned(name.to_string()))) 479 | } 480 | 481 | fn serialize_unit_variant( 482 | self, 483 | _name: &'static str, 484 | _variant_index: u32, 485 | variant: &'static str, 486 | ) -> Result { 487 | Ok(Value::Identifier(Cow::Owned(variant.to_string()))) 488 | } 489 | 490 | fn serialize_newtype_struct( 491 | self, 492 | name: &'static str, 493 | value: &T, 494 | ) -> Result 495 | where 496 | T: serde::Serialize + ?Sized, 497 | { 498 | Ok(Value::Named(Named { 499 | name: Cow::Owned(name.to_string()), 500 | contents: StructContents::Tuple(List(vec![value.serialize(ValueSerializer)?])), 501 | })) 502 | } 503 | 504 | fn serialize_newtype_variant( 505 | self, 506 | _name: &'static str, 507 | _variant_index: u32, 508 | variant: &'static str, 509 | value: &T, 510 | ) -> Result 511 | where 512 | T: serde::Serialize + ?Sized, 513 | { 514 | Ok(Value::Named(Named { 515 | name: Cow::Owned(variant.to_string()), 516 | contents: StructContents::Tuple(List(vec![value.serialize(ValueSerializer)?])), 517 | })) 518 | } 519 | 520 | fn serialize_seq(self, _len: Option) -> Result { 521 | Ok(SequenceSerializer::new(None, Nested::List)) 522 | } 523 | 524 | fn serialize_tuple(self, _len: usize) -> Result { 525 | Ok(SequenceSerializer::new(None, Nested::Tuple)) 526 | } 527 | 528 | fn serialize_tuple_struct( 529 | self, 530 | name: &'static str, 531 | _len: usize, 532 | ) -> Result { 533 | Ok(SequenceSerializer::new( 534 | Some(name.to_string()), 535 | Nested::Tuple, 536 | )) 537 | } 538 | 539 | fn serialize_tuple_variant( 540 | self, 541 | _name: &'static str, 542 | _variant_index: u32, 543 | variant: &'static str, 544 | _len: usize, 545 | ) -> Result { 546 | Ok(SequenceSerializer::new( 547 | Some(variant.to_string()), 548 | Nested::Tuple, 549 | )) 550 | } 551 | 552 | fn serialize_map(self, _len: Option) -> Result { 553 | Ok(MapSerializer { 554 | name: None, 555 | contents: Map::default(), 556 | }) 557 | } 558 | 559 | fn serialize_struct( 560 | self, 561 | name: &'static str, 562 | _len: usize, 563 | ) -> Result { 564 | Ok(MapSerializer { 565 | name: Some(name.to_string()), 566 | contents: Map::default(), 567 | }) 568 | } 569 | 570 | fn serialize_struct_variant( 571 | self, 572 | _name: &'static str, 573 | _variant_index: u32, 574 | variant: &'static str, 575 | _len: usize, 576 | ) -> Result { 577 | Ok(MapSerializer { 578 | name: Some(variant.to_string()), 579 | contents: Map::default(), 580 | }) 581 | } 582 | 583 | fn serialize_i128(self, v: i128) -> Result { 584 | Ok(Value::Integer(Integer::try_from(v)?)) 585 | } 586 | 587 | fn serialize_u128(self, v: u128) -> Result { 588 | Ok(Value::Integer(Integer::try_from(v)?)) 589 | } 590 | } 591 | 592 | pub struct SequenceSerializer { 593 | name: Option, 594 | kind: Nested, 595 | contents: List<'static>, 596 | } 597 | 598 | impl SequenceSerializer { 599 | pub fn new(name: Option, kind: Nested) -> Self { 600 | Self { 601 | name, 602 | kind, 603 | contents: List::default(), 604 | } 605 | } 606 | } 607 | 608 | impl SerializeSeq for SequenceSerializer { 609 | type Error = ToValueError; 610 | type Ok = OwnedValue; 611 | 612 | fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> 613 | where 614 | T: serde::Serialize + ?Sized, 615 | { 616 | self.contents.0.push(value.serialize(ValueSerializer)?); 617 | Ok(()) 618 | } 619 | 620 | fn end(self) -> Result { 621 | match (self.name, self.kind) { 622 | (Some(name), Nested::List | Nested::Tuple) => Ok(Value::Named(Named { 623 | name: Cow::Owned(name), 624 | contents: StructContents::Tuple(self.contents), 625 | })), 626 | (None, Nested::List) => Ok(Value::Array(self.contents)), 627 | (None, Nested::Tuple) => Ok(Value::Tuple(self.contents)), 628 | (_, Nested::Map) => unreachable!(), 629 | } 630 | } 631 | } 632 | 633 | impl SerializeTuple for SequenceSerializer { 634 | type Error = ToValueError; 635 | type Ok = OwnedValue; 636 | 637 | fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> 638 | where 639 | T: serde::Serialize + ?Sized, 640 | { 641 | SerializeSeq::serialize_element(self, value) 642 | } 643 | 644 | fn end(self) -> Result { 645 | SerializeSeq::end(self) 646 | } 647 | } 648 | 649 | impl SerializeTupleStruct for SequenceSerializer { 650 | type Error = ToValueError; 651 | type Ok = OwnedValue; 652 | 653 | fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> 654 | where 655 | T: serde::Serialize + ?Sized, 656 | { 657 | SerializeSeq::serialize_element(self, value) 658 | } 659 | 660 | fn end(self) -> Result { 661 | SerializeSeq::end(self) 662 | } 663 | } 664 | 665 | impl SerializeTupleVariant for SequenceSerializer { 666 | type Error = ToValueError; 667 | type Ok = OwnedValue; 668 | 669 | fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> 670 | where 671 | T: serde::Serialize + ?Sized, 672 | { 673 | SerializeSeq::serialize_element(self, value) 674 | } 675 | 676 | fn end(self) -> Result { 677 | SerializeSeq::end(self) 678 | } 679 | } 680 | 681 | #[derive(Default)] 682 | pub struct MapSerializer { 683 | name: Option, 684 | contents: Map<'static>, 685 | } 686 | 687 | impl SerializeMap for MapSerializer { 688 | type Error = ToValueError; 689 | type Ok = OwnedValue; 690 | 691 | fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> 692 | where 693 | T: serde::Serialize + ?Sized, 694 | { 695 | self.contents.0.push(( 696 | key.serialize(ValueSerializer)?, 697 | Value::Tuple(List::default()), 698 | )); 699 | Ok(()) 700 | } 701 | 702 | fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> 703 | where 704 | T: serde::Serialize + ?Sized, 705 | { 706 | self.contents 707 | .0 708 | .last_mut() 709 | .expect("serialize_key not called") 710 | .1 = value.serialize(ValueSerializer)?; 711 | Ok(()) 712 | } 713 | 714 | fn end(self) -> Result { 715 | if let Some(name) = self.name { 716 | Ok(Value::Named(Named { 717 | name: Cow::Owned(name), 718 | contents: StructContents::Map(self.contents), 719 | })) 720 | } else { 721 | Ok(Value::Map(self.contents)) 722 | } 723 | } 724 | } 725 | 726 | impl SerializeStruct for MapSerializer { 727 | type Error = ToValueError; 728 | type Ok = OwnedValue; 729 | 730 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> 731 | where 732 | T: serde::Serialize + ?Sized, 733 | { 734 | SerializeMap::serialize_key(self, key)?; 735 | SerializeMap::serialize_value(self, value) 736 | } 737 | 738 | fn end(self) -> Result { 739 | SerializeMap::end(self) 740 | } 741 | } 742 | 743 | impl SerializeStructVariant for MapSerializer { 744 | type Error = ToValueError; 745 | type Ok = OwnedValue; 746 | 747 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> 748 | where 749 | T: serde::Serialize + ?Sized, 750 | { 751 | SerializeMap::serialize_key(self, key)?; 752 | SerializeMap::serialize_value(self, value) 753 | } 754 | 755 | fn end(self) -> Result { 756 | SerializeMap::end(self) 757 | } 758 | } 759 | 760 | #[derive(Clone)] 761 | pub struct ValueDeserializer<'a, 'de>(pub &'a Value<'de>); 762 | 763 | macro_rules! deserialize_int { 764 | ($deserialize_name:ident, $as_name:ident, $visit_name:ident, $expected:expr) => { 765 | fn $deserialize_name(self, visitor: V) -> Result 766 | where 767 | V: serde::de::Visitor<'de>, 768 | { 769 | if let Some(value) = self.0.$as_name() { 770 | visitor.$visit_name(value) 771 | } else { 772 | Err(FromValueError::Expected($expected)) 773 | } 774 | } 775 | }; 776 | } 777 | 778 | impl<'a, 'de> Deserializer<'de> for ValueDeserializer<'a, 'de> { 779 | type Error = FromValueError; 780 | 781 | deserialize_int!(deserialize_i8, as_i8, visit_i8, ExpectedKind::I8); 782 | 783 | deserialize_int!(deserialize_i16, as_i16, visit_i16, ExpectedKind::I16); 784 | 785 | deserialize_int!(deserialize_i32, as_i32, visit_i32, ExpectedKind::I32); 786 | 787 | deserialize_int!(deserialize_i64, as_i64, visit_i64, ExpectedKind::I64); 788 | 789 | deserialize_int!(deserialize_i128, as_i128, visit_i128, ExpectedKind::I128); 790 | 791 | deserialize_int!(deserialize_u8, as_u8, visit_u8, ExpectedKind::U8); 792 | 793 | deserialize_int!(deserialize_u16, as_u16, visit_u16, ExpectedKind::U16); 794 | 795 | deserialize_int!(deserialize_u32, as_u32, visit_u32, ExpectedKind::U32); 796 | 797 | deserialize_int!(deserialize_u64, as_u64, visit_u64, ExpectedKind::U64); 798 | 799 | deserialize_int!(deserialize_u128, as_u128, visit_u128, ExpectedKind::U128); 800 | 801 | deserialize_int!(deserialize_f64, as_f64, visit_f64, ExpectedKind::Float); 802 | 803 | #[allow(clippy::cast_possible_truncation)] 804 | fn deserialize_any(self, visitor: V) -> Result 805 | where 806 | V: serde::de::Visitor<'de>, 807 | { 808 | match &self.0 { 809 | Value::Integer(value) => match *value { 810 | Integer::Usize(usize) => match usize::BITS { 811 | 0..=16 => visitor.visit_u16(usize as u16), 812 | 17..=32 => visitor.visit_u32(usize as u32), 813 | 33..=64 => visitor.visit_u64(usize as u64), 814 | 65..=128 => visitor.visit_u128(usize as u128), 815 | _ => unreachable!("unsupported pointer width"), 816 | }, 817 | Integer::Isize(isize) => match usize::BITS { 818 | 0..=16 => visitor.visit_i16(isize as i16), 819 | 17..=32 => visitor.visit_i32(isize as i32), 820 | 33..=64 => visitor.visit_i64(isize as i64), 821 | 65..=128 => visitor.visit_i128(isize as i128), 822 | _ => unreachable!("unsupported pointer width"), 823 | }, 824 | #[cfg(feature = "integer128")] 825 | Integer::UnsignedLarge(large) => visitor.visit_u128(large), 826 | #[cfg(not(feature = "integer128"))] 827 | Integer::UnsignedLarge(large) => visitor.visit_u64(large), 828 | #[cfg(feature = "integer128")] 829 | Integer::SignedLarge(large) => visitor.visit_i128(large), 830 | #[cfg(not(feature = "integer128"))] 831 | Integer::SignedLarge(large) => visitor.visit_i64(large), 832 | }, 833 | Value::Float(value) => visitor.visit_f64(*value), 834 | Value::Bool(value) => visitor.visit_bool(*value), 835 | Value::Char(value) => visitor.visit_char(*value), 836 | Value::Byte(value) => visitor.visit_u8(*value), 837 | Value::Identifier(value) => match value { 838 | Cow::Borrowed(str) => visitor.visit_borrowed_str(str), 839 | Cow::Owned(str) => visitor.visit_str(str), 840 | }, 841 | Value::String(value) => visitor.visit_str(value), 842 | Value::Bytes(value) => match value { 843 | Cow::Borrowed(bytes) => visitor.visit_borrowed_bytes(bytes), 844 | Cow::Owned(bytes) => visitor.visit_bytes(bytes), 845 | }, 846 | Value::Named(value) => match &value.contents { 847 | StructContents::Map(map) => visitor.visit_map(MapDeserializer::new(map)), 848 | StructContents::Tuple(list) => { 849 | visitor.visit_seq(SequenceDeserializer(list.0.iter())) 850 | } 851 | }, 852 | Value::Array(list) | Value::Tuple(list) => { 853 | visitor.visit_seq(SequenceDeserializer(list.0.iter())) 854 | } 855 | Value::Map(map) => visitor.visit_map(MapDeserializer::new(map)), 856 | } 857 | } 858 | 859 | fn deserialize_bool(self, visitor: V) -> Result 860 | where 861 | V: serde::de::Visitor<'de>, 862 | { 863 | match &self.0 { 864 | Value::Integer(int) => visitor.visit_bool(!int.is_zero()), 865 | Value::Bool(bool) => visitor.visit_bool(*bool), 866 | _ => Err(FromValueError::Expected(ExpectedKind::Bool)), 867 | } 868 | } 869 | 870 | #[allow(clippy::cast_possible_truncation)] 871 | fn deserialize_f32(self, visitor: V) -> Result 872 | where 873 | V: serde::de::Visitor<'de>, 874 | { 875 | if let Some(value) = self.0.as_f64() { 876 | visitor.visit_f32(value as f32) 877 | } else { 878 | Err(FromValueError::Expected(ExpectedKind::Float)) 879 | } 880 | } 881 | 882 | fn deserialize_char(self, visitor: V) -> Result 883 | where 884 | V: serde::de::Visitor<'de>, 885 | { 886 | if let Value::Char(ch) = &self.0 { 887 | visitor.visit_char(*ch) 888 | } else { 889 | Err(FromValueError::Expected(ExpectedKind::Char)) 890 | } 891 | } 892 | 893 | fn deserialize_str(self, visitor: V) -> Result 894 | where 895 | V: serde::de::Visitor<'de>, 896 | { 897 | match &self.0 { 898 | Value::String(str) | Value::Identifier(str) => match str { 899 | Cow::Borrowed(str) => visitor.visit_borrowed_str(str), 900 | Cow::Owned(str) => visitor.visit_str(str), 901 | }, 902 | Value::Bytes(bytes) => match bytes { 903 | Cow::Borrowed(bytes) => { 904 | let str = str::from_utf8(bytes)?; 905 | visitor.visit_borrowed_str(str) 906 | } 907 | Cow::Owned(bytes) => { 908 | let str = str::from_utf8(bytes)?; 909 | visitor.visit_str(str) 910 | } 911 | }, 912 | Value::Named(name) => match &name.name { 913 | Cow::Borrowed(str) => visitor.visit_borrowed_str(str), 914 | Cow::Owned(str) => visitor.visit_str(str), 915 | }, 916 | _ => Err(FromValueError::Expected(ExpectedKind::String)), 917 | } 918 | } 919 | 920 | fn deserialize_string(self, visitor: V) -> Result 921 | where 922 | V: serde::de::Visitor<'de>, 923 | { 924 | self.deserialize_str(visitor) 925 | } 926 | 927 | fn deserialize_bytes(self, visitor: V) -> Result 928 | where 929 | V: serde::de::Visitor<'de>, 930 | { 931 | match &self.0 { 932 | Value::Bytes(bytes) => match bytes { 933 | Cow::Borrowed(bytes) => visitor.visit_borrowed_bytes(bytes), 934 | Cow::Owned(bytes) => visitor.visit_bytes(bytes), 935 | }, 936 | Value::String(str) | Value::Identifier(str) => match str { 937 | Cow::Borrowed(str) => visitor.visit_borrowed_bytes(str.as_bytes()), 938 | Cow::Owned(str) => visitor.visit_bytes(str.as_bytes()), 939 | }, 940 | _ => Err(FromValueError::Expected(ExpectedKind::Bytes)), 941 | } 942 | } 943 | 944 | fn deserialize_byte_buf(self, visitor: V) -> Result 945 | where 946 | V: serde::de::Visitor<'de>, 947 | { 948 | self.deserialize_bytes(visitor) 949 | } 950 | 951 | fn deserialize_option(self, visitor: V) -> Result 952 | where 953 | V: serde::de::Visitor<'de>, 954 | { 955 | match &self.0 { 956 | Value::String(name) | Value::Identifier(name) if name == "None" => { 957 | visitor.visit_none() 958 | } 959 | Value::Named(Named { 960 | name, 961 | contents: StructContents::Tuple(list), 962 | }) if name == "Some" && !list.0.is_empty() => { 963 | visitor.visit_some(ValueDeserializer(list.0.first().expect("length checked"))) 964 | } 965 | // To support changing a T to Option, we can fuzzily allow a 966 | // value to be treated as Some(). TODO should this be optional? 967 | _ => visitor.visit_some(self), 968 | } 969 | } 970 | 971 | fn deserialize_unit(self, visitor: V) -> Result 972 | where 973 | V: serde::de::Visitor<'de>, 974 | { 975 | match &self.0 { 976 | Value::Named(Named { 977 | contents: StructContents::Tuple(_), 978 | .. 979 | }) 980 | | Value::Tuple(_) 981 | | Value::Array(_) => visitor.visit_unit(), 982 | _ => Err(FromValueError::Expected(ExpectedKind::Unit)), 983 | } 984 | } 985 | 986 | fn deserialize_unit_struct( 987 | self, 988 | _name: &'static str, 989 | visitor: V, 990 | ) -> Result 991 | where 992 | V: serde::de::Visitor<'de>, 993 | { 994 | self.deserialize_unit(visitor) 995 | } 996 | 997 | fn deserialize_newtype_struct( 998 | self, 999 | name: &'static str, 1000 | visitor: V, 1001 | ) -> Result 1002 | where 1003 | V: serde::de::Visitor<'de>, 1004 | { 1005 | match &self.0 { 1006 | Value::Named(named) if named.name == name => match &named.contents { 1007 | StructContents::Tuple(contents) => { 1008 | if let Some(first) = contents.0.first() { 1009 | visitor.visit_newtype_struct(ValueDeserializer(first)) 1010 | } else { 1011 | visitor.visit_newtype_struct(ValueDeserializer(&Value::unit())) 1012 | } 1013 | } 1014 | StructContents::Map(map) => { 1015 | if let Some((_, first)) = map.0.first() { 1016 | visitor.visit_newtype_struct(ValueDeserializer(first)) 1017 | } else { 1018 | visitor.visit_newtype_struct(ValueDeserializer(&Value::unit())) 1019 | } 1020 | } 1021 | }, 1022 | _ => visitor.visit_newtype_struct(self), 1023 | } 1024 | } 1025 | 1026 | fn deserialize_seq(self, visitor: V) -> Result 1027 | where 1028 | V: serde::de::Visitor<'de>, 1029 | { 1030 | match &self.0 { 1031 | Value::Named(Named { 1032 | contents: StructContents::Tuple(list), 1033 | .. 1034 | }) 1035 | | Value::Tuple(list) 1036 | | Value::Array(list) => visitor.visit_seq(SequenceDeserializer(list.0.iter())), 1037 | _ => Err(FromValueError::Expected(ExpectedKind::Sequence)), 1038 | } 1039 | } 1040 | 1041 | fn deserialize_tuple(self, _len: usize, visitor: V) -> Result 1042 | where 1043 | V: serde::de::Visitor<'de>, 1044 | { 1045 | self.deserialize_seq(visitor) 1046 | } 1047 | 1048 | fn deserialize_tuple_struct( 1049 | self, 1050 | _name: &'static str, 1051 | len: usize, 1052 | visitor: V, 1053 | ) -> Result 1054 | where 1055 | V: serde::de::Visitor<'de>, 1056 | { 1057 | self.deserialize_tuple(len, visitor) 1058 | } 1059 | 1060 | fn deserialize_map(self, visitor: V) -> Result 1061 | where 1062 | V: serde::de::Visitor<'de>, 1063 | { 1064 | match &self.0 { 1065 | Value::Named(Named { 1066 | contents: StructContents::Map(map), 1067 | .. 1068 | }) 1069 | | Value::Map(map) => visitor.visit_map(MapDeserializer::new(map)), 1070 | _ => Err(FromValueError::Expected(ExpectedKind::Map)), 1071 | } 1072 | } 1073 | 1074 | fn deserialize_struct( 1075 | self, 1076 | _name: &'static str, 1077 | _fields: &'static [&'static str], 1078 | visitor: V, 1079 | ) -> Result 1080 | where 1081 | V: serde::de::Visitor<'de>, 1082 | { 1083 | self.deserialize_map(visitor) 1084 | } 1085 | 1086 | fn deserialize_enum( 1087 | self, 1088 | _name: &'static str, 1089 | _variants: &'static [&'static str], 1090 | visitor: V, 1091 | ) -> Result 1092 | where 1093 | V: serde::de::Visitor<'de>, 1094 | { 1095 | visitor.visit_enum(self) 1096 | } 1097 | 1098 | fn deserialize_identifier(self, visitor: V) -> Result 1099 | where 1100 | V: serde::de::Visitor<'de>, 1101 | { 1102 | self.deserialize_str(visitor) 1103 | } 1104 | 1105 | fn deserialize_ignored_any(self, visitor: V) -> Result 1106 | where 1107 | V: serde::de::Visitor<'de>, 1108 | { 1109 | self.deserialize_any(visitor) 1110 | } 1111 | } 1112 | 1113 | impl<'a, 'de> EnumAccess<'de> for ValueDeserializer<'a, 'de> { 1114 | type Error = FromValueError; 1115 | type Variant = EnumVariantAccessor<'a, 'de>; 1116 | 1117 | fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error> 1118 | where 1119 | V: serde::de::DeserializeSeed<'de>, 1120 | { 1121 | match &self.0 { 1122 | Value::Identifier(_) | Value::String(_) => { 1123 | Ok((seed.deserialize(self)?, EnumVariantAccessor::Unit)) 1124 | } 1125 | Value::Named(named) => { 1126 | let variant = 1127 | seed.deserialize(ValueDeserializer(&Value::String(named.name.clone())))?; 1128 | 1129 | let accessor = match &named.contents { 1130 | StructContents::Map(map) => EnumVariantAccessor::Map(map), 1131 | StructContents::Tuple(list) => EnumVariantAccessor::Tuple(list), 1132 | }; 1133 | 1134 | Ok((variant, accessor)) 1135 | } 1136 | _ => Err(FromValueError::Expected(ExpectedKind::Enum)), 1137 | } 1138 | } 1139 | } 1140 | 1141 | pub enum EnumVariantAccessor<'a, 'de> { 1142 | Unit, 1143 | Tuple(&'a List<'de>), 1144 | Map(&'a Map<'de>), 1145 | } 1146 | 1147 | impl<'a, 'de> VariantAccess<'de> for EnumVariantAccessor<'a, 'de> { 1148 | type Error = FromValueError; 1149 | 1150 | fn unit_variant(self) -> Result<(), Self::Error> { 1151 | Ok(()) 1152 | } 1153 | 1154 | fn newtype_variant_seed(self, seed: T) -> Result 1155 | where 1156 | T: serde::de::DeserializeSeed<'de>, 1157 | { 1158 | match self { 1159 | EnumVariantAccessor::Unit => seed.deserialize(ValueDeserializer(&Value::unit())), 1160 | EnumVariantAccessor::Tuple(list) => { 1161 | if let Some(first) = list.0.first() { 1162 | seed.deserialize(ValueDeserializer(first)) 1163 | } else { 1164 | seed.deserialize(ValueDeserializer(&Value::unit())) 1165 | } 1166 | } 1167 | EnumVariantAccessor::Map(_) => Err(FromValueError::Expected(ExpectedKind::Newtype)), 1168 | } 1169 | } 1170 | 1171 | fn tuple_variant(self, _len: usize, visitor: V) -> Result 1172 | where 1173 | V: serde::de::Visitor<'de>, 1174 | { 1175 | match self { 1176 | EnumVariantAccessor::Unit => visitor.visit_seq(SequenceDeserializer([].iter())), 1177 | EnumVariantAccessor::Tuple(list) => { 1178 | visitor.visit_seq(SequenceDeserializer(list.0.iter())) 1179 | } 1180 | EnumVariantAccessor::Map(map) => visitor.visit_map(MapDeserializer::new(map)), 1181 | } 1182 | } 1183 | 1184 | fn struct_variant( 1185 | self, 1186 | _fields: &'static [&'static str], 1187 | visitor: V, 1188 | ) -> Result 1189 | where 1190 | V: serde::de::Visitor<'de>, 1191 | { 1192 | match self { 1193 | EnumVariantAccessor::Unit => visitor.visit_seq(SequenceDeserializer([].iter())), 1194 | EnumVariantAccessor::Tuple(list) => { 1195 | visitor.visit_seq(SequenceDeserializer(list.0.iter())) 1196 | } 1197 | EnumVariantAccessor::Map(map) => visitor.visit_map(MapDeserializer::new(map)), 1198 | } 1199 | } 1200 | } 1201 | 1202 | pub struct MapDeserializer<'a, 'de> { 1203 | map: &'a Map<'de>, 1204 | index: usize, 1205 | } 1206 | 1207 | impl<'a, 'de> MapDeserializer<'a, 'de> { 1208 | pub fn new(map: &'a Map<'de>) -> Self { 1209 | Self { map, index: 0 } 1210 | } 1211 | } 1212 | 1213 | impl<'a, 'de> MapAccess<'de> for MapDeserializer<'a, 'de> { 1214 | type Error = FromValueError; 1215 | 1216 | fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> 1217 | where 1218 | K: serde::de::DeserializeSeed<'de>, 1219 | { 1220 | self.map 1221 | .0 1222 | .get(self.index) 1223 | .map(|item| seed.deserialize(ValueDeserializer(&item.0))) 1224 | .transpose() 1225 | } 1226 | 1227 | fn next_value_seed(&mut self, seed: V) -> Result 1228 | where 1229 | V: serde::de::DeserializeSeed<'de>, 1230 | { 1231 | let value = seed.deserialize(ValueDeserializer(&self.map.0[self.index].1))?; 1232 | self.index += 1; 1233 | Ok(value) 1234 | } 1235 | } 1236 | 1237 | pub struct SequenceDeserializer<'a, 'de>(slice::Iter<'a, Value<'de>>); 1238 | 1239 | impl<'a, 'de> SeqAccess<'de> for SequenceDeserializer<'a, 'de> { 1240 | type Error = FromValueError; 1241 | 1242 | fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> 1243 | where 1244 | T: serde::de::DeserializeSeed<'de>, 1245 | { 1246 | self.0 1247 | .next() 1248 | .map(|item| seed.deserialize(ValueDeserializer(item))) 1249 | .transpose() 1250 | } 1251 | } 1252 | 1253 | /// An error from serializing to a [`Value`]. 1254 | #[derive(Debug, PartialEq)] 1255 | 1256 | pub enum ToValueError { 1257 | /// A custom serialization error. 1258 | Message(String), 1259 | /// An integer was too larget to represent. 1260 | IntegerTooLarge(TryFromIntError), 1261 | } 1262 | 1263 | impl From for ToValueError { 1264 | fn from(value: TryFromIntError) -> Self { 1265 | Self::IntegerTooLarge(value) 1266 | } 1267 | } 1268 | 1269 | impl serde::ser::Error for ToValueError { 1270 | fn custom(msg: T) -> Self 1271 | where 1272 | T: Display, 1273 | { 1274 | Self::Message(msg.to_string()) 1275 | } 1276 | } 1277 | 1278 | impl serde::ser::StdError for ToValueError {} 1279 | 1280 | impl Display for ToValueError { 1281 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 1282 | match self { 1283 | ToValueError::Message(message) => f.write_str(message), 1284 | ToValueError::IntegerTooLarge(err) => write!(f, "{err}"), 1285 | } 1286 | } 1287 | } 1288 | 1289 | /// An error from deserializing from a [`Value`]. 1290 | #[derive(Debug, PartialEq)] 1291 | 1292 | pub enum FromValueError { 1293 | /// A custom serialization error. 1294 | Message(String), 1295 | /// Expected a kind of data, but encountered another kind. 1296 | Expected(ExpectedKind), 1297 | /// Invalid UTF-8 was encountered. 1298 | InvalidUtf8, 1299 | } 1300 | 1301 | impl serde::de::Error for FromValueError { 1302 | fn custom(msg: T) -> Self 1303 | where 1304 | T: Display, 1305 | { 1306 | Self::Message(msg.to_string()) 1307 | } 1308 | } 1309 | 1310 | impl From for FromValueError { 1311 | fn from(_value: Utf8Error) -> Self { 1312 | Self::InvalidUtf8 1313 | } 1314 | } 1315 | 1316 | impl serde::de::StdError for FromValueError {} 1317 | 1318 | impl Display for FromValueError { 1319 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 1320 | match self { 1321 | FromValueError::Message(message) => f.write_str(message), 1322 | FromValueError::Expected(kind) => write!(f, "expected {kind}"), 1323 | FromValueError::InvalidUtf8 => { 1324 | f.write_str("invalid utf8 when interpreting bytes as a string") 1325 | } 1326 | } 1327 | } 1328 | } 1329 | 1330 | #[derive(Debug, PartialEq)] 1331 | pub enum ExpectedKind { 1332 | I8, 1333 | I16, 1334 | I32, 1335 | I64, 1336 | I128, 1337 | U8, 1338 | U16, 1339 | U32, 1340 | U64, 1341 | U128, 1342 | Bool, 1343 | Char, 1344 | String, 1345 | Bytes, 1346 | Float, 1347 | Enum, 1348 | Map, 1349 | Sequence, 1350 | Unit, 1351 | Newtype, 1352 | } 1353 | 1354 | impl Display for ExpectedKind { 1355 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 1356 | match self { 1357 | ExpectedKind::I8 => f.write_str("i8"), 1358 | ExpectedKind::I16 => f.write_str("i16"), 1359 | ExpectedKind::I32 => f.write_str("i32"), 1360 | ExpectedKind::I64 => f.write_str("i64"), 1361 | ExpectedKind::I128 => f.write_str("i128"), 1362 | ExpectedKind::U8 => f.write_str("u8"), 1363 | ExpectedKind::U16 => f.write_str("u16"), 1364 | ExpectedKind::U32 => f.write_str("u32"), 1365 | ExpectedKind::U64 => f.write_str("u64"), 1366 | ExpectedKind::U128 => f.write_str("u128"), 1367 | ExpectedKind::Bool => f.write_str("bool"), 1368 | ExpectedKind::Char => f.write_str("char"), 1369 | ExpectedKind::String => f.write_str("String"), 1370 | ExpectedKind::Bytes => f.write_str("Bytes"), 1371 | ExpectedKind::Float => f.write_str("Float"), 1372 | ExpectedKind::Enum => f.write_str("Enum"), 1373 | ExpectedKind::Map => f.write_str("Map"), 1374 | ExpectedKind::Sequence => f.write_str("Sequence"), 1375 | ExpectedKind::Unit => f.write_str("()"), 1376 | ExpectedKind::Newtype => f.write_str("Newtype"), 1377 | } 1378 | } 1379 | } 1380 | } 1381 | 1382 | #[test] 1383 | fn display() { 1384 | use alloc::string::ToString; 1385 | use alloc::{format, vec}; 1386 | 1387 | assert_eq!( 1388 | Value::Named(Named { 1389 | name: Cow::Borrowed("Hello"), 1390 | contents: StructContents::Tuple(List(vec![Value::String(Cow::Borrowed("World"))])) 1391 | }) 1392 | .to_string(), 1393 | "Hello(\"World\")" 1394 | ); 1395 | 1396 | assert_eq!( 1397 | format!( 1398 | "{:#}", 1399 | Value::Named(Named { 1400 | name: Cow::Borrowed("Hello"), 1401 | contents: StructContents::Tuple(List(vec![Value::String(Cow::Borrowed("World"))])) 1402 | }) 1403 | ), 1404 | "Hello(\n \"World\"\n)" 1405 | ); 1406 | } 1407 | 1408 | #[cfg(feature = "serde")] 1409 | pub use self::serde::{FromValueError, ToValueError}; 1410 | -------------------------------------------------------------------------------- /src/writer.rs: -------------------------------------------------------------------------------- 1 | use alloc::borrow::Cow; 2 | use alloc::string::String; 3 | use alloc::vec::Vec; 4 | use core::fmt; 5 | use core::fmt::Write; 6 | 7 | use crate::tokenizer::Integer; 8 | use crate::value::{StructContents, Value}; 9 | 10 | /// A low-level writer for the Rsn format. 11 | #[derive(Debug)] 12 | pub struct Writer<'config, Output> { 13 | output: Output, 14 | nested: Vec, 15 | config: Cow<'config, Config>, 16 | } 17 | 18 | impl Default for Writer<'static, String> { 19 | fn default() -> Self { 20 | Self::new(String::new(), &Config::Compact) 21 | } 22 | } 23 | 24 | impl<'config, Output> Writer<'config, Output> 25 | where 26 | Output: Write, 27 | { 28 | /// Returns a writer that outputs to `output` using `config`. 29 | pub fn new(output: Output, config: &'config Config) -> Self { 30 | Self { 31 | output, 32 | nested: Vec::new(), 33 | config: Cow::Borrowed(config), 34 | } 35 | } 36 | 37 | /// Finishes writing and returns the output. 38 | /// 39 | /// # Panics 40 | /// 41 | /// This function panics if a nested type was begun and a corresponding 42 | /// `finish_nested` was not called. 43 | pub fn finish(self) -> Output { 44 | assert!(self.nested.is_empty()); 45 | self.output 46 | } 47 | 48 | /// Begins a named map. A corresponding call to `finish_nested` must be made 49 | /// when the map contents are completed. 50 | /// 51 | /// # Errors 52 | /// 53 | /// Returns any errors that arise while writing to `Output`. 54 | pub fn begin_named_map(&mut self, name: &str) -> fmt::Result { 55 | self.prepare_to_write_value()?; 56 | self.output.write_str(name)?; 57 | if matches!(self.config.as_ref(), Config::Pretty { .. }) { 58 | self.output.write_char(' ')?; 59 | } 60 | self.output.write_char('{')?; 61 | self.nested.push(NestedState::Map(MapState::Empty)); 62 | Ok(()) 63 | } 64 | 65 | /// Begins a named tuple. A corresponding call to `finish_nested` must be 66 | /// made when the tuple contents are completed. 67 | /// 68 | /// # Errors 69 | /// 70 | /// Returns any errors that arise while writing to `Output`. 71 | pub fn begin_named_tuple(&mut self, name: &str) -> fmt::Result { 72 | self.prepare_to_write_value()?; 73 | self.nested.push(NestedState::Tuple(SequenceState::Empty)); 74 | self.output.write_str(name)?; 75 | self.output.write_char('(') 76 | } 77 | 78 | /// Begins a map. A corresponding call to `finish_nested` must be made when 79 | /// the map contents are completed. 80 | /// 81 | /// # Errors 82 | /// 83 | /// Returns any errors that arise while writing to `Output`. 84 | pub fn begin_map(&mut self) -> fmt::Result { 85 | self.prepare_to_write_value()?; 86 | self.nested.push(NestedState::Map(MapState::Empty)); 87 | self.output.write_char('{') 88 | } 89 | 90 | /// Begins a tuple. A corresponding call to `finish_nested` must be made when 91 | /// the tuple contents are completed. 92 | /// 93 | /// # Errors 94 | /// 95 | /// Returns any errors that arise while writing to `Output`. 96 | pub fn begin_tuple(&mut self) -> fmt::Result { 97 | self.prepare_to_write_value()?; 98 | self.nested.push(NestedState::Tuple(SequenceState::Empty)); 99 | self.output.write_char('(') 100 | } 101 | 102 | /// Begins a list/array. A corresponding call to `finish_nested` must be 103 | /// made when the list contents are completed. 104 | /// 105 | /// # Errors 106 | /// 107 | /// Returns any errors that arise while writing to `Output`. 108 | pub fn begin_list(&mut self) -> fmt::Result { 109 | self.prepare_to_write_value()?; 110 | self.nested.push(NestedState::List(SequenceState::Empty)); 111 | self.output.write_char('[') 112 | } 113 | 114 | /// Writes a primitive value, formatting it as valid Rsn. 115 | /// 116 | /// # Errors 117 | /// 118 | /// Returns any errors that arise while writing to `Output`. 119 | pub fn write_primitive

(&mut self, p: &P) -> fmt::Result 120 | where 121 | P: Primitive + ?Sized, 122 | { 123 | self.prepare_to_write_value()?; 124 | p.render_to(&mut self.output) 125 | } 126 | 127 | /// Writes `ident` without any extra formatting. 128 | /// 129 | /// # Errors 130 | /// 131 | /// Returns any errors that arise while writing to `Output`. 132 | pub fn write_raw_value(&mut self, ident: &str) -> fmt::Result { 133 | self.prepare_to_write_value()?; 134 | self.output.write_str(ident) 135 | } 136 | 137 | fn prepare_to_write_value(&mut self) -> fmt::Result { 138 | match self.nested.last_mut() { 139 | Some( 140 | NestedState::List(state @ SequenceState::Empty) 141 | | NestedState::Tuple(state @ SequenceState::Empty), 142 | ) => { 143 | *state = SequenceState::NotEmpty; 144 | self.insert_newline()?; 145 | } 146 | Some(NestedState::List(_) | NestedState::Tuple(_)) => { 147 | self.output.write_char(',')?; 148 | self.insert_newline()?; 149 | } 150 | Some(NestedState::Map(state @ MapState::Empty)) => { 151 | *state = MapState::AfterKey; 152 | self.insert_newline()?; 153 | } 154 | Some(NestedState::Map(state @ MapState::AfterEntry)) => { 155 | *state = MapState::AfterKey; 156 | self.output.write_char(',')?; 157 | self.insert_newline()?; 158 | } 159 | Some(NestedState::Map(state @ MapState::AfterKey)) => { 160 | *state = MapState::AfterEntry; 161 | if matches!(self.config.as_ref(), Config::Compact) { 162 | self.output.write_char(':')?; 163 | } else { 164 | self.output.write_str(": ")?; 165 | } 166 | } 167 | None => {} 168 | } 169 | 170 | Ok(()) 171 | } 172 | 173 | /// Inserts the configured newline character, if needed. 174 | /// 175 | /// # Errors 176 | /// 177 | /// Returns any errors that arise while writing to `Output`. 178 | pub fn insert_newline(&mut self) -> fmt::Result { 179 | if let Config::Pretty { 180 | indentation, 181 | newline, 182 | .. 183 | } = self.config.as_ref() 184 | { 185 | self.output.write_str(newline)?; 186 | for _ in 0..self.nested.len() { 187 | self.output.write_str(indentation)?; 188 | } 189 | } 190 | Ok(()) 191 | } 192 | 193 | /// Finishes the current nested value. 194 | /// 195 | /// # Errors 196 | /// 197 | /// Returns any errors that arise while writing to `Output`. 198 | /// 199 | /// # Panics 200 | /// 201 | /// This function panics if are no open nested types. 202 | pub fn finish_nested(&mut self) -> fmt::Result { 203 | match self.nested.pop().expect("not in a nested state") { 204 | NestedState::Tuple(state) => { 205 | if matches!(state, SequenceState::NotEmpty) { 206 | self.insert_newline()?; 207 | } 208 | self.output.write_char(')') 209 | } 210 | NestedState::List(state) => { 211 | if matches!(state, SequenceState::NotEmpty) { 212 | self.insert_newline()?; 213 | } 214 | self.output.write_char(']') 215 | } 216 | NestedState::Map(state @ (MapState::AfterEntry | MapState::Empty)) => { 217 | if matches!(state, MapState::AfterEntry) { 218 | self.insert_newline()?; 219 | } 220 | self.output.write_char('}') 221 | } 222 | NestedState::Map(_) => unreachable!("map entry not complete"), 223 | } 224 | } 225 | 226 | /// Writes a value. 227 | /// 228 | /// # Errors 229 | /// 230 | /// Returns any errors that arise while writing to `Output`. 231 | pub fn write_value(&mut self, value: &Value<'_>) -> fmt::Result { 232 | match value { 233 | Value::Integer(value) => match value { 234 | Integer::Usize(value) => self.write_primitive(value), 235 | Integer::Isize(value) => self.write_primitive(value), 236 | Integer::UnsignedLarge(value) => self.write_primitive(value), 237 | Integer::SignedLarge(value) => self.write_primitive(value), 238 | }, 239 | Value::Float(value) => self.write_primitive(value), 240 | Value::Bool(value) => self.write_primitive(value), 241 | Value::Char(value) => self.write_primitive(value), 242 | Value::Byte(value) => self.write_primitive(value), 243 | Value::Identifier(value) | Value::String(value) => self.write_primitive(value.as_ref()), 244 | Value::Bytes(value) => self.write_primitive(value.as_ref()), 245 | Value::Named(value) => { 246 | match &value.contents { 247 | StructContents::Map(map) => { 248 | self.begin_named_map(&value.name)?; 249 | for (key, value) in &map.0 { 250 | self.write_value(key)?; 251 | self.write_value(value)?; 252 | } 253 | } 254 | StructContents::Tuple(seq) => { 255 | self.begin_named_tuple(&value.name)?; 256 | for value in &seq.0 { 257 | self.write_value(value)?; 258 | } 259 | } 260 | } 261 | self.finish_nested() 262 | } 263 | Value::Tuple(list) => { 264 | self.begin_tuple()?; 265 | for value in &list.0 { 266 | self.write_value(value)?; 267 | } 268 | self.finish_nested() 269 | } 270 | Value::Array(list) => { 271 | self.begin_list()?; 272 | for value in &list.0 { 273 | self.write_value(value)?; 274 | } 275 | self.finish_nested() 276 | } 277 | Value::Map(map) => { 278 | self.begin_map()?; 279 | for (key, value) in &map.0 { 280 | self.write_value(key)?; 281 | self.write_value(value)?; 282 | } 283 | self.finish_nested() 284 | } 285 | } 286 | } 287 | } 288 | 289 | /// A type that can be written as a primitive.q 290 | pub trait Primitive { 291 | /// Renders this type to `buffer`. 292 | /// 293 | /// # Errors 294 | /// 295 | /// Returns any errors that arise while writing to `buffer`. 296 | fn render_to(&self, buffer: &mut W) -> fmt::Result; 297 | } 298 | 299 | macro_rules! impl_primitive_using_to_string { 300 | ($type:ty) => { 301 | impl Primitive for $type { 302 | fn render_to(&self, buffer: &mut W) -> fmt::Result { 303 | write!(buffer, "{self}") 304 | } 305 | } 306 | }; 307 | } 308 | 309 | impl_primitive_using_to_string!(u8); 310 | impl_primitive_using_to_string!(u16); 311 | impl_primitive_using_to_string!(u32); 312 | impl_primitive_using_to_string!(u64); 313 | impl_primitive_using_to_string!(u128); 314 | impl_primitive_using_to_string!(usize); 315 | impl_primitive_using_to_string!(i8); 316 | impl_primitive_using_to_string!(i16); 317 | impl_primitive_using_to_string!(i32); 318 | impl_primitive_using_to_string!(i64); 319 | impl_primitive_using_to_string!(i128); 320 | impl_primitive_using_to_string!(isize); 321 | impl_primitive_using_to_string!(f64); 322 | impl_primitive_using_to_string!(f32); 323 | 324 | impl Primitive for str { 325 | fn render_to(&self, buffer: &mut W) -> fmt::Result { 326 | buffer.write_char('"')?; 327 | for ch in self.chars() { 328 | escape_string_char(ch, buffer)?; 329 | } 330 | buffer.write_char('"') 331 | } 332 | } 333 | 334 | impl Primitive for bool { 335 | fn render_to(&self, buffer: &mut W) -> fmt::Result { 336 | buffer.write_str(if *self { "true" } else { "false" }) 337 | } 338 | } 339 | 340 | impl Primitive for [u8] { 341 | fn render_to(&self, buffer: &mut W) -> fmt::Result { 342 | buffer.write_str("b\"")?; 343 | for byte in self { 344 | match DEFAULT_STRING_ESCAPE_HANDLING.get(usize::from(*byte)) { 345 | Some(Some(escaped)) => { 346 | buffer.write_str(escaped)?; 347 | } 348 | Some(None) => { 349 | buffer.write_char(char::from(*byte))?; 350 | } 351 | None => { 352 | // Non-ASCII, must be hex-escaped. 353 | write!(buffer, "\\x{byte:02x}")?; 354 | } 355 | } 356 | } 357 | buffer.write_char('"') 358 | } 359 | } 360 | 361 | #[inline] 362 | fn escape_string_char(ch: char, buffer: &mut W) -> fmt::Result { 363 | if let Ok(cp) = usize::try_from(u32::from(ch)) { 364 | if let Some(Some(escaped)) = DEFAULT_STRING_ESCAPE_HANDLING.get(cp) { 365 | return buffer.write_str(escaped); 366 | } 367 | } 368 | 369 | let mut utf8_bytes = [0; 8]; 370 | buffer.write_str(ch.encode_utf8(&mut utf8_bytes)) 371 | } 372 | 373 | impl Primitive for char { 374 | fn render_to(&self, buffer: &mut W) -> fmt::Result { 375 | buffer.write_char('\'')?; 376 | escape_string_char(*self, buffer)?; 377 | buffer.write_char('\'') 378 | } 379 | } 380 | 381 | #[rustfmt::skip] 382 | static DEFAULT_STRING_ESCAPE_HANDLING: [Option<&'static str>; 128] = [ 383 | // 0x0 1 2 3 4 5 6 7 384 | Some("\\0"), Some("\\x01"), Some("\\x02"), Some("\\x03"), Some("\\x04"), Some("\\x05"), Some("\\x06"), Some("\\x07"), 385 | // 0x8 9 A B C D E F 386 | Some("\\x08"), Some("\\t"), Some("\\n"), Some("\\x0b"), Some("\\x0c"), Some("\\r"), Some("\\x0e"), Some("\\x0f"), 387 | // 0x10 388 | Some("\\x10"), Some("\\x11"), Some("\\x12"), Some("\\x13"), Some("\\x14"), Some("\\x15"), Some("\\x16"), Some("\\x17"), 389 | Some("\\x18"), Some("\\x19"), Some("\\x1a"), Some("\\x1b"), Some("\\x1c"), Some("\\x1d"), Some("\\x1e"), Some("\\x1f"), 390 | // 0x20 391 | None, None, Some("\\\""), None, None, None, None, None, 392 | None, None, None, None, None, None, None, None, 393 | // 0x30 394 | None, None, None, None, None, None, None, None, 395 | None, None, None, None, None, None, None, None, 396 | // 0x40 397 | None, None, None, None, None, None, None, None, 398 | None, None, None, None, None, None, None, None, 399 | // 0x50 400 | None, None, None, None, None, None, None, None, 401 | None, None, None, None, Some("\\\\"), None, None, None, 402 | // 0x60 403 | None, None, None, None, None, None, None, None, 404 | None, None, None, None, None, None, None, None, 405 | // 0x70 406 | None, None, None, None, None, None, None, None, 407 | None, None, None, None, None, None, None, Some("\\x7f"), 408 | ]; 409 | 410 | #[derive(Debug)] 411 | enum NestedState { 412 | Tuple(SequenceState), 413 | List(SequenceState), 414 | Map(MapState), 415 | } 416 | 417 | #[derive(Debug)] 418 | enum SequenceState { 419 | Empty, 420 | NotEmpty, 421 | } 422 | 423 | #[derive(Debug)] 424 | enum MapState { 425 | Empty, 426 | AfterEntry, 427 | AfterKey, 428 | } 429 | 430 | /// A writer configuration. 431 | #[derive(Debug, Default, Clone)] 432 | #[non_exhaustive] 433 | pub enum Config { 434 | /// Renders Rsn in its most compact representation. 435 | #[default] 436 | Compact, 437 | /// Renders Rsn with indentation and line endings. 438 | Pretty { 439 | /// The indentation to include for each level of nested data type. 440 | indentation: Cow<'static, str>, 441 | /// The newline character(s) to include when wrapping to a new line. 442 | newline: Cow<'static, str>, 443 | }, 444 | } 445 | 446 | #[test] 447 | fn string_rendering() { 448 | use crate::tokenizer::{Token, TokenKind, Tokenizer}; 449 | let mut to_encode = String::new(); 450 | for ch in 0_u8..128 { 451 | to_encode.push(ch as char); 452 | } 453 | to_encode.write_char('\u{1_F980}').unwrap(); 454 | let mut rendered = String::new(); 455 | to_encode.render_to(&mut rendered).unwrap(); 456 | assert_eq!( 457 | rendered, 458 | "\"\\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\x7f🦀\"" 459 | ); 460 | let Some(Ok(Token { 461 | kind: TokenKind::String(parsed), 462 | .. 463 | })) = Tokenizer::full(&rendered).next() 464 | else { 465 | unreachable!("failed to parse rendered string") 466 | }; 467 | assert_eq!(parsed, to_encode); 468 | } 469 | 470 | #[test] 471 | fn byte_rendering() { 472 | use crate::tokenizer::{Token, TokenKind, Tokenizer}; 473 | let mut to_encode = Vec::new(); 474 | for ch in 0_u8..255 { 475 | to_encode.push(ch); 476 | } 477 | let mut rendered = String::new(); 478 | to_encode.render_to(&mut rendered).unwrap(); 479 | assert_eq!( 480 | rendered, 481 | "b\"\\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc\\xbd\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\"" 482 | ); 483 | let Some(Ok(Token { 484 | kind: TokenKind::Bytes(parsed), 485 | .. 486 | })) = Tokenizer::full(&rendered).next() 487 | else { 488 | unreachable!("failed to parse rendered bytes") 489 | }; 490 | assert_eq!(parsed, to_encode); 491 | } 492 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | khonsu-tools = { git = "https://github.com/khonsulabs/khonsu-tools.git", branch = "main" } 9 | -------------------------------------------------------------------------------- /xtask/src/main.rs: -------------------------------------------------------------------------------- 1 | use khonsu_tools::universal::clap::Parser; 2 | use khonsu_tools::universal::{anyhow, DefaultConfig}; 3 | use khonsu_tools::Commands; 4 | 5 | fn main() -> anyhow::Result<()> { 6 | Commands::parse().execute::() 7 | } 8 | 9 | enum Config {} 10 | 11 | impl khonsu_tools::Config for Config { 12 | type Publish = Self; 13 | type Universal = Self; 14 | } 15 | 16 | impl khonsu_tools::universal::Config for Config { 17 | type Audit = DefaultConfig; 18 | type CodeCoverage = Self; 19 | } 20 | 21 | impl khonsu_tools::publish::Config for Config { 22 | fn paths() -> Vec { 23 | vec![String::from(".")] 24 | } 25 | } 26 | 27 | impl khonsu_tools::universal::code_coverage::Config for Config { 28 | fn cargo_args() -> Vec { 29 | vec![ 30 | String::from("test"), 31 | String::from("--workspace"), 32 | String::from("--all-targets"), 33 | String::from("--features"), 34 | String::from("std,serde"), 35 | ] 36 | } 37 | } 38 | --------------------------------------------------------------------------------