├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE-Apache-2.0 ├── LICENSE-ISC ├── README.md ├── src ├── de.rs ├── error.rs ├── lib.rs └── ser.rs └── tests ├── adapted_from_js_reference.rs ├── assets └── json5_dot_org_example.json5 ├── common └── mod.rs ├── de.rs ├── json5_dot_org_example.rs └── ser.rs /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | ci: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | os: [ubuntu-latest, windows-latest, macOS-latest] 15 | rust: 16 | - stable 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v1 20 | 21 | - name: Install Rust 22 | uses: actions-rs/toolchain/@v1 23 | with: 24 | profile: minimal 25 | toolchain: ${{ matrix.rust }} 26 | override: true 27 | 28 | - name: Build 29 | uses: actions-rs/cargo@v1 30 | with: 31 | command: build 32 | 33 | - name: Run Tests 34 | uses: actions-rs/cargo@v1 35 | with: 36 | command: test 37 | args: ${{ matrix.features }} 38 | 39 | rustfmt: 40 | name: rustfmt 41 | runs-on: ubuntu-latest 42 | steps: 43 | - name: Checkout repository 44 | uses: actions/checkout@v1 45 | 46 | - name: Install Rust 47 | uses: actions-rs/toolchain@v1 48 | with: 49 | toolchain: stable 50 | override: true 51 | profile: minimal 52 | components: rustfmt 53 | 54 | - name: Check formatting 55 | run: cargo fmt --all -- --check 56 | 57 | clippy: 58 | name: clippy 59 | runs-on: ubuntu-latest 60 | steps: 61 | - name: Checkout repository 62 | uses: actions/checkout@v1 63 | 64 | - name: Install Rust 65 | uses: actions-rs/toolchain@v1 66 | with: 67 | toolchain: stable 68 | override: true 69 | profile: minimal 70 | components: rustfmt 71 | 72 | - name: Check Clippy 73 | run: cargo clippy --all --tests --workspace -- -D warnings 74 | 75 | docs: 76 | name: docs 77 | runs-on: ubuntu-latest 78 | steps: 79 | - name: Checkout repository 80 | uses: actions/checkout@v1 81 | 82 | - name: Install Rust 83 | uses: actions-rs/toolchain@v1 84 | with: 85 | toolchain: stable 86 | override: true 87 | profile: minimal 88 | 89 | - name: Check documentation 90 | env: 91 | RUSTDOCFLAGS: -D warnings 92 | run: cargo doc --no-deps --document-private-items --workspace 93 | 94 | coverage: 95 | name: coverage 96 | runs-on: ubuntu-latest 97 | 98 | steps: 99 | - name: Checkout repository 100 | uses: actions/checkout@v2 101 | with: 102 | fetch-depth: 0 103 | 104 | - name: Install Rust 105 | uses: actions-rs/toolchain@v1 106 | with: 107 | toolchain: nightly 108 | override: true 109 | components: llvm-tools-preview 110 | 111 | - name: Install cargo-llvm-cov 112 | uses: taiki-e/install-action@cargo-llvm-cov 113 | 114 | - name: Generate code coverage 115 | run: cargo llvm-cov --workspace --lcov --output-path lcov.info 116 | 117 | - name: Upload coverage to coveralls 118 | uses: coverallsapp/github-action@master 119 | with: 120 | github-token: ${{ secrets.GITHUB_TOKEN }} 121 | path-to-lcov: ./lcov.info 122 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target/ 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement (CLA). You (or your employer) retain the copyright to your 10 | contribution; this simply gives us permission to use and redistribute your 11 | contributions as part of the project. Head over to 12 | to see your current agreements on file or 13 | to sign a new one. 14 | 15 | You generally only need to submit a CLA once, so if you've already submitted one 16 | (even if it was for a different project), you probably don't need to do it 17 | again. 18 | 19 | ## Code reviews 20 | 21 | All submissions, including submissions by project members, require review. We 22 | use GitHub pull requests for this purpose. Consult 23 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 24 | information on using pull requests. 25 | 26 | ## Community Guidelines 27 | 28 | This project follows 29 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/). 30 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "serde_json5" 3 | version = "0.2.1" 4 | authors = ["Gary Bressler "] 5 | description = "A Serde (de)serializer for JSON5." 6 | license = "Apache-2.0 AND ISC" 7 | repository = "https://github.com/google/serde_json5" 8 | readme = "README.md" 9 | keywords = ["json5", "json", "serde", "serialization"] 10 | edition = "2021" 11 | 12 | [dependencies] 13 | pest = "2.0" 14 | pest_derive = "2.0" 15 | serde = "1.0" 16 | 17 | [dev-dependencies] 18 | matches = "0.1.8" 19 | serde_derive = "1.0" 20 | serde_json = "1.0" 21 | -------------------------------------------------------------------------------- /LICENSE-Apache-2.0: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /LICENSE-ISC: -------------------------------------------------------------------------------- 1 | Copyright 2018 Callum Oakley 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 9 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 12 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSON5 2 | 3 | A Rust library for serializing and deserializing [JSON5][json5] with 4 | [Serde][serde]. 5 | 6 | This project is a fork of [callum-oakley/json5-rs][github-json5-rs]. 7 | 8 | [json5]: https://json5.org/ 9 | [serde]: https://serde.rs/ 10 | [github-json5-rs]: https://github.com/callum-oakley/json5-rs 11 | -------------------------------------------------------------------------------- /src/de.rs: -------------------------------------------------------------------------------- 1 | use pest::iterators::Pair; 2 | use pest::Parser as P; 3 | use pest_derive::Parser; 4 | use serde::de; 5 | use serde::forward_to_deserialize_any; 6 | use std::char; 7 | use std::collections::VecDeque; 8 | use std::f64; 9 | use std::io::Read; 10 | 11 | use crate::error::{self, Error, Result}; 12 | 13 | #[derive(Parser)] 14 | #[grammar_inline = r#" 15 | // see https://spec.json5.org/#syntactic-grammar and 16 | // https://spec.json5.org/#lexical-grammar 17 | 18 | COMMENT = _{ "/*" ~ (!"*/" ~ ANY)* ~ "*/" | "//" ~ (!line_terminator ~ ANY)* } 19 | 20 | WHITESPACE = _{ 21 | "\u{0009}" | 22 | "\u{000B}" | 23 | "\u{000C}" | 24 | "\u{0020}" | 25 | "\u{00A0}" | 26 | "\u{FEFF}" | 27 | SPACE_SEPARATOR | 28 | line_terminator 29 | } 30 | 31 | array = { "[" ~ "]" | "[" ~ value ~ ("," ~ value)* ~ ","? ~ "]" } 32 | 33 | boolean = @{ "true" | "false" } 34 | 35 | char_escape_sequence = @{ single_escape_char | non_escape_char } 36 | 37 | char_literal = @{ !("\\" | line_terminator) ~ ANY } 38 | 39 | decimal_integer_literal = _{ "0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* } 40 | 41 | decimal_literal = _{ 42 | decimal_integer_literal ~ "." ~ ASCII_DIGIT* ~ exponent_part? | 43 | "." ~ ASCII_DIGIT+~ exponent_part? | 44 | decimal_integer_literal ~ exponent_part? 45 | } 46 | 47 | double_quote_char = _{ 48 | "\\" ~ escape_sequence | 49 | line_continuation | 50 | !"\"" ~ char_literal 51 | } 52 | 53 | escape_char = _{ single_escape_char | ASCII_DIGIT | "x" | "u" } 54 | 55 | escape_sequence = _{ 56 | char_escape_sequence | 57 | nul_escape_sequence | 58 | "x" ~ hex_escape_sequence | 59 | "u" ~ unicode_escape_sequence 60 | } 61 | 62 | exponent_part = _{ ^"e" ~ ("+" | "-")? ~ ASCII_DIGIT+ } 63 | 64 | hex_escape_sequence = @{ ASCII_HEX_DIGIT{2} } 65 | 66 | hex_integer_literal = _{ ^"0x" ~ ASCII_HEX_DIGIT+ } 67 | 68 | identifier = ${ identifier_start ~ identifier_part* } 69 | 70 | identifier_part = _{ 71 | identifier_start | 72 | &( 73 | NONSPACING_MARK | 74 | DIACRITIC | // not sure about this, spec says "Combining spacing mark (Mc)" 75 | DECIMAL_NUMBER | 76 | CONNECTOR_PUNCTUATION | 77 | "\u{200C}" | 78 | "\u{200D}" 79 | ) ~ char_literal 80 | } 81 | 82 | identifier_start = _{ 83 | &(unicode_letter | "$" | "_") ~ char_literal | 84 | "\\u" ~ unicode_escape_sequence 85 | } 86 | 87 | key = _{ identifier | string } 88 | 89 | line_continuation = _{ "\\" ~ line_terminator_sequence } 90 | 91 | line_terminator = _{ "\u{000A}" | "\u{000D}" | "\u{2028}" | "\u{2029}" } 92 | 93 | line_terminator_sequence = _{ "\u{000D}" ~ "\u{000A}" | line_terminator } 94 | 95 | non_escape_char = _{ !(escape_char | line_terminator) ~ ANY } 96 | 97 | nul_escape_sequence = @{ "0" } 98 | 99 | null = @{ "null" } 100 | 101 | number = @{ ("+" | "-")? ~ numeric_literal } 102 | 103 | numeric_literal = _{ 104 | hex_integer_literal | 105 | decimal_literal | 106 | "Infinity" | 107 | "NaN" 108 | } 109 | 110 | object = { "{" ~ "}" | "{" ~ pair ~ ("," ~ pair)* ~ ","? ~ "}" } 111 | 112 | pair = _{ key ~ ":" ~ value } 113 | 114 | single_escape_char = _{ "'" | "\"" | "\\" | "b" | "f" | "n" | "r" | "t" | "v" } 115 | 116 | single_quote_char = _{ 117 | "\\" ~ escape_sequence | 118 | line_continuation | 119 | !"'" ~ char_literal 120 | } 121 | 122 | string = ${ "\"" ~ double_quote_char* ~ "\"" | "'" ~ single_quote_char* ~ "'" } 123 | 124 | text = _{ SOI ~ value ~ EOI } 125 | 126 | unicode_escape_sequence = @{ ASCII_HEX_DIGIT{4} } 127 | 128 | unicode_letter = _{ 129 | UPPERCASE_LETTER | 130 | LOWERCASE_LETTER | 131 | TITLECASE_LETTER | 132 | MODIFIER_LETTER | 133 | OTHER_LETTER | 134 | LETTER_NUMBER 135 | } 136 | 137 | value = _{ null | boolean | string | number | object | array } 138 | "#] 139 | struct Parser; 140 | 141 | /// Deserialize an instance of type `T` from a string of JSON5 text. Can fail if the input is 142 | /// invalid JSON5, or doesn’t match the structure of the target type. 143 | pub fn from_str<'a, T>(s: &'a str) -> Result 144 | where 145 | T: de::Deserialize<'a>, 146 | { 147 | let mut deserializer = Deserializer::from_str(s)?; 148 | T::deserialize(&mut deserializer) 149 | } 150 | 151 | /// Deserialize an instance of type `T` from a slice of JSON5 text. Can fail if the input is 152 | /// invalid JSON5, or doesn’t match the structure of the target type. 153 | pub fn from_slice<'a, T>(s: &'a [u8]) -> Result 154 | where 155 | T: de::Deserialize<'a>, 156 | { 157 | let valid_utf8 = std::str::from_utf8(s)?; 158 | let mut deserializer = Deserializer::from_str(valid_utf8)?; 159 | T::deserialize(&mut deserializer) 160 | } 161 | 162 | /// Deserialize an instance of type `T` from any implementation of Read. Can fail if the input is 163 | /// invalid JSON5, or doesn’t match the structure of the target type. 164 | pub fn from_reader(mut reader: R) -> Result 165 | where 166 | T: serde::de::DeserializeOwned, 167 | R: Read, 168 | { 169 | let mut data = String::default(); 170 | reader.read_to_string(&mut data)?; 171 | from_str(&data) 172 | } 173 | 174 | /// A Deserializes JSON data into a Rust value. 175 | pub struct Deserializer<'de> { 176 | pair: Option>, 177 | } 178 | 179 | impl<'de> Deserializer<'de> { 180 | /// Creates a JSON5 deserializer from a `&str`. This parses the input at construction time, so 181 | /// can fail if the input is not valid JSON5. 182 | #[allow(clippy::should_implement_trait)] 183 | pub fn from_str(input: &'de str) -> Result { 184 | let pair = Parser::parse(Rule::text, input)?.next().unwrap(); 185 | Ok(Deserializer::from_pair(pair)) 186 | } 187 | 188 | fn from_pair(pair: Pair<'de, Rule>) -> Self { 189 | Deserializer { pair: Some(pair) } 190 | } 191 | } 192 | 193 | impl<'de> de::Deserializer<'de> for &mut Deserializer<'de> { 194 | type Error = Error; 195 | 196 | fn deserialize_any(self, visitor: V) -> Result 197 | where 198 | V: de::Visitor<'de>, 199 | { 200 | let pair = self.pair.take().unwrap(); 201 | let span = pair.as_span(); 202 | let mut res = (move || match pair.as_rule() { 203 | Rule::null => visitor.visit_unit(), 204 | Rule::boolean => visitor.visit_bool(parse_bool(&pair)), 205 | Rule::string | Rule::identifier => visitor.visit_string(parse_string(pair)?), 206 | Rule::number => { 207 | if is_int(pair.as_str()) { 208 | visitor.visit_i64(parse_integer(&pair)?) 209 | } else { 210 | visitor.visit_f64(parse_number(&pair)?) 211 | } 212 | } 213 | Rule::array => visitor.visit_seq(Seq::new(pair)), 214 | Rule::object => visitor.visit_map(Map::new(pair)), 215 | _ => unreachable!(), 216 | })(); 217 | error::set_location(&mut res, &span); 218 | res 219 | } 220 | 221 | fn deserialize_enum( 222 | self, 223 | _name: &'static str, 224 | _variants: &'static [&'static str], 225 | visitor: V, 226 | ) -> Result 227 | where 228 | V: de::Visitor<'de>, 229 | { 230 | let pair = self.pair.take().unwrap(); 231 | let span = pair.as_span(); 232 | let mut res = visitor.visit_enum(Enum { pair }); 233 | error::set_location(&mut res, &span); 234 | res 235 | } 236 | 237 | // The below will get us the right types, but won't necessarily give 238 | // meaningful results if the source is out of the range of the target type. 239 | fn deserialize_i8(self, visitor: V) -> Result 240 | where 241 | V: de::Visitor<'de>, 242 | { 243 | let pair = self.pair.take().unwrap(); 244 | let span = pair.as_span(); 245 | let mut res = (move || visitor.visit_i8(parse_number(&pair)? as i8))(); 246 | error::set_location(&mut res, &span); 247 | res 248 | } 249 | 250 | fn deserialize_i16(self, visitor: V) -> Result 251 | where 252 | V: de::Visitor<'de>, 253 | { 254 | let pair = self.pair.take().unwrap(); 255 | let span = pair.as_span(); 256 | let mut res = (move || visitor.visit_i16(parse_number(&pair)? as i16))(); 257 | error::set_location(&mut res, &span); 258 | res 259 | } 260 | 261 | fn deserialize_i32(self, visitor: V) -> Result 262 | where 263 | V: de::Visitor<'de>, 264 | { 265 | let pair = self.pair.take().unwrap(); 266 | let span = pair.as_span(); 267 | let mut res = (move || visitor.visit_i32(parse_number(&pair)? as i32))(); 268 | error::set_location(&mut res, &span); 269 | res 270 | } 271 | 272 | fn deserialize_i64(self, visitor: V) -> Result 273 | where 274 | V: de::Visitor<'de>, 275 | { 276 | let pair = self.pair.take().unwrap(); 277 | let span = pair.as_span(); 278 | let mut res = (move || visitor.visit_i64(parse_number(&pair)? as i64))(); 279 | error::set_location(&mut res, &span); 280 | res 281 | } 282 | 283 | fn deserialize_i128(self, visitor: V) -> Result 284 | where 285 | V: de::Visitor<'de>, 286 | { 287 | let pair = self.pair.take().unwrap(); 288 | let span = pair.as_span(); 289 | let mut res = (move || visitor.visit_i128(parse_number(&pair)? as i128))(); 290 | error::set_location(&mut res, &span); 291 | res 292 | } 293 | 294 | fn deserialize_u8(self, visitor: V) -> Result 295 | where 296 | V: de::Visitor<'de>, 297 | { 298 | let pair = self.pair.take().unwrap(); 299 | let span = pair.as_span(); 300 | let mut res = (move || visitor.visit_u8(parse_number(&pair)? as u8))(); 301 | error::set_location(&mut res, &span); 302 | res 303 | } 304 | 305 | fn deserialize_u16(self, visitor: V) -> Result 306 | where 307 | V: de::Visitor<'de>, 308 | { 309 | let pair = self.pair.take().unwrap(); 310 | let span = pair.as_span(); 311 | let mut res = (move || visitor.visit_u16(parse_number(&pair)? as u16))(); 312 | error::set_location(&mut res, &span); 313 | res 314 | } 315 | 316 | fn deserialize_u32(self, visitor: V) -> Result 317 | where 318 | V: de::Visitor<'de>, 319 | { 320 | let pair = self.pair.take().unwrap(); 321 | let span = pair.as_span(); 322 | let mut res = (move || visitor.visit_u32(parse_number(&pair)? as u32))(); 323 | error::set_location(&mut res, &span); 324 | res 325 | } 326 | 327 | fn deserialize_u64(self, visitor: V) -> Result 328 | where 329 | V: de::Visitor<'de>, 330 | { 331 | let pair = self.pair.take().unwrap(); 332 | let span = pair.as_span(); 333 | let mut res = (move || visitor.visit_u64(parse_number(&pair)? as u64))(); 334 | error::set_location(&mut res, &span); 335 | res 336 | } 337 | 338 | fn deserialize_u128(self, visitor: V) -> Result 339 | where 340 | V: de::Visitor<'de>, 341 | { 342 | let pair = self.pair.take().unwrap(); 343 | let span = pair.as_span(); 344 | let mut res = (move || visitor.visit_u128(parse_number(&pair)? as u128))(); 345 | error::set_location(&mut res, &span); 346 | res 347 | } 348 | 349 | fn deserialize_f32(self, visitor: V) -> Result 350 | where 351 | V: de::Visitor<'de>, 352 | { 353 | let pair = self.pair.take().unwrap(); 354 | let span = pair.as_span(); 355 | let mut res = (move || visitor.visit_f32(parse_number(&pair)? as f32))(); 356 | error::set_location(&mut res, &span); 357 | res 358 | } 359 | 360 | fn deserialize_f64(self, visitor: V) -> Result 361 | where 362 | V: de::Visitor<'de>, 363 | { 364 | let pair = self.pair.take().unwrap(); 365 | let span = pair.as_span(); 366 | let mut res = (move || visitor.visit_f64(parse_number(&pair)?))(); 367 | error::set_location(&mut res, &span); 368 | res 369 | } 370 | 371 | fn deserialize_option(self, visitor: V) -> Result 372 | where 373 | V: de::Visitor<'de>, 374 | { 375 | let pair = self.pair.take().unwrap(); 376 | let span = pair.as_span(); 377 | let mut res = match pair.as_rule() { 378 | Rule::null => visitor.visit_none(), 379 | _ => visitor.visit_some(&mut Deserializer::from_pair(pair)), 380 | }; 381 | error::set_location(&mut res, &span); 382 | res 383 | } 384 | 385 | fn deserialize_newtype_struct(self, _name: &str, visitor: V) -> Result 386 | where 387 | V: de::Visitor<'de>, 388 | { 389 | let span = self.pair.as_ref().unwrap().as_span(); 390 | let mut res = visitor.visit_newtype_struct(self); 391 | error::set_location(&mut res, &span); 392 | res 393 | } 394 | 395 | forward_to_deserialize_any! { 396 | bool char str string bytes byte_buf unit unit_struct seq 397 | tuple tuple_struct map struct identifier ignored_any 398 | } 399 | } 400 | 401 | fn parse_bool(pair: &Pair<'_, Rule>) -> bool { 402 | match pair.as_str() { 403 | "true" => true, 404 | "false" => false, 405 | _ => unreachable!(), 406 | } 407 | } 408 | 409 | fn parse_string(pair: Pair<'_, Rule>) -> Result { 410 | let span = pair.as_span(); 411 | let mut res = pair 412 | .into_inner() 413 | .map(|component| match component.as_rule() { 414 | Rule::char_literal => Ok(String::from(component.as_str())), 415 | Rule::char_escape_sequence => Ok(parse_char_escape_sequence(&component)), 416 | Rule::nul_escape_sequence => Ok(String::from("\u{0000}")), 417 | Rule::hex_escape_sequence | Rule::unicode_escape_sequence => { 418 | let hex_escape = parse_hex(component.as_str())?; 419 | match char::from_u32(hex_escape) { 420 | Some(s) => Ok(s.to_string()), 421 | None => Err(de::Error::custom("error parsing hex prefix")), 422 | } 423 | } 424 | _ => unreachable!(), 425 | }) 426 | .collect(); 427 | error::set_location(&mut res, &span); 428 | res 429 | } 430 | 431 | fn parse_char_escape_sequence(pair: &Pair<'_, Rule>) -> String { 432 | String::from(match pair.as_str() { 433 | "b" => "\u{0008}", 434 | "f" => "\u{000C}", 435 | "n" => "\n", 436 | "r" => "\r", 437 | "t" => "\t", 438 | "v" => "\u{000B}", 439 | c => c, 440 | }) 441 | } 442 | 443 | fn parse_number(pair: &Pair<'_, Rule>) -> Result { 444 | match pair.as_str() { 445 | "Infinity" => Ok(f64::INFINITY), 446 | "-Infinity" => Ok(f64::NEG_INFINITY), 447 | "NaN" | "-NaN" => Ok(f64::NAN), 448 | s if is_hex_literal(s) => parse_hex(&s[2..]).map(f64::from), 449 | s => { 450 | if let Ok(r) = s.parse::() { 451 | if r.is_finite() { 452 | Ok(r) 453 | } else { 454 | Err(de::Error::custom("error parsing number: too large")) 455 | } 456 | } else { 457 | Err(de::Error::custom("error parsing number")) 458 | } 459 | } 460 | } 461 | } 462 | 463 | fn parse_integer(pair: &Pair<'_, Rule>) -> Result { 464 | match pair.as_str() { 465 | s if is_hex_literal(s) => Ok(parse_hex(&s[2..])? as i64), 466 | s => s 467 | .parse() 468 | .map_err(|_| de::Error::custom("error parsing integer")), 469 | } 470 | } 471 | 472 | fn is_int(s: &str) -> bool { 473 | !s.contains('.') 474 | && (is_hex_literal(s) 475 | || (!s.contains('e') 476 | && !s.contains('E') 477 | && !s.contains("Infinity") 478 | && !s.contains("NaN"))) 479 | } 480 | 481 | fn parse_hex(s: &str) -> Result { 482 | u32::from_str_radix(s, 16).map_err(|_| de::Error::custom("error parsing hex")) 483 | } 484 | 485 | fn is_hex_literal(s: &str) -> bool { 486 | s.len() > 2 && (&s[..2] == "0x" || &s[..2] == "0X") 487 | } 488 | 489 | struct Seq<'de> { 490 | pairs: VecDeque>, 491 | } 492 | 493 | impl<'de> Seq<'de> { 494 | pub fn new(pair: Pair<'de, Rule>) -> Self { 495 | Self { 496 | pairs: pair.into_inner().collect(), 497 | } 498 | } 499 | } 500 | 501 | impl<'de> de::SeqAccess<'de> for Seq<'de> { 502 | type Error = Error; 503 | 504 | fn size_hint(&self) -> Option { 505 | Some(self.pairs.len()) 506 | } 507 | 508 | fn next_element_seed(&mut self, seed: T) -> Result> 509 | where 510 | T: de::DeserializeSeed<'de>, 511 | { 512 | if let Some(pair) = self.pairs.pop_front() { 513 | seed.deserialize(&mut Deserializer::from_pair(pair)) 514 | .map(Some) 515 | } else { 516 | Ok(None) 517 | } 518 | } 519 | } 520 | 521 | struct Map<'de> { 522 | pairs: VecDeque>, 523 | } 524 | 525 | impl<'de> Map<'de> { 526 | pub fn new(pair: Pair<'de, Rule>) -> Self { 527 | Self { 528 | pairs: pair.into_inner().collect(), 529 | } 530 | } 531 | } 532 | 533 | impl<'de> de::MapAccess<'de> for Map<'de> { 534 | type Error = Error; 535 | 536 | fn size_hint(&self) -> Option { 537 | Some(self.pairs.len() / 2) 538 | } 539 | 540 | fn next_key_seed(&mut self, seed: K) -> Result> 541 | where 542 | K: de::DeserializeSeed<'de>, 543 | { 544 | if let Some(pair) = self.pairs.pop_front() { 545 | seed.deserialize(&mut Deserializer::from_pair(pair)) 546 | .map(Some) 547 | } else { 548 | Ok(None) 549 | } 550 | } 551 | 552 | fn next_value_seed(&mut self, seed: V) -> Result 553 | where 554 | V: de::DeserializeSeed<'de>, 555 | { 556 | seed.deserialize(&mut Deserializer::from_pair( 557 | self.pairs.pop_front().unwrap(), 558 | )) 559 | } 560 | } 561 | 562 | struct Enum<'de> { 563 | pair: Pair<'de, Rule>, 564 | } 565 | 566 | impl<'de> de::EnumAccess<'de> for Enum<'de> { 567 | type Error = Error; 568 | type Variant = Variant<'de>; 569 | 570 | fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant)> 571 | where 572 | V: de::DeserializeSeed<'de>, 573 | { 574 | let span = self.pair.as_span(); 575 | let mut res = (move || match self.pair.as_rule() { 576 | Rule::string => { 577 | let tag = seed.deserialize(&mut Deserializer::from_pair(self.pair))?; 578 | Ok((tag, Variant { pair: None })) 579 | } 580 | Rule::object => { 581 | let mut pairs = self.pair.into_inner(); 582 | 583 | if let Some(tag_pair) = pairs.next() { 584 | let tag = seed.deserialize(&mut Deserializer::from_pair(tag_pair))?; 585 | Ok((tag, Variant { pair: pairs.next() })) 586 | } else { 587 | Err(de::Error::custom("expected a nonempty object")) 588 | } 589 | } 590 | _ => Err(de::Error::custom("expected a string or an object")), 591 | })(); 592 | error::set_location(&mut res, &span); 593 | res 594 | } 595 | } 596 | 597 | struct Variant<'de> { 598 | pair: Option>, 599 | } 600 | 601 | impl<'de> de::VariantAccess<'de> for Variant<'de> { 602 | type Error = Error; 603 | 604 | fn unit_variant(self) -> Result<()> { 605 | if let Some(pair) = self.pair { 606 | serde::Deserialize::deserialize(&mut Deserializer::from_pair(pair)) 607 | } else { 608 | Ok(()) 609 | } 610 | } 611 | 612 | fn newtype_variant_seed(self, seed: T) -> Result 613 | where 614 | T: de::DeserializeSeed<'de>, 615 | { 616 | seed.deserialize(&mut Deserializer::from_pair(self.pair.unwrap())) 617 | } 618 | 619 | fn tuple_variant(self, _len: usize, visitor: V) -> Result 620 | where 621 | V: de::Visitor<'de>, 622 | { 623 | match self.pair { 624 | Some(pair) => match pair.as_rule() { 625 | Rule::array => visitor.visit_seq(Seq::new(pair)), 626 | _ => Err(de::Error::custom("expected an array")), 627 | }, 628 | None => Err(de::Error::custom("expected an array")), 629 | } 630 | } 631 | 632 | fn struct_variant(self, _fields: &'static [&'static str], visitor: V) -> Result 633 | where 634 | V: de::Visitor<'de>, 635 | { 636 | match self.pair { 637 | Some(pair) => match pair.as_rule() { 638 | Rule::object => visitor.visit_map(Map::new(pair)), 639 | _ => Err(de::Error::custom("expected an object")), 640 | }, 641 | None => Err(de::Error::custom("expected an object")), 642 | } 643 | } 644 | } 645 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use pest::Span; 2 | use serde::{de, ser}; 3 | use std::fmt::{self, Display}; 4 | 5 | use crate::de::Rule; 6 | 7 | /// Alias for a `Result` with error type `json5::Error` 8 | pub type Result = std::result::Result; 9 | 10 | /// One-based line and column at which the error was detected. 11 | #[derive(Clone, Debug, PartialEq)] 12 | pub struct Location { 13 | /// The one-based line number of the error. 14 | pub line: usize, 15 | /// The one-based column number of the error. 16 | pub column: usize, 17 | } 18 | 19 | impl From<&Span<'_>> for Location { 20 | fn from(s: &Span<'_>) -> Self { 21 | let (line, column) = s.start_pos().line_col(); 22 | Self { line, column } 23 | } 24 | } 25 | 26 | /// A bare bones error type which currently just collapses all the underlying errors in to a single 27 | /// string... This is fine for displaying to the user, but not very useful otherwise. Work to be 28 | /// done here. 29 | #[derive(Clone, Debug, PartialEq)] 30 | pub enum Error { 31 | /// Just shove everything in a single variant for now. 32 | Message { 33 | /// The error message. 34 | msg: String, 35 | /// The location of the error, if applicable. 36 | location: Option, 37 | }, 38 | } 39 | 40 | impl From> for Error { 41 | fn from(err: pest::error::Error) -> Self { 42 | let (line, column) = match err.line_col { 43 | pest::error::LineColLocation::Pos((l, c)) => (l, c), 44 | pest::error::LineColLocation::Span((l, c), (_, _)) => (l, c), 45 | }; 46 | Error::Message { 47 | msg: err.to_string(), 48 | location: Some(Location { line, column }), 49 | } 50 | } 51 | } 52 | 53 | impl From for Error { 54 | fn from(err: std::io::Error) -> Self { 55 | Error::Message { 56 | msg: err.to_string(), 57 | location: None, 58 | } 59 | } 60 | } 61 | 62 | impl From for Error { 63 | fn from(err: std::string::FromUtf8Error) -> Self { 64 | Error::Message { 65 | msg: err.to_string(), 66 | location: None, 67 | } 68 | } 69 | } 70 | 71 | impl From for Error { 72 | fn from(err: std::str::Utf8Error) -> Self { 73 | Error::Message { 74 | msg: err.to_string(), 75 | location: None, 76 | } 77 | } 78 | } 79 | 80 | impl ser::Error for Error { 81 | fn custom(msg: T) -> Self { 82 | Error::Message { 83 | msg: msg.to_string(), 84 | location: None, 85 | } 86 | } 87 | } 88 | 89 | impl de::Error for Error { 90 | fn custom(msg: T) -> Self { 91 | Error::Message { 92 | msg: msg.to_string(), 93 | location: None, 94 | } 95 | } 96 | } 97 | 98 | impl Display for Error { 99 | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 100 | match self { 101 | Error::Message { ref msg, .. } => write!(formatter, "{}", msg), 102 | } 103 | } 104 | } 105 | 106 | impl std::error::Error for Error {} 107 | 108 | /// Adds location information from `span`, if `res` is an error. 109 | pub fn set_location(res: &mut Result, span: &Span<'_>) { 110 | if let Err(ref mut e) = res { 111 | let Error::Message { location, .. } = e; 112 | if location.is_none() { 113 | let (line, column) = span.start_pos().line_col(); 114 | *location = Some(Location { line, column }); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! JSON5 is a superset of [JSON][] with an expanded syntax including some productions from 2 | //! [ECMAScript 5.1][]. 3 | //! 4 | //! In particular, JSON5 allows comments, trailing commas, object keys without quotes, single 5 | //! quoted strings and more. See the [JSON5 project page][] for full details. 6 | //! 7 | //! ```json5,ignore 8 | //! { 9 | //! // comments 10 | //! unquoted: 'and you can quote me on that', 11 | //! singleQuotes: 'I can use "double quotes" here', 12 | //! lineBreaks: "Look, Mom! \ 13 | //! No \\n's!", 14 | //! hexadecimal: 0xdecaf, 15 | //! leadingDecimalPoint: .8675309, andTrailing: 8675309., 16 | //! positiveSign: +1, 17 | //! trailingComma: 'in objects', andIn: ['arrays',], 18 | //! "backwardsCompatible": "with JSON", 19 | //! } 20 | //! ``` 21 | //! 22 | //! This crate provides functions for deserializing JSON5 text into a Rust datatype and for 23 | //! serializing a Rust datatype as JSON5 text, both via the [Serde framework][]. 24 | //! 25 | //! # Deserialization 26 | //! 27 | //! Implementing Serde’s [`Deserialize`][] trait on your type will allow you to parse JSON5 28 | //! text into a value of that type with [`from_str`][]. 29 | //! 30 | //! ```rust 31 | //! use serde_derive::Deserialize; 32 | //! 33 | //! #[derive(Deserialize, Debug, PartialEq)] 34 | //! struct Config { 35 | //! message: String, 36 | //! n: i32, 37 | //! } 38 | //! 39 | //! let config = " 40 | //! { 41 | //! // A traditional message. 42 | //! message: 'hello world', 43 | //! 44 | //! // A number for some reason. 45 | //! n: 42, 46 | //! } 47 | //! "; 48 | //! 49 | //! assert_eq!( 50 | //! serde_json5::from_str(config), 51 | //! Ok(Config { 52 | //! message: "hello world".to_string(), 53 | //! n: 42, 54 | //! }), 55 | //! ); 56 | //! ``` 57 | //! 58 | //! There are many ways to customise the deserialization (e.g. deserializing `camelCase` field 59 | //! names into a struct with `snake_case` fields). See the Serde docs, especially the 60 | //! [Attributes][], [Custom serialization][] and [Examples][] sections. 61 | //! 62 | //! # Serialization 63 | //! 64 | //! Similarly, implementing [`Serialize`][] on a Rust type allows you to produce a JSON5 65 | //! serialization of values of that type with [`to_string`][]. At present the serializer will just 66 | //! produce JSON (since it's a valid subset of JSON5), but future work will allow specifying the 67 | //! output style (single over double quotes, trailing commas, indentation etc.). 68 | //! 69 | //! ```rust 70 | //! use serde_derive::Serialize; 71 | //! 72 | //! #[derive(Serialize, PartialEq, Debug)] 73 | //! #[serde(untagged)] 74 | //! enum Val { 75 | //! Number(f64), 76 | //! Bool(bool), 77 | //! String(String), 78 | //! } 79 | //! 80 | //! assert_eq!( 81 | //! serde_json5::to_string(&vec![ 82 | //! Val::Number(42.), 83 | //! Val::Bool(true), 84 | //! Val::String("hello".to_owned()), 85 | //! ]), 86 | //! Ok("[42,true,\"hello\"]".to_owned()), 87 | //! ) 88 | //! ``` 89 | //! 90 | //! There are many ways to customise the serialization (e.g. serializing `snake_case` struct fields 91 | //! as `camelCase`). See the Serde docs, especially the [Attributes][], [Custom serialization][] 92 | //! and [Examples][] sections. 93 | //! 94 | //! # Limitations 95 | //! 96 | //! At the time of writing the following is unsupported: 97 | //! 98 | //! - deserializing into borrowed types (e.g. fields of type `&str`) 99 | //! 100 | //! - serializing or deserializing [byte arrays][] 101 | //! 102 | //! - specifying the style of JSON5 output from the serializer (single over double quotes, trailing 103 | //! commas, indentation etc.) 104 | //! 105 | //! [JSON]: https://tools.ietf.org/html/rfc7159 106 | //! [ECMAScript 5.1]: https://www.ecma-international.org/ecma-262/5.1/ 107 | //! [JSON5 project page]: https://json5.org/ 108 | //! [Serde framework]: https://serde.rs/ 109 | //! [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html 110 | //! [`from_str`]: fn.from_str.html 111 | //! [Attributes]: https://serde.rs/attributes.html 112 | //! [Custom serialization]: https://serde.rs/custom-serialization.html 113 | //! [Examples]: https://serde.rs/examples.html 114 | //! [`Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html 115 | //! [`to_string`]: fn.to_string.html 116 | //! [byte arrays]: https://serde.rs/data-model.html#types 117 | 118 | #![warn(missing_docs)] 119 | #![warn(rust_2018_idioms)] 120 | 121 | mod de; 122 | mod error; 123 | mod ser; 124 | 125 | pub use crate::de::{from_reader, from_slice, from_str, Deserializer}; 126 | pub use crate::error::{Error, Location, Result}; 127 | pub use crate::ser::{to_string, to_writer}; 128 | -------------------------------------------------------------------------------- /src/ser.rs: -------------------------------------------------------------------------------- 1 | use serde::ser::{self, Serialize}; 2 | use std::fmt::Display; 3 | use std::io::Write; 4 | use std::{f32, f64, io}; 5 | 6 | use crate::error::{Error, Result}; 7 | 8 | /// Attempts to serialize the input as a JSON5 string (actually a JSON string). 9 | pub fn to_string(value: &T) -> Result 10 | where 11 | T: Serialize, 12 | { 13 | let mut serializer = Serializer::new(Vec::::new()); 14 | value.serialize(&mut serializer)?; 15 | let output = String::from_utf8(serializer.take_output())?; 16 | Ok(output) 17 | } 18 | 19 | /// Attempts to serialize the input as JSON5 string into the I/O stream. 20 | pub fn to_writer(writer: W, value: &T) -> Result<()> 21 | where 22 | W: io::Write, 23 | T: ?Sized + Serialize, 24 | { 25 | let mut ser = Serializer::new(writer); 26 | value.serialize(&mut ser) 27 | } 28 | 29 | struct Serializer { 30 | output: InnerWriter, 31 | // TODO settings for formatting (single vs double quotes, whitespace etc) 32 | } 33 | 34 | impl Serializer { 35 | fn new(writer: W) -> Self { 36 | Self { 37 | output: InnerWriter { 38 | writer, 39 | last_byte: 0, 40 | }, 41 | } 42 | } 43 | 44 | fn take_output(self) -> W { 45 | self.output.writer 46 | } 47 | } 48 | 49 | struct InnerWriter { 50 | writer: W, 51 | last_byte: u8, 52 | } 53 | 54 | impl InnerWriter { 55 | fn ends_with(&self, c: char) -> bool { 56 | self.last_byte == (c as u8) 57 | } 58 | } 59 | 60 | impl io::Write for InnerWriter { 61 | fn write(&mut self, buf: &[u8]) -> io::Result { 62 | let written = self.writer.write(buf)?; 63 | if written > 0 { 64 | self.last_byte = buf[written - 1]; 65 | } 66 | Ok(written) 67 | } 68 | 69 | fn flush(&mut self) -> io::Result<()> { 70 | self.writer.flush() 71 | } 72 | } 73 | 74 | impl Serializer { 75 | fn write_display(&mut self, v: &T) -> Result<()> 76 | where 77 | T: Display, 78 | { 79 | write!(&mut self.output, "{}", v)?; 80 | Ok(()) 81 | } 82 | } 83 | 84 | impl ser::Serializer for &mut Serializer { 85 | type Ok = (); 86 | type Error = Error; 87 | 88 | type SerializeSeq = Self; 89 | type SerializeTuple = Self; 90 | type SerializeTupleStruct = Self; 91 | type SerializeTupleVariant = Self; 92 | type SerializeMap = Self; 93 | type SerializeStruct = Self; 94 | type SerializeStructVariant = Self; 95 | 96 | fn serialize_bool(self, v: bool) -> Result<()> { 97 | self.write_display(&v) 98 | } 99 | 100 | fn serialize_i8(self, v: i8) -> Result<()> { 101 | self.write_display(&v) 102 | } 103 | 104 | fn serialize_i16(self, v: i16) -> Result<()> { 105 | self.write_display(&v) 106 | } 107 | 108 | fn serialize_i32(self, v: i32) -> Result<()> { 109 | self.write_display(&v) 110 | } 111 | 112 | fn serialize_i64(self, v: i64) -> Result<()> { 113 | self.write_display(&v) 114 | } 115 | 116 | fn serialize_u8(self, v: u8) -> Result<()> { 117 | self.write_display(&v) 118 | } 119 | 120 | fn serialize_u16(self, v: u16) -> Result<()> { 121 | self.write_display(&v) 122 | } 123 | 124 | fn serialize_u32(self, v: u32) -> Result<()> { 125 | self.write_display(&v) 126 | } 127 | 128 | fn serialize_u64(self, v: u64) -> Result<()> { 129 | self.write_display(&v) 130 | } 131 | 132 | fn serialize_f32(self, v: f32) -> Result<()> { 133 | if v == f32::INFINITY { 134 | self.output.write_all(b"Infinity")?; 135 | } else if v == f32::NEG_INFINITY { 136 | self.output.write_all(b"-Infinity")?; 137 | } else if v.is_nan() { 138 | self.output.write_all(b"NaN")?; 139 | } else { 140 | self.write_display(&v)?; 141 | } 142 | Ok(()) 143 | } 144 | 145 | fn serialize_f64(self, v: f64) -> Result<()> { 146 | if v == f64::INFINITY { 147 | self.output.write_all(b"Infinity")?; 148 | } else if v == f64::NEG_INFINITY { 149 | self.output.write_all(b"-Infinity")?; 150 | } else if v.is_nan() { 151 | self.output.write_all(b"NaN")?; 152 | } else { 153 | self.write_display(&v)?; 154 | } 155 | Ok(()) 156 | } 157 | 158 | fn serialize_char(self, v: char) -> Result<()> { 159 | // A char encoded as UTF-8 takes 4 bytes at most. 160 | let mut buf = [0; 4]; 161 | self.serialize_str(v.encode_utf8(&mut buf)) 162 | } 163 | 164 | fn serialize_str(self, v: &str) -> Result<()> { 165 | write!(&mut self.output, "\"{}\"", escape(v))?; 166 | Ok(()) 167 | } 168 | 169 | fn serialize_bytes(self, _v: &[u8]) -> Result<()> { 170 | unimplemented!() // TODO 171 | } 172 | 173 | fn serialize_none(self) -> Result<()> { 174 | self.serialize_unit() 175 | } 176 | 177 | fn serialize_some(self, value: &T) -> Result<()> 178 | where 179 | T: ?Sized + Serialize, 180 | { 181 | value.serialize(self) 182 | } 183 | 184 | fn serialize_unit(self) -> Result<()> { 185 | self.output.write_all(b"null")?; 186 | Ok(()) 187 | } 188 | 189 | fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { 190 | self.serialize_unit() 191 | } 192 | 193 | fn serialize_unit_variant( 194 | self, 195 | _name: &'static str, 196 | _variant_index: u32, 197 | variant: &'static str, 198 | ) -> Result<()> { 199 | self.serialize_str(variant) 200 | } 201 | 202 | fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> 203 | where 204 | T: ?Sized + Serialize, 205 | { 206 | value.serialize(self) 207 | } 208 | 209 | fn serialize_newtype_variant( 210 | self, 211 | _name: &'static str, 212 | _variant_index: u32, 213 | variant: &'static str, 214 | value: &T, 215 | ) -> Result<()> 216 | where 217 | T: ?Sized + Serialize, 218 | { 219 | self.output.write_all(b"{")?; 220 | variant.serialize(&mut *self)?; // TODO drop the quotes where possible 221 | self.output.write_all(b":")?; 222 | value.serialize(&mut *self)?; 223 | self.output.write_all(b"}")?; 224 | Ok(()) 225 | } 226 | 227 | fn serialize_seq(self, _len: Option) -> Result { 228 | self.output.write_all(b"[")?; 229 | Ok(self) 230 | } 231 | 232 | fn serialize_tuple(self, len: usize) -> Result { 233 | self.serialize_seq(Some(len)) 234 | } 235 | 236 | fn serialize_tuple_struct( 237 | self, 238 | _name: &'static str, 239 | len: usize, 240 | ) -> Result { 241 | self.serialize_seq(Some(len)) 242 | } 243 | 244 | fn serialize_tuple_variant( 245 | self, 246 | _name: &'static str, 247 | _variant_index: u32, 248 | variant: &'static str, 249 | _len: usize, 250 | ) -> Result { 251 | self.output.write_all(b"{")?; 252 | variant.serialize(&mut *self)?; 253 | self.output.write_all(b":[")?; 254 | Ok(self) 255 | } 256 | 257 | fn serialize_map(self, _len: Option) -> Result { 258 | self.output.write_all(b"{")?; 259 | Ok(self) 260 | } 261 | 262 | fn serialize_struct(self, _name: &'static str, len: usize) -> Result { 263 | self.serialize_map(Some(len)) 264 | } 265 | 266 | fn serialize_struct_variant( 267 | self, 268 | _name: &'static str, 269 | _variant_index: u32, 270 | variant: &'static str, 271 | _len: usize, 272 | ) -> Result { 273 | self.output.write_all(b"{")?; 274 | variant.serialize(&mut *self)?; 275 | self.output.write_all(b":{")?; 276 | Ok(self) 277 | } 278 | } 279 | 280 | impl ser::SerializeSeq for &mut Serializer { 281 | type Ok = (); 282 | type Error = Error; 283 | 284 | fn serialize_element(&mut self, value: &T) -> Result<()> 285 | where 286 | T: ?Sized + Serialize, 287 | { 288 | if !self.output.ends_with('[') { 289 | self.output.write_all(b",")?; 290 | } 291 | value.serialize(&mut **self) 292 | } 293 | 294 | fn end(self) -> Result<()> { 295 | self.output.write_all(b"]")?; 296 | Ok(()) 297 | } 298 | } 299 | 300 | impl ser::SerializeTuple for &mut Serializer { 301 | type Ok = (); 302 | type Error = Error; 303 | 304 | fn serialize_element(&mut self, value: &T) -> Result<()> 305 | where 306 | T: ?Sized + Serialize, 307 | { 308 | ser::SerializeSeq::serialize_element(self, value) 309 | } 310 | 311 | fn end(self) -> Result<()> { 312 | ser::SerializeSeq::end(self) 313 | } 314 | } 315 | 316 | impl ser::SerializeTupleStruct for &mut Serializer { 317 | type Ok = (); 318 | type Error = Error; 319 | 320 | fn serialize_field(&mut self, value: &T) -> Result<()> 321 | where 322 | T: ?Sized + Serialize, 323 | { 324 | ser::SerializeSeq::serialize_element(self, value) 325 | } 326 | 327 | fn end(self) -> Result<()> { 328 | ser::SerializeSeq::end(self) 329 | } 330 | } 331 | 332 | impl ser::SerializeTupleVariant for &mut Serializer { 333 | type Ok = (); 334 | type Error = Error; 335 | 336 | fn serialize_field(&mut self, value: &T) -> Result<()> 337 | where 338 | T: ?Sized + Serialize, 339 | { 340 | ser::SerializeSeq::serialize_element(self, value) 341 | } 342 | 343 | fn end(self) -> Result<()> { 344 | self.output.write_all(b"]}")?; 345 | Ok(()) 346 | } 347 | } 348 | 349 | impl ser::SerializeMap for &mut Serializer { 350 | type Ok = (); 351 | type Error = Error; 352 | 353 | fn serialize_key(&mut self, key: &T) -> Result<()> 354 | where 355 | T: ?Sized + Serialize, 356 | { 357 | if !self.output.ends_with('{') { 358 | self.output.write_all(b",")?; 359 | } 360 | key.serialize(&mut **self) 361 | } 362 | 363 | fn serialize_value(&mut self, value: &T) -> Result<()> 364 | where 365 | T: ?Sized + Serialize, 366 | { 367 | self.output.write_all(b":")?; 368 | value.serialize(&mut **self) 369 | } 370 | 371 | fn end(self) -> Result<()> { 372 | self.output.write_all(b"}")?; 373 | Ok(()) 374 | } 375 | } 376 | 377 | impl ser::SerializeStruct for &mut Serializer { 378 | type Ok = (); 379 | type Error = Error; 380 | 381 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> 382 | where 383 | T: ?Sized + Serialize, 384 | { 385 | ser::SerializeMap::serialize_key(self, key)?; 386 | ser::SerializeMap::serialize_value(self, value) 387 | } 388 | 389 | fn end(self) -> Result<()> { 390 | ser::SerializeMap::end(self) 391 | } 392 | } 393 | 394 | impl ser::SerializeStructVariant for &mut Serializer { 395 | type Ok = (); 396 | type Error = Error; 397 | 398 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> 399 | where 400 | T: ?Sized + Serialize, 401 | { 402 | ser::SerializeStruct::serialize_field(self, key, value) 403 | } 404 | 405 | fn end(self) -> Result<()> { 406 | self.output.write_all(b"}}")?; 407 | Ok(()) 408 | } 409 | } 410 | 411 | fn escape(v: &str) -> String { 412 | v.chars() 413 | .flat_map(|c| match c { 414 | '"' => vec!['\\', c], 415 | '\n' => vec!['\\', 'n'], 416 | '\r' => vec!['\\', 'r'], 417 | '\t' => vec!['\\', 't'], 418 | '\\' => vec!['\\', '\\'], 419 | '\u{0008}' => vec!['\\', 'b'], 420 | '\u{000c}' => vec!['\\', 'f'], 421 | c => vec![c], 422 | }) 423 | .collect() 424 | } 425 | -------------------------------------------------------------------------------- /tests/adapted_from_js_reference.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | 3 | use std::collections::HashMap; 4 | use std::f64; 5 | 6 | use common::{deserializes_to, deserializes_to_nan_f64}; 7 | 8 | // The following tests are adapted from https://github.com/json5/json5/blob/d828908384ce8dc40d8dde017ae82afd1b952d79/test/parse.js 9 | 10 | // objects 11 | 12 | #[test] 13 | fn parses_empty_objects() { 14 | let m: HashMap = HashMap::new(); 15 | deserializes_to("{}", m); 16 | } 17 | 18 | #[test] 19 | fn parses_double_string_property_names() { 20 | let mut m = HashMap::new(); 21 | m.insert("a".to_owned(), 1); 22 | deserializes_to("{\"a\":1}", m); 23 | } 24 | 25 | #[test] 26 | fn parses_single_string_property_names() { 27 | let mut m = HashMap::new(); 28 | m.insert("a".to_owned(), 1); 29 | deserializes_to("{'a':1}", m); 30 | } 31 | 32 | #[test] 33 | fn parses_unquoted_property_names() { 34 | let mut m = HashMap::new(); 35 | m.insert("a".to_owned(), 1); 36 | deserializes_to("{a:1}", m); 37 | } 38 | 39 | #[test] 40 | fn parses_special_character_property_names() { 41 | let mut m = HashMap::new(); 42 | m.insert("$_".to_owned(), 1); 43 | m.insert("_$".to_owned(), 2); 44 | m.insert("a\u{200C}".to_owned(), 3); 45 | deserializes_to("{$_:1,_$:2,a\u{200C}:3}", m); 46 | } 47 | 48 | #[test] 49 | fn parses_unicode_property_names() { 50 | let mut m = HashMap::new(); 51 | m.insert("ùńîċõďë".to_owned(), 9); 52 | deserializes_to("{ùńîċõďë:9}", m); 53 | } 54 | 55 | #[test] 56 | fn parses_escaped_property_names() { 57 | let mut m = HashMap::new(); 58 | m.insert("ab".to_owned(), 1); 59 | m.insert("$_".to_owned(), 2); 60 | m.insert("_$".to_owned(), 3); 61 | deserializes_to("{\\u0061\\u0062:1,\\u0024\\u005F:2,\\u005F\\u0024:3}", m); 62 | } 63 | 64 | #[test] 65 | fn parses_multiple_properties() { 66 | let mut m = HashMap::new(); 67 | m.insert("abc".to_owned(), 1); 68 | m.insert("def".to_owned(), 2); 69 | deserializes_to("{abc:1,def:2}", m); 70 | } 71 | 72 | #[test] 73 | fn parses_nested_objects() { 74 | let mut inner = HashMap::new(); 75 | inner.insert("b".to_owned(), 2); 76 | let mut outer = HashMap::new(); 77 | outer.insert("a".to_owned(), inner); 78 | deserializes_to("{a:{b:2}}", outer); 79 | } 80 | 81 | // arrays 82 | 83 | #[test] 84 | fn parses_empty_arrays() { 85 | let v: Vec = vec![]; 86 | deserializes_to("[]", v); 87 | } 88 | 89 | #[test] 90 | fn parses_array_values() { 91 | deserializes_to("[1]", vec![1]); 92 | } 93 | 94 | #[test] 95 | fn parses_multiple_array_values() { 96 | deserializes_to("[1,2]", vec![1, 2]); 97 | } 98 | 99 | #[test] 100 | fn parses_nested_arrays() { 101 | deserializes_to("[1,[2,3]]", (1, vec![2, 3])); 102 | } 103 | 104 | #[test] 105 | fn parses_nulls() { 106 | deserializes_to("null", ()); 107 | } 108 | 109 | #[test] 110 | fn parses_true() { 111 | deserializes_to("true", true); 112 | } 113 | 114 | #[test] 115 | fn parses_false() { 116 | deserializes_to("false", false); 117 | } 118 | 119 | // numbers 120 | 121 | #[test] 122 | fn parses_leading_zeroes() { 123 | deserializes_to("[0,0,0e0]", vec![0, 0, 0]); 124 | } 125 | 126 | #[test] 127 | fn parses_integers() { 128 | deserializes_to("[1,23,456,7890]", vec![1, 23, 456, 7890]); 129 | } 130 | 131 | #[test] 132 | fn parses_signed_numbers() { 133 | deserializes_to("[-1,+2,-.1,-0]", vec![-1., 2., -0.1, -0.]); 134 | } 135 | 136 | #[test] 137 | fn parses_leading_decimal_points() { 138 | deserializes_to("[.1,.23]", vec![0.1, 0.23]); 139 | } 140 | 141 | #[test] 142 | fn parses_fractional_numbers() { 143 | deserializes_to("[1.0,1.23]", vec![1., 1.23]); 144 | } 145 | 146 | #[test] 147 | fn parses_exponents() { 148 | deserializes_to( 149 | "[1e0,1e1,1e01,1.e0,1.1e0,1e-1,1e+1]", 150 | vec![1., 10., 10., 1., 1.1, 0.1, 10.], 151 | ); 152 | } 153 | 154 | #[test] 155 | fn parses_hexadecimal_numbers() { 156 | deserializes_to("[0x1,0x10,0xff,0xFF]", vec![1, 16, 255, 255]); 157 | } 158 | 159 | #[test] 160 | fn parses_signed_and_unsiged_infinity() { 161 | deserializes_to( 162 | "[Infinity,-Infinity]", 163 | vec![f64::INFINITY, f64::NEG_INFINITY], 164 | ); 165 | } 166 | 167 | #[test] 168 | fn parses_signed_and_unsigned_nan() { 169 | deserializes_to_nan_f64("NaN"); 170 | deserializes_to_nan_f64("-NaN"); 171 | } 172 | 173 | // strings 174 | 175 | #[test] 176 | fn parses_double_quoted_strings() { 177 | deserializes_to("\"abc\"", "abc".to_owned()); 178 | } 179 | 180 | #[test] 181 | fn parses_single_quoted_strings() { 182 | deserializes_to("'abc'", "abc".to_owned()); 183 | } 184 | 185 | #[test] 186 | fn parses_nested_quotes_strings() { 187 | deserializes_to("['\"',\"'\"]", vec!["\"".to_owned(), "'".to_owned()]); 188 | } 189 | 190 | #[test] 191 | fn parses_escaped_characters() { 192 | deserializes_to( 193 | "'\\b\\f\\n\\r\\t\\v\\0\\x0f\\u01fF\\\n\\\r\n\\\r\\\u{2028}\\\u{2029}\\a\\'\\\"'", 194 | "\u{0008}\u{000C}\n\r\t\u{000B}\0\x0f\u{01FF}a'\"".to_owned(), 195 | ); 196 | } 197 | 198 | // comments 199 | 200 | #[test] 201 | fn parses_single_line_comments() { 202 | let m: HashMap = HashMap::new(); 203 | deserializes_to("{//comment\n}", m); 204 | } 205 | 206 | #[test] 207 | fn parses_single_line_comments_at_end_of_input() { 208 | let m: HashMap = HashMap::new(); 209 | deserializes_to("{}//comment", m); 210 | } 211 | 212 | #[test] 213 | fn parses_multi_line_comments() { 214 | let m: HashMap = HashMap::new(); 215 | deserializes_to("{/*comment\n** */}", m); 216 | } 217 | 218 | #[test] 219 | fn parses_whitespace() { 220 | let m: HashMap = HashMap::new(); 221 | deserializes_to( 222 | "{\t\u{000B}\u{000C} \u{00A0}\u{FEFF}\n\r\u{2028}\u{2029}\u{2003}}", 223 | m, 224 | ); 225 | } 226 | -------------------------------------------------------------------------------- /tests/assets/json5_dot_org_example.json5: -------------------------------------------------------------------------------- 1 | { 2 | // comments 3 | unquoted: 'and you can quote me on that', 4 | singleQuotes: 'I can use "double quotes" here', 5 | lineBreaks: "Look, Mom! \ 6 | No \\n's!", 7 | hexadecimal: 0xdecaf, 8 | leadingDecimalPoint: .8675309, andTrailing: 8675309., 9 | positiveSign: +1, 10 | trailingComma: 'in objects', andIn: ['arrays',], 11 | "backwardsCompatible": "with JSON", 12 | } 13 | -------------------------------------------------------------------------------- /tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | use serde_json5::{Error, Location}; 2 | 3 | #[allow(unused)] 4 | pub fn deserializes_to<'a, T>(s: &'a str, v: T) 5 | where 6 | T: ::std::fmt::Debug + ::std::cmp::PartialEq + serde::de::Deserialize<'a>, 7 | { 8 | assert_eq!(serde_json5::from_str::(s), Ok(v)); 9 | } 10 | 11 | #[allow(unused)] 12 | pub fn deserializes_to_nan_f32(s: &str) { 13 | assert!(serde_json5::from_str::(s).unwrap().is_nan()); 14 | } 15 | 16 | #[allow(unused)] 17 | pub fn deserializes_to_nan_f64(s: &str) { 18 | assert!(serde_json5::from_str::(s).unwrap().is_nan()); 19 | } 20 | 21 | #[allow(unused)] 22 | pub fn deserializes_with_error<'a, T>(s: &'a str, error_expected: Error) 23 | where 24 | T: ::std::fmt::Debug + ::std::cmp::PartialEq + serde::de::Deserialize<'a>, 25 | { 26 | assert_eq!(serde_json5::from_str::(s), Err(error_expected)); 27 | } 28 | 29 | #[allow(unused)] 30 | pub fn make_error(msg: impl Into, line: usize, column: usize) -> Error { 31 | Error::Message { 32 | msg: msg.into(), 33 | location: Some(Location { line, column }), 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/de.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | 3 | use serde::de; 4 | use serde_derive::Deserialize; 5 | 6 | use core::f64; 7 | use std::collections::HashMap; 8 | use std::fmt; 9 | 10 | use common::{ 11 | deserializes_to, deserializes_to_nan_f32, deserializes_to_nan_f64, deserializes_with_error, 12 | make_error, 13 | }; 14 | 15 | /// Defines a struct `A` with a `de::Deserializer` implementation that returns an error. Works for 16 | /// visitors that accept a single value. 17 | macro_rules! error_struct { 18 | ($type:ty, $visit_fn:ident, $deserialize_fn:ident) => { 19 | #[derive(Debug, PartialEq)] 20 | struct A; 21 | impl<'de> de::Deserialize<'de> for A { 22 | fn deserialize(deserializer: D) -> Result 23 | where 24 | D: de::Deserializer<'de>, 25 | { 26 | struct Visitor; 27 | impl<'de> de::Visitor<'de> for Visitor { 28 | type Value = A; 29 | fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 30 | f.write_str("...") 31 | } 32 | fn $visit_fn(self, _v: $type) -> Result 33 | where 34 | E: de::Error, 35 | { 36 | Err(de::Error::custom("oops")) 37 | } 38 | } 39 | deserializer.$deserialize_fn(Visitor) 40 | } 41 | } 42 | }; 43 | } 44 | 45 | #[test] 46 | fn deserializes_bool() { 47 | deserializes_to("true", true); 48 | deserializes_to("false", false); 49 | 50 | error_struct!(bool, visit_bool, deserialize_bool); 51 | deserializes_with_error::("\n true", make_error("oops", 2, 2)); 52 | } 53 | 54 | #[test] 55 | fn deserializes_i8() { 56 | let x: i8 = 42; 57 | deserializes_to("0x2A", x); 58 | deserializes_to("0x2a", x); 59 | deserializes_to("0X2A", x); 60 | deserializes_to("0X2a", x); 61 | deserializes_to("0x00002A", x); 62 | deserializes_to("42", x); 63 | deserializes_to("42.", x); 64 | deserializes_to("42.0", x); 65 | deserializes_to("42e0", x); 66 | deserializes_to("4.2e1", x); 67 | deserializes_to(".42e2", x); 68 | deserializes_to("0.42e2", x); 69 | deserializes_to("-42", -x); 70 | deserializes_to("-42.", -x); 71 | deserializes_to("-42.0", -x); 72 | deserializes_to("-42e0", -x); 73 | deserializes_to("-4.2e1", -x); 74 | deserializes_to("-.42e2", -x); 75 | deserializes_to("-0.42e2", -x); 76 | 77 | error_struct!(i8, visit_i8, deserialize_i8); 78 | deserializes_with_error::("\n 42", make_error("oops", 2, 2)); 79 | } 80 | 81 | #[test] 82 | fn deserializes_u8() { 83 | let x: u8 = 42; 84 | deserializes_to("0x2A", x); 85 | deserializes_to("0x2a", x); 86 | deserializes_to("0X2A", x); 87 | deserializes_to("0X2a", x); 88 | deserializes_to("0x00002A", x); 89 | deserializes_to("42", x); 90 | deserializes_to("42.", x); 91 | deserializes_to("42.0", x); 92 | deserializes_to("42e0", x); 93 | deserializes_to("4.2e1", x); 94 | deserializes_to(".42e2", x); 95 | deserializes_to("0.42e2", x); 96 | 97 | error_struct!(u8, visit_u8, deserialize_u8); 98 | deserializes_with_error::("\n 42", make_error("oops", 2, 2)); 99 | } 100 | 101 | #[test] 102 | fn deserializes_i16() { 103 | let x: i16 = 42; 104 | deserializes_to("0x2A", x); 105 | deserializes_to("0x2a", x); 106 | deserializes_to("0X2A", x); 107 | deserializes_to("0X2a", x); 108 | deserializes_to("0x00002A", x); 109 | deserializes_to("42", x); 110 | deserializes_to("42.", x); 111 | deserializes_to("42.0", x); 112 | deserializes_to("42e0", x); 113 | deserializes_to("4.2e1", x); 114 | deserializes_to(".42e2", x); 115 | deserializes_to("0.42e2", x); 116 | deserializes_to("-42", -x); 117 | deserializes_to("-42.", -x); 118 | deserializes_to("-42.0", -x); 119 | deserializes_to("-42e0", -x); 120 | deserializes_to("-4.2e1", -x); 121 | deserializes_to("-.42e2", -x); 122 | deserializes_to("-0.42e2", -x); 123 | 124 | error_struct!(i16, visit_i16, deserialize_i16); 125 | deserializes_with_error::("\n 42", make_error("oops", 2, 2)); 126 | } 127 | 128 | #[test] 129 | fn deserializes_u16() { 130 | let x: u16 = 42; 131 | deserializes_to("0x2A", x); 132 | deserializes_to("0x2a", x); 133 | deserializes_to("0X2A", x); 134 | deserializes_to("0X2a", x); 135 | deserializes_to("0x00002A", x); 136 | deserializes_to("42", x); 137 | deserializes_to("42.", x); 138 | deserializes_to("42.0", x); 139 | deserializes_to("42e0", x); 140 | deserializes_to("4.2e1", x); 141 | deserializes_to(".42e2", x); 142 | deserializes_to("0.42e2", x); 143 | 144 | error_struct!(u16, visit_u16, deserialize_u16); 145 | deserializes_with_error::("\n 42", make_error("oops", 2, 2)); 146 | } 147 | 148 | #[test] 149 | fn deserializes_i32() { 150 | let x: i32 = 42; 151 | deserializes_to("0x2A", x); 152 | deserializes_to("0x2a", x); 153 | deserializes_to("0X2A", x); 154 | deserializes_to("0X2a", x); 155 | deserializes_to("0x00002A", x); 156 | deserializes_to("42", x); 157 | deserializes_to("42.", x); 158 | deserializes_to("42.0", x); 159 | deserializes_to("42e0", x); 160 | deserializes_to("4.2e1", x); 161 | deserializes_to(".42e2", x); 162 | deserializes_to("0.42e2", x); 163 | deserializes_to("-42", -x); 164 | deserializes_to("-42.", -x); 165 | deserializes_to("-42.0", -x); 166 | deserializes_to("-42e0", -x); 167 | deserializes_to("-4.2e1", -x); 168 | deserializes_to("-.42e2", -x); 169 | deserializes_to("-0.42e2", -x); 170 | 171 | error_struct!(i32, visit_i32, deserialize_i32); 172 | deserializes_with_error::("\n 42", make_error("oops", 2, 2)); 173 | } 174 | 175 | #[test] 176 | fn deserializes_u32() { 177 | let x: u32 = 42; 178 | deserializes_to("0x2A", x); 179 | deserializes_to("0x2a", x); 180 | deserializes_to("0X2A", x); 181 | deserializes_to("0X2a", x); 182 | deserializes_to("0x00002A", x); 183 | deserializes_to("42", x); 184 | deserializes_to("42.", x); 185 | deserializes_to("42.0", x); 186 | deserializes_to("42e0", x); 187 | deserializes_to("4.2e1", x); 188 | deserializes_to(".42e2", x); 189 | deserializes_to("0.42e2", x); 190 | 191 | error_struct!(u32, visit_u32, deserialize_u32); 192 | deserializes_with_error::("\n 42", make_error("oops", 2, 2)); 193 | } 194 | 195 | #[test] 196 | fn deserializes_i64() { 197 | let x: i64 = 42; 198 | deserializes_to("0x2A", x); 199 | deserializes_to("0x2a", x); 200 | deserializes_to("0X2A", x); 201 | deserializes_to("0X2a", x); 202 | deserializes_to("0x00002A", x); 203 | deserializes_to("42", x); 204 | deserializes_to("42.", x); 205 | deserializes_to("42.0", x); 206 | deserializes_to("42e0", x); 207 | deserializes_to("4.2e1", x); 208 | deserializes_to(".42e2", x); 209 | deserializes_to("0.42e2", x); 210 | deserializes_to("-42", -x); 211 | deserializes_to("-42.", -x); 212 | deserializes_to("-42.0", -x); 213 | deserializes_to("-42e0", -x); 214 | deserializes_to("-4.2e1", -x); 215 | deserializes_to("-.42e2", -x); 216 | deserializes_to("-0.42e2", -x); 217 | 218 | error_struct!(i64, visit_i64, deserialize_i64); 219 | deserializes_with_error::("\n 42", make_error("oops", 2, 2)); 220 | let over_i64 = format!("\n {}0", i64::MAX); 221 | deserializes_with_error::( 222 | over_i64.as_str(), 223 | make_error("error parsing integer", 2, 2), 224 | ); 225 | } 226 | 227 | #[test] 228 | fn deserializes_u64() { 229 | let x: u64 = 42; 230 | deserializes_to("0x2A", x); 231 | deserializes_to("0x2a", x); 232 | deserializes_to("0X2A", x); 233 | deserializes_to("0X2a", x); 234 | deserializes_to("0x00002A", x); 235 | deserializes_to("42", x); 236 | deserializes_to("42.", x); 237 | deserializes_to("42.0", x); 238 | deserializes_to("42e0", x); 239 | deserializes_to("4.2e1", x); 240 | deserializes_to(".42e2", x); 241 | deserializes_to("0.42e2", x); 242 | 243 | deserializes_to("Infinity", f32::INFINITY); 244 | deserializes_to("-Infinity", f32::NEG_INFINITY); 245 | deserializes_to_nan_f32("NaN"); 246 | 247 | error_struct!(u64, visit_u64, deserialize_u64); 248 | deserializes_with_error::("\n 42", make_error("oops", 2, 2)); 249 | } 250 | 251 | #[test] 252 | fn deserializes_f32() { 253 | let x: f32 = 42.42; 254 | deserializes_to("42.42", x); 255 | deserializes_to("42.42e0", x); 256 | deserializes_to("4.242e1", x); 257 | deserializes_to(".4242e2", x); 258 | deserializes_to("0.4242e2", x); 259 | deserializes_to("-42.42", -x); 260 | deserializes_to("-42.42", -x); 261 | deserializes_to("-42.42", -x); 262 | deserializes_to("-42.42e0", -x); 263 | deserializes_to("-4.242e1", -x); 264 | deserializes_to("-.4242e2", -x); 265 | deserializes_to("-0.4242e2", -x); 266 | 267 | deserializes_to("Infinity", f32::INFINITY); 268 | deserializes_to("-Infinity", f32::NEG_INFINITY); 269 | deserializes_to_nan_f32("NaN"); 270 | 271 | error_struct!(f32, visit_f32, deserialize_f32); 272 | deserializes_with_error::("\n 42", make_error("oops", 2, 2)); 273 | } 274 | 275 | #[test] 276 | fn deserializes_f64() { 277 | let x: f64 = 42.42; 278 | deserializes_to("42.42", x); 279 | deserializes_to("42.42e0", x); 280 | deserializes_to("4.242e1", x); 281 | deserializes_to(".4242e2", x); 282 | deserializes_to("0.4242e2", x); 283 | deserializes_to("-42.42", -x); 284 | deserializes_to("-42.42", -x); 285 | deserializes_to("-42.42", -x); 286 | deserializes_to("-42.42e0", -x); 287 | deserializes_to("-4.242e1", -x); 288 | deserializes_to("-.4242e2", -x); 289 | deserializes_to("-0.4242e2", -x); 290 | 291 | deserializes_to("Infinity", f64::INFINITY); 292 | deserializes_to("-Infinity", f64::NEG_INFINITY); 293 | deserializes_to_nan_f64("NaN"); 294 | 295 | error_struct!(f64, visit_f64, deserialize_f64); 296 | deserializes_with_error::("\n 42", make_error("oops", 2, 2)); 297 | deserializes_with_error::( 298 | "\n 1e309", 299 | make_error("error parsing number: too large", 2, 2), 300 | ); 301 | } 302 | 303 | #[test] 304 | fn deserializes_char() { 305 | deserializes_to("'x'", 'x'); 306 | deserializes_to("\"자\"", '자'); 307 | deserializes_to(r#""\"""#, '"'); 308 | deserializes_to(r#""\r""#, '\r'); 309 | deserializes_to(r#""\n""#, '\n'); 310 | deserializes_to(r#""\t""#, '\t'); 311 | deserializes_to(r#""\\""#, '\\'); 312 | deserializes_to(r#""\/""#, '/'); 313 | deserializes_to(r#""\b""#, '\u{0008}'); 314 | deserializes_to(r#""\f""#, '\u{000c}'); 315 | 316 | // `deserialize_char` calls `visit_str` 317 | error_struct!(&str, visit_str, deserialize_char); 318 | deserializes_with_error::("\n 'x'", make_error("oops", 2, 2)); 319 | } 320 | 321 | #[test] 322 | #[ignore] // TODO currently unsupported 323 | fn deserializes_str() { 324 | deserializes_to("'Hello!'", "Hello!"); 325 | deserializes_to("\"안녕하세요\"", "안녕하세요"); 326 | } 327 | 328 | #[test] 329 | fn deserializes_string() { 330 | deserializes_to("'Hello!'", "Hello!".to_owned()); 331 | deserializes_to("\"안녕하세요\"", "안녕하세요".to_owned()); 332 | 333 | error_struct!(&str, visit_str, deserialize_string); 334 | deserializes_with_error::("\n 'Hello!'", make_error("oops", 2, 2)); 335 | } 336 | 337 | #[test] 338 | #[ignore] // TODO currently unsupported 339 | fn deserializes_bytes() {} 340 | 341 | #[test] 342 | #[ignore] // TODO currently unsupported 343 | fn deserializes_byte_buf() {} 344 | 345 | #[test] 346 | fn deserializes_option() { 347 | deserializes_to::>("null", None); 348 | deserializes_to("42", Some(42)); 349 | deserializes_to("42", Some(Some(42))); 350 | } 351 | 352 | #[test] 353 | fn deserializes_option_error() { 354 | #[derive(Debug, PartialEq)] 355 | struct A; 356 | impl<'de> de::Deserialize<'de> for A { 357 | fn deserialize(deserializer: D) -> Result 358 | where 359 | D: de::Deserializer<'de>, 360 | { 361 | struct Visitor; 362 | impl<'de> de::Visitor<'de> for Visitor { 363 | type Value = A; 364 | fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 365 | f.write_str("...") 366 | } 367 | fn visit_some(self, _deserializer: D) -> Result 368 | where 369 | D: de::Deserializer<'de>, 370 | { 371 | Err(de::Error::custom("oops")) 372 | } 373 | } 374 | deserializer.deserialize_option(Visitor) 375 | } 376 | } 377 | deserializes_with_error::("\n 42", make_error("oops", 2, 2)); 378 | } 379 | 380 | #[test] 381 | fn deserializes_unit() { 382 | deserializes_to("null", ()); 383 | 384 | #[derive(Deserialize, Debug, PartialEq)] 385 | struct A; 386 | deserializes_to("null", A); 387 | } 388 | 389 | #[test] 390 | fn deserializes_unit_error() { 391 | #[derive(Debug, PartialEq)] 392 | struct A; 393 | impl<'de> de::Deserialize<'de> for A { 394 | fn deserialize(deserializer: D) -> Result 395 | where 396 | D: de::Deserializer<'de>, 397 | { 398 | struct Visitor; 399 | impl de::Visitor<'_> for Visitor { 400 | type Value = A; 401 | fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 402 | f.write_str("...") 403 | } 404 | fn visit_unit(self) -> Result 405 | where 406 | E: de::Error, 407 | { 408 | Err(de::Error::custom("oops")) 409 | } 410 | } 411 | deserializer.deserialize_unit(Visitor) 412 | } 413 | } 414 | deserializes_with_error::("\n null", make_error("oops", 2, 2)); 415 | } 416 | 417 | #[test] 418 | fn deserializes_newtype_struct() { 419 | #[derive(Deserialize, PartialEq, Debug)] 420 | struct A(i32); 421 | 422 | #[derive(Deserialize, PartialEq, Debug)] 423 | struct B(f64); 424 | 425 | deserializes_to("42", A(42)); 426 | deserializes_to("42", B(42.)); 427 | } 428 | 429 | #[test] 430 | fn deserializes_newtype_struct_error() { 431 | #[derive(Debug, PartialEq)] 432 | struct A; 433 | impl<'de> de::Deserialize<'de> for A { 434 | fn deserialize(deserializer: D) -> Result 435 | where 436 | D: de::Deserializer<'de>, 437 | { 438 | struct Visitor; 439 | impl<'de> de::Visitor<'de> for Visitor { 440 | type Value = A; 441 | fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 442 | f.write_str("...") 443 | } 444 | fn visit_newtype_struct(self, _deserializer: D) -> Result 445 | where 446 | D: de::Deserializer<'de>, 447 | { 448 | Err(de::Error::custom("oops")) 449 | } 450 | } 451 | deserializer.deserialize_newtype_struct("A", Visitor) 452 | } 453 | } 454 | deserializes_with_error::("\n 42", make_error("oops", 2, 2)); 455 | } 456 | 457 | #[test] 458 | fn deserializes_seq() { 459 | #[derive(Deserialize, PartialEq, Debug)] 460 | #[serde(untagged)] 461 | enum Val { 462 | Number(f64), 463 | Bool(bool), 464 | String(String), 465 | } 466 | 467 | deserializes_to("[1, 2, 3]", vec![1, 2, 3]); 468 | deserializes_to( 469 | "[42, true, 'hello']", 470 | vec![ 471 | Val::Number(42.), 472 | Val::Bool(true), 473 | Val::String("hello".to_owned()), 474 | ], 475 | ); 476 | } 477 | 478 | #[test] 479 | fn deserializes_seq_size_hint() { 480 | #[derive(Debug, PartialEq)] 481 | struct Size(usize); 482 | impl<'de> de::Deserialize<'de> for Size { 483 | fn deserialize(deserializer: D) -> Result 484 | where 485 | D: de::Deserializer<'de>, 486 | { 487 | struct Visitor; 488 | impl<'de> de::Visitor<'de> for Visitor { 489 | type Value = Size; 490 | 491 | fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 492 | f.write_str("...") 493 | } 494 | 495 | fn visit_seq(self, seq: A) -> Result 496 | where 497 | A: de::SeqAccess<'de>, 498 | { 499 | Ok(Size(seq.size_hint().unwrap())) 500 | } 501 | } 502 | deserializer.deserialize_seq(Visitor) 503 | } 504 | } 505 | 506 | deserializes_to("[]", Size(0)); 507 | deserializes_to("[42, true, 'hello']", Size(3)); 508 | deserializes_to("[42, true, [1, 2]]", Size(3)); 509 | } 510 | 511 | #[test] 512 | fn deserializes_seq_error() { 513 | #[derive(Debug, PartialEq)] 514 | struct A; 515 | impl<'de> de::Deserialize<'de> for A { 516 | fn deserialize(deserializer: D) -> Result 517 | where 518 | D: de::Deserializer<'de>, 519 | { 520 | struct Visitor; 521 | impl<'de> de::Visitor<'de> for Visitor { 522 | type Value = A; 523 | fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 524 | f.write_str("...") 525 | } 526 | fn visit_seq(self, _a: A) -> Result 527 | where 528 | A: de::SeqAccess<'de>, 529 | { 530 | Err(de::Error::custom("oops")) 531 | } 532 | } 533 | deserializer.deserialize_seq(Visitor) 534 | } 535 | } 536 | deserializes_with_error::("\n [ true ]", make_error("oops", 2, 2)); 537 | } 538 | 539 | #[test] 540 | fn deserializes_tuple() { 541 | deserializes_to("[1, 2, 3]", (1, 2, 3)); 542 | } 543 | 544 | #[test] 545 | fn deserializes_tuple_struct() { 546 | #[derive(Deserialize, PartialEq, Debug)] 547 | struct A(i32, f64); 548 | 549 | #[derive(Deserialize, PartialEq, Debug)] 550 | struct B(f64, i32); 551 | 552 | deserializes_to("[1, 2]", A(1, 2.)); 553 | deserializes_to("[1, 2]", B(1., 2)); 554 | } 555 | 556 | #[test] 557 | fn deserializes_tuple_error() { 558 | #[derive(Debug, PartialEq)] 559 | struct A; 560 | impl<'de> de::Deserialize<'de> for A { 561 | fn deserialize(deserializer: D) -> Result 562 | where 563 | D: de::Deserializer<'de>, 564 | { 565 | struct Visitor; 566 | impl<'de> de::Visitor<'de> for Visitor { 567 | type Value = A; 568 | fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 569 | f.write_str("...") 570 | } 571 | fn visit_seq(self, _a: A) -> Result 572 | where 573 | A: de::SeqAccess<'de>, 574 | { 575 | Err(de::Error::custom("oops")) 576 | } 577 | } 578 | deserializer.deserialize_tuple(2, Visitor) 579 | } 580 | } 581 | deserializes_with_error::("\n [1, 2]", make_error("oops", 2, 2)); 582 | 583 | #[derive(Deserialize, Debug, PartialEq)] 584 | struct B(i32, f64); 585 | deserializes_with_error::( 586 | "\n [1]", 587 | make_error( 588 | "invalid length 1, expected tuple struct B with 2 elements", 589 | 2, 590 | 2, 591 | ), 592 | ); 593 | } 594 | 595 | #[test] 596 | fn deserializes_map() { 597 | let mut m = HashMap::new(); 598 | m.insert("a".to_owned(), 1); 599 | m.insert("b".to_owned(), 2); 600 | m.insert("c".to_owned(), 3); 601 | 602 | deserializes_to("{ a: 1, 'b': 2, \"c\": 3 }", m); 603 | } 604 | 605 | #[test] 606 | fn deserializes_map_size_hint() { 607 | #[derive(Debug, PartialEq)] 608 | struct Size(usize); 609 | 610 | impl<'de> de::Deserialize<'de> for Size { 611 | fn deserialize(deserializer: D) -> Result 612 | where 613 | D: de::Deserializer<'de>, 614 | { 615 | struct Visitor; 616 | impl<'de> de::Visitor<'de> for Visitor { 617 | type Value = Size; 618 | 619 | fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 620 | f.write_str("...") 621 | } 622 | 623 | fn visit_map(self, map: A) -> Result 624 | where 625 | A: de::MapAccess<'de>, 626 | { 627 | Ok(Size(map.size_hint().unwrap())) 628 | } 629 | } 630 | deserializer.deserialize_map(Visitor) 631 | } 632 | } 633 | 634 | deserializes_to("{}", Size(0)); 635 | deserializes_to("{ a: 1, 'b': 2, \"c\": 3 }", Size(3)); 636 | deserializes_to("{ a: 1, 'b': 2, \"c\": [1, 2] }", Size(3)); 637 | } 638 | 639 | #[test] 640 | fn deserializes_map_error() { 641 | #[derive(Debug, PartialEq)] 642 | struct A {} 643 | impl<'de> de::Deserialize<'de> for A { 644 | fn deserialize(deserializer: D) -> Result 645 | where 646 | D: de::Deserializer<'de>, 647 | { 648 | struct Visitor; 649 | impl<'de> de::Visitor<'de> for Visitor { 650 | type Value = A; 651 | fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 652 | f.write_str("...") 653 | } 654 | fn visit_map(self, _a: A) -> Result 655 | where 656 | A: de::MapAccess<'de>, 657 | { 658 | Err(de::Error::custom("oops")) 659 | } 660 | } 661 | deserializer.deserialize_map(Visitor) 662 | } 663 | } 664 | 665 | deserializes_with_error::("\n { 'a': true }", make_error("oops", 2, 2)); 666 | } 667 | 668 | #[test] 669 | fn deserialize_any_ieee_754_f64() { 670 | #[derive(Deserialize)] 671 | struct Data { 672 | pos_inf: f64, 673 | neg_inf: f64, 674 | pos_nan: f64, 675 | neg_nan: f64, 676 | } 677 | 678 | #[derive(Deserialize)] 679 | #[serde(tag = "type", content = "data")] 680 | enum Val { 681 | #[serde(rename = "one")] 682 | One { 683 | other: Box, 684 | #[serde(flatten)] 685 | data: Data, 686 | }, 687 | #[serde(rename = "two")] 688 | Two, 689 | } 690 | 691 | let raw = r#" 692 | { 693 | "type": "one", 694 | "data": { 695 | "other": { 696 | "type": "two" 697 | }, 698 | "pos_inf": Infinity, 699 | "neg_inf": -Infinity, 700 | "pos_nan": NaN, 701 | "neg_nan": -NaN, 702 | } 703 | } 704 | "#; 705 | 706 | let val: Val = serde_json5::from_str(raw).unwrap(); 707 | match val { 708 | Val::One { other, data } => { 709 | assert!(matches!(other.as_ref(), Val::Two)); 710 | assert_eq!(data.pos_inf, f64::INFINITY); 711 | assert_eq!(data.neg_inf, f64::NEG_INFINITY); 712 | assert!(data.pos_nan.is_nan()); 713 | assert!(data.neg_nan.is_nan()); 714 | } 715 | _ => unreachable!(), 716 | } 717 | } 718 | 719 | #[test] 720 | fn deserialize_any_ieee_754_f32() { 721 | #[derive(Deserialize)] 722 | struct Data { 723 | pos_inf: f32, 724 | neg_inf: f32, 725 | pos_nan: f32, 726 | neg_nan: f32, 727 | } 728 | 729 | #[derive(Deserialize)] 730 | #[serde(tag = "type", content = "data")] 731 | enum Val { 732 | #[serde(rename = "one")] 733 | One { 734 | other: Box, 735 | #[serde(flatten)] 736 | data: Data, 737 | }, 738 | #[serde(rename = "two")] 739 | Two, 740 | } 741 | 742 | let raw = r#" 743 | { 744 | "type": "one", 745 | "data": { 746 | "other": { 747 | "type": "two" 748 | }, 749 | "pos_inf": Infinity, 750 | "neg_inf": -Infinity, 751 | "pos_nan": NaN, 752 | "neg_nan": -NaN, 753 | } 754 | } 755 | "#; 756 | 757 | let val: Val = serde_json5::from_str(raw).unwrap(); 758 | match val { 759 | Val::One { other, data } => { 760 | assert!(matches!(other.as_ref(), Val::Two)); 761 | assert_eq!(data.pos_inf, f32::INFINITY); 762 | assert_eq!(data.neg_inf, f32::NEG_INFINITY); 763 | assert!(data.pos_nan.is_nan()); 764 | assert!(data.neg_nan.is_nan()); 765 | } 766 | _ => unreachable!(), 767 | } 768 | } 769 | 770 | #[test] 771 | fn deserializes_struct() { 772 | #[derive(Deserialize, PartialEq, Debug)] 773 | struct S { 774 | a: i32, 775 | b: i32, 776 | c: i32, 777 | } 778 | 779 | deserializes_to("{ a: 1, 'b': 2, \"c\": 3 }", S { a: 1, b: 2, c: 3 }); 780 | } 781 | 782 | #[test] 783 | fn deserializes_struct_error() { 784 | #[derive(Deserialize, PartialEq, Debug)] 785 | struct S { 786 | a: i32, 787 | b: i32, 788 | c: i32, 789 | } 790 | deserializes_with_error::("\n { a: 1, 'b': 2 }", make_error("missing field `c`", 2, 2)); 791 | } 792 | 793 | #[test] 794 | fn deserializes_enum() { 795 | #[derive(Deserialize, PartialEq, Debug)] 796 | #[allow(clippy::enum_variant_names)] 797 | enum E { 798 | A, 799 | B(i32), 800 | C(i32, i32), 801 | D { a: i32, b: i32 }, 802 | E {}, 803 | F(), 804 | } 805 | 806 | deserializes_to("'A'", E::A); 807 | deserializes_to("{ A: null }", E::A); 808 | deserializes_to("{ B: 2 }", E::B(2)); 809 | deserializes_to("{ C: [3, 5] }", E::C(3, 5)); 810 | deserializes_to("{ D: { a: 7, b: 11 } }", E::D { a: 7, b: 11 }); 811 | deserializes_to("{ E: {} }", E::E {}); 812 | deserializes_to("{ F: [] }", E::F()); 813 | } 814 | 815 | #[test] 816 | fn deserializes_enum_error() { 817 | #[derive(Deserialize, PartialEq, Debug)] 818 | enum E { 819 | A {}, 820 | B(), 821 | C, 822 | } 823 | 824 | #[derive(Deserialize, PartialEq, Debug)] 825 | struct S { 826 | e: E, 827 | } 828 | 829 | deserializes_with_error::("{ e: 'A' }", make_error("expected an object", 1, 6)); 830 | deserializes_with_error::("{ e: 'B' }", make_error("expected an array", 1, 6)); 831 | deserializes_with_error::("{ e: { 'A': 5 } }", make_error("expected an object", 1, 6)); 832 | deserializes_with_error::("{ e: { 'B': 5 } }", make_error("expected an array", 1, 6)); 833 | deserializes_with_error::( 834 | "{ e: { 'C': 5 } }", 835 | make_error("invalid type: integer `5`, expected unit", 1, 13), 836 | ); 837 | deserializes_with_error::( 838 | "{ e: { 'C': {} } }", 839 | make_error("invalid type: map, expected unit", 1, 13), 840 | ); 841 | deserializes_with_error::( 842 | "\n 'D'", 843 | make_error("unknown variant `D`, expected one of `A`, `B`, `C`", 2, 2), 844 | ); 845 | } 846 | 847 | #[test] 848 | fn deserializes_ignored() { 849 | #[derive(Deserialize, PartialEq, Debug)] 850 | struct S { 851 | a: i32, 852 | b: i32, 853 | } 854 | 855 | deserializes_to("{ a: 1, ignored: 42, b: 2 }", S { a: 1, b: 2 }); 856 | } 857 | 858 | #[test] 859 | fn deserializes_json_values() { 860 | // As int if json uses int type. 861 | deserializes_to("0x2a", serde_json::json!(42)); 862 | deserializes_to("0x2A", serde_json::json!(42)); 863 | deserializes_to("0X2A", serde_json::json!(42)); 864 | deserializes_to("42", serde_json::json!(42)); 865 | 866 | // As float if json calls for explicit float type. 867 | deserializes_to("42.", serde_json::json!(42.)); 868 | deserializes_to("42e0", serde_json::json!(42.)); 869 | deserializes_to("4e2", serde_json::json!(400.)); 870 | deserializes_to("4e2", serde_json::json!(4e2)); 871 | } 872 | 873 | #[test] 874 | fn deserializes_parse_error() { 875 | let parse_err_str = r#" --> 1:2 876 | | 877 | 1 | { 878 | | ^--- 879 | | 880 | = expected identifier or string"#; 881 | #[derive(Deserialize, PartialEq, Debug)] 882 | struct A; 883 | deserializes_with_error::("{", make_error(parse_err_str, 1, 2)); 884 | 885 | deserializes_with_error::( 886 | "\n 42", 887 | make_error("invalid type: integer `42`, expected a boolean", 2, 2), 888 | ); 889 | } 890 | 891 | #[test] 892 | fn test_from_str() { 893 | #[derive(Deserialize, PartialEq, Debug)] 894 | struct S { 895 | a: i32, 896 | b: i32, 897 | c: i32, 898 | } 899 | 900 | let data = "{ a: 5, b: 4, c: 3}"; 901 | assert_eq!(serde_json5::from_str(data), Ok(S { a: 5, b: 4, c: 3 })) 902 | } 903 | 904 | #[test] 905 | fn test_from_slice() { 906 | #[derive(Deserialize, PartialEq, Debug)] 907 | struct S { 908 | a: i32, 909 | b: i32, 910 | c: i32, 911 | } 912 | 913 | let data = "{ a: 5, b: 4, c: 3}".as_bytes(); 914 | assert_eq!(serde_json5::from_slice(data), Ok(S { a: 5, b: 4, c: 3 })) 915 | } 916 | 917 | #[test] 918 | fn test_from_reader() { 919 | #[derive(Deserialize, PartialEq, Debug)] 920 | struct S { 921 | a: i32, 922 | b: i32, 923 | c: i32, 924 | } 925 | 926 | let data = "{ a: 5, b: 4, c: 3}"; 927 | let mut reader = std::io::Cursor::new(data); 928 | assert_eq!( 929 | serde_json5::from_reader(&mut reader), 930 | Ok(S { a: 5, b: 4, c: 3 }) 931 | ) 932 | } 933 | -------------------------------------------------------------------------------- /tests/json5_dot_org_example.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | 3 | use serde_derive::Deserialize; 4 | use std::fs::File; 5 | use std::io::prelude::*; 6 | 7 | use common::deserializes_to; 8 | 9 | #[derive(Deserialize, PartialEq, Debug)] 10 | #[serde(rename_all = "camelCase")] 11 | struct Example { 12 | unquoted: String, 13 | single_quotes: String, 14 | line_breaks: String, 15 | hexadecimal: u32, 16 | leading_decimal_point: f64, 17 | and_trailing: f64, 18 | positive_sign: i32, 19 | trailing_comma: String, 20 | and_in: Vec, 21 | backwards_compatible: String, 22 | } 23 | 24 | #[test] 25 | fn serializes_example_from_json5_dot_org() { 26 | let mut contents = String::new(); 27 | File::open("tests/assets/json5_dot_org_example.json5") 28 | .unwrap() 29 | .read_to_string(&mut contents) 30 | .unwrap(); 31 | 32 | let expected = Example { 33 | unquoted: "and you can quote me on that".to_owned(), 34 | single_quotes: "I can use \"double quotes\" here".to_owned(), 35 | line_breaks: "Look, Mom! No \\n's!".to_owned(), 36 | hexadecimal: 0xdecaf, 37 | leading_decimal_point: 0.8675309, 38 | and_trailing: 8675309.0, 39 | positive_sign: 1, 40 | trailing_comma: "in objects".to_owned(), 41 | and_in: vec!["arrays".to_owned()], 42 | backwards_compatible: "with JSON".to_owned(), 43 | }; 44 | 45 | deserializes_to(&contents, expected) 46 | } 47 | -------------------------------------------------------------------------------- /tests/ser.rs: -------------------------------------------------------------------------------- 1 | use serde_derive::Serialize; 2 | 3 | use std::collections::HashMap; 4 | 5 | macro_rules! serializes_to { 6 | ($value:expr, $actual:expr) => {{ 7 | let actual = serde_json5::to_string(&$value).unwrap(); 8 | assert_eq!(actual, $actual); 9 | }}; 10 | ($value:expr, $actual:expr,) => { 11 | serializes_to!($value, $actual) 12 | }; 13 | } 14 | 15 | #[test] 16 | fn serializes_bool() { 17 | serializes_to!(true, "true"); 18 | serializes_to!(false, "false"); 19 | } 20 | 21 | #[test] 22 | fn serializes_i8() { 23 | let x: i8 = 42; 24 | serializes_to!(x, "42"); 25 | } 26 | 27 | #[test] 28 | fn serializes_u8() { 29 | let x: u8 = 42; 30 | serializes_to!(x, "42"); 31 | } 32 | 33 | #[test] 34 | fn serializes_i16() { 35 | let x: i16 = 42; 36 | serializes_to!(x, "42"); 37 | } 38 | 39 | #[test] 40 | fn serializes_u16() { 41 | let x: u16 = 42; 42 | serializes_to!(x, "42"); 43 | } 44 | 45 | #[test] 46 | fn serializes_i32() { 47 | let x: i32 = 42; 48 | serializes_to!(x, "42"); 49 | } 50 | 51 | #[test] 52 | fn serializes_u32() { 53 | let x: u32 = 42; 54 | serializes_to!(x, "42"); 55 | } 56 | 57 | #[test] 58 | fn serializes_i64() { 59 | let x: i64 = 42; 60 | serializes_to!(x, "42"); 61 | } 62 | 63 | #[test] 64 | fn serializes_u64() { 65 | let x: u64 = 42; 66 | serializes_to!(x, "42"); 67 | } 68 | 69 | #[test] 70 | fn serializes_f32() { 71 | let x: f32 = 42.42; 72 | serializes_to!(x, "42.42"); 73 | 74 | serializes_to!(f32::INFINITY, "Infinity"); 75 | serializes_to!(f32::NEG_INFINITY, "-Infinity"); 76 | serializes_to!(f32::NAN, "NaN"); 77 | } 78 | 79 | #[test] 80 | fn serializes_f64() { 81 | let x: f64 = 42.42; 82 | serializes_to!(x, "42.42"); 83 | 84 | serializes_to!(f64::INFINITY, "Infinity"); 85 | serializes_to!(f64::NEG_INFINITY, "-Infinity"); 86 | serializes_to!(f64::NAN, "NaN"); 87 | } 88 | 89 | #[test] 90 | fn serializes_char() { 91 | serializes_to!('x', "\"x\""); 92 | serializes_to!('자', "\"자\""); 93 | serializes_to!('"', r#""\"""#); 94 | serializes_to!('\r', r#""\r""#); 95 | serializes_to!('\n', r#""\n""#); 96 | serializes_to!('\t', r#""\t""#); 97 | serializes_to!('\\', r#""\\""#); 98 | serializes_to!('/', r#""/""#); 99 | serializes_to!('\u{0008}', r#""\b""#); 100 | serializes_to!('\u{000c}', r#""\f""#); 101 | } 102 | 103 | #[test] 104 | fn serializes_str() { 105 | serializes_to!("Hello!", "\"Hello!\""); 106 | serializes_to!("안녕하세요", "\"안녕하세요\""); 107 | serializes_to!("\"quotes!\"", "\"\\\"quotes!\\\"\""); 108 | serializes_to!("new\nlines", "\"new\\nlines\""); 109 | serializes_to!("\\", "\"\\\\\""); 110 | } 111 | 112 | #[test] 113 | fn serializes_string() { 114 | serializes_to!("Hello!".to_owned(), "\"Hello!\""); 115 | serializes_to!("안녕하세요".to_owned(), "\"안녕하세요\""); 116 | serializes_to!("\"quotes!\"".to_owned(), "\"\\\"quotes!\\\"\""); 117 | serializes_to!("new\nlines".to_owned(), "\"new\\nlines\""); 118 | serializes_to!("\\".to_owned(), "\"\\\\\""); 119 | } 120 | 121 | #[test] 122 | #[ignore] // TODO currently unsupported 123 | fn serializes_bytes() {} 124 | 125 | #[test] 126 | #[ignore] // TODO currently unsupported 127 | fn serializes_byte_buf() {} 128 | 129 | #[test] 130 | fn serializes_option() { 131 | serializes_to!(None::, "null"); 132 | serializes_to!(Some(42), "42"); 133 | serializes_to!(Some(Some(42)), "42"); 134 | } 135 | 136 | #[test] 137 | fn serializes_unit() { 138 | serializes_to!((), "null"); 139 | } 140 | 141 | #[test] 142 | fn serializes_unit_struct() { 143 | #[derive(Serialize, PartialEq, Debug)] 144 | struct A; 145 | serializes_to!(A, "null"); 146 | } 147 | 148 | #[test] 149 | fn serializes_newtype_struct() { 150 | #[derive(Serialize, PartialEq, Debug)] 151 | struct A(i32); 152 | 153 | #[derive(Serialize, PartialEq, Debug)] 154 | struct B(f64); 155 | 156 | serializes_to!(A(42), "42"); 157 | serializes_to!(B(42.), "42"); 158 | } 159 | 160 | #[test] 161 | fn serializes_seq() { 162 | #[derive(Serialize, PartialEq, Debug)] 163 | #[serde(untagged)] 164 | enum Val { 165 | Number(f64), 166 | Bool(bool), 167 | String(String), 168 | } 169 | 170 | serializes_to!(vec![1, 2, 3], "[1,2,3]"); 171 | serializes_to!( 172 | vec![ 173 | Val::Number(42.), 174 | Val::Bool(true), 175 | Val::String("hello".to_owned()) 176 | ], 177 | "[42,true,\"hello\"]", 178 | ) 179 | } 180 | 181 | #[test] 182 | fn serializes_tuple() { 183 | serializes_to!((1, 2, 3), "[1,2,3]"); 184 | } 185 | 186 | #[test] 187 | fn serializes_tuple_struct() { 188 | #[derive(Serialize, PartialEq, Debug)] 189 | struct A(i32, f64); 190 | 191 | #[derive(Serialize, PartialEq, Debug)] 192 | struct B(f64, i32); 193 | 194 | serializes_to!(A(1, 2.), "[1,2]"); 195 | serializes_to!(B(1., 2), "[1,2]"); 196 | } 197 | 198 | #[test] 199 | fn serializes_map() { 200 | let mut inner = HashMap::new(); 201 | inner.insert("b".to_owned(), true); 202 | 203 | let mut outer = HashMap::new(); 204 | outer.insert("a".to_owned(), inner); 205 | 206 | serializes_to!(outer, "{\"a\":{\"b\":true}}"); 207 | } 208 | 209 | #[test] 210 | fn serializes_struct() { 211 | #[derive(Serialize, PartialEq, Debug)] 212 | struct S { 213 | a: i32, 214 | b: i32, 215 | c: i32, 216 | } 217 | 218 | serializes_to!(S { a: 1, b: 2, c: 3 }, "{\"a\":1,\"b\":2,\"c\":3}"); 219 | } 220 | 221 | #[test] 222 | fn serializes_enum() { 223 | #[derive(Serialize, PartialEq, Debug)] 224 | enum E { 225 | A, 226 | B(i32), 227 | C(i32, i32), 228 | D { a: i32, b: i32 }, 229 | } 230 | 231 | serializes_to!(E::A, "\"A\""); 232 | serializes_to!(E::B(2), "{\"B\":2}"); 233 | serializes_to!(E::C(3, 5), "{\"C\":[3,5]}"); 234 | serializes_to!(E::D { a: 7, b: 11 }, "{\"D\":{\"a\":7,\"b\":11}}"); 235 | } 236 | 237 | #[test] 238 | fn test_to_writer() { 239 | #[derive(Serialize)] 240 | struct S { 241 | a: i32, 242 | b: i32, 243 | c: i32, 244 | } 245 | 246 | let value = S { a: 5, b: 4, c: 3 }; 247 | let mut writer = Vec::::new(); 248 | serde_json5::to_writer(&mut writer, &value).expect("to_writer succeeds"); 249 | let data = std::str::from_utf8(&writer).expect("valid utf8"); 250 | assert_eq!(data, r#"{"a":5,"b":4,"c":3}"#); 251 | } 252 | --------------------------------------------------------------------------------