mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 04:33:37 +00:00
upgrade ron and use decimal fork
This commit is contained in:
parent
f1d58609d5
commit
6e31b90ec3
50 changed files with 6946 additions and 31 deletions
|
@ -58,7 +58,7 @@ log = { version = "0.4", features = ["release_max_level_info"] }
|
|||
[dev-dependencies]
|
||||
rand = "0.7.2"
|
||||
serde = { version = "1", features = ["derive"]}
|
||||
ron = "0.5.1"
|
||||
ron = { path = "crates/ron" }
|
||||
serde_json = "1.0"
|
||||
env_logger = "0.7"
|
||||
|
||||
|
|
|
@ -247,39 +247,39 @@ fn delete_first() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn merge() {
|
||||
let _ = tracing_subscriber::fmt::try_init();
|
||||
// fn merge() {
|
||||
// let _ = tracing_subscriber::fmt::try_init();
|
||||
|
||||
let universe = Universe::new();
|
||||
let mut world_1 = universe.create_world();
|
||||
let mut world_2 = universe.create_world();
|
||||
// let universe = Universe::new();
|
||||
// let mut world_1 = universe.create_world();
|
||||
// let mut world_2 = universe.create_world();
|
||||
|
||||
let shared = (Static, Model(5));
|
||||
let components = vec![
|
||||
(Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)),
|
||||
(Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)),
|
||||
];
|
||||
// let shared = (Static, Model(5));
|
||||
// let components = vec![
|
||||
// (Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)),
|
||||
// (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)),
|
||||
// ];
|
||||
|
||||
let mut world_1_entities: Vec<Entity> = Vec::new();
|
||||
for e in world_1.insert(shared, components.clone()) {
|
||||
world_1_entities.push(*e);
|
||||
}
|
||||
// let mut world_1_entities: Vec<Entity> = Vec::new();
|
||||
// for e in world_1.insert(shared, components.clone()) {
|
||||
// world_1_entities.push(*e);
|
||||
// }
|
||||
|
||||
let mut world_2_entities: Vec<Entity> = Vec::new();
|
||||
for e in world_2.insert(shared, components.clone()) {
|
||||
world_2_entities.push(*e);
|
||||
}
|
||||
// let mut world_2_entities: Vec<Entity> = Vec::new();
|
||||
// for e in world_2.insert(shared, components.clone()) {
|
||||
// world_2_entities.push(*e);
|
||||
// }
|
||||
|
||||
world_1.merge(world_2);
|
||||
// world_1.merge(world_2);
|
||||
|
||||
for (i, e) in world_2_entities.iter().enumerate() {
|
||||
assert!(world_1.is_alive(*e));
|
||||
// for (i, e) in world_2_entities.iter().enumerate() {
|
||||
// assert!(world_1.is_alive(*e));
|
||||
|
||||
let (pos, rot) = components.get(i).unwrap();
|
||||
assert_eq!(pos, &world_1.get_component(*e).unwrap() as &Pos);
|
||||
assert_eq!(rot, &world_1.get_component(*e).unwrap() as &Rot);
|
||||
}
|
||||
}
|
||||
// let (pos, rot) = components.get(i).unwrap();
|
||||
// assert_eq!(pos, &world_1.get_component(*e).unwrap() as &Pos);
|
||||
// assert_eq!(rot, &world_1.get_component(*e).unwrap() as &Rot);
|
||||
// }
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn mutate_add_component() {
|
||||
|
|
|
@ -11,6 +11,6 @@ bevy_property = { path = "../bevy_property" }
|
|||
legion = { path = "../bevy_legion", features = ["serialize"] }
|
||||
serde = { version = "1.0", features = ["derive"]}
|
||||
erased-serde = "0.3"
|
||||
ron = "0.5.1"
|
||||
ron = { path = "../ron" }
|
||||
uuid = { version = "0.8", features = ["v4", "serde"] }
|
||||
anyhow = "1.0"
|
154
crates/ron/CHANGELOG.md
Normal file
154
crates/ron/CHANGELOG.md
Normal file
|
@ -0,0 +1,154 @@
|
|||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Next
|
||||
|
||||
## [0.6.0] - 2020-05-21
|
||||
### Additions
|
||||
- Implement integer support in Numbers ([#210](https://github.com/ron-rs/ron/pull/210))
|
||||
- Port `ser::Serializer` to `io::Write` ([#206](https://github.com/ron-rs/ron/pull/206))
|
||||
- Support i128 and u128 ([#219](https://github.com/ron-rs/ron/pull/219))
|
||||
- Allow pretty ser to work with implicit-some extension ([#182](https://github.com/ron-rs/ron/pull/182))
|
||||
- Make PrettyConfig future-proof ([#173](https://github.com/ron-rs/ron/pull/173))
|
||||
- Use indexmap to preserve order (optional) ([#172](https://github.com/ron-rs/ron/pull/172))
|
||||
- Add tests for different enum representations ([#166](https://github.com/ron-rs/ron/pull/166))
|
||||
- Implement inf, -inf and NaN handling ([#163](https://github.com/ron-rs/ron/pull/163))
|
||||
- Add VS code language tooling ([#160](https://github.com/ron-rs/ron/pull/160))
|
||||
- Be smarter about integer deserialization ([#157](https://github.com/ron-rs/ron/pull/157))
|
||||
|
||||
### Fixes
|
||||
- Fix parsing of borrowed strings ([#228](https://github.com/ron-rs/ron/pull/228))
|
||||
- Fix depth limit test for off-by-one fix ([#225](https://github.com/ron-rs/ron/pull/225))
|
||||
- Remove deprecated uses of `Error::description` ([#208](https://github.com/ron-rs/ron/pull/208))
|
||||
- Preserve ordering of map sequences ([#197](https://github.com/ron-rs/ron/pull/197))
|
||||
- Remove unneeded Neg requirement for signed_integer ([#193](https://github.com/ron-rs/ron/pull/193))
|
||||
- Ensure "Untagged tuple-like enum variants not deserializing correctly……" is fixed ([#170](https://github.com/ron-rs/ron/pull/170))
|
||||
|
||||
### Changes
|
||||
- Update `serde` requirement to 1.0.60 ([#226](https://github.com/ron-rs/ron/pull/226))
|
||||
- Replace Travis with GitHub actions ([#223](https://github.com/ron-rs/ron/pull/223))
|
||||
- Rename `format_doc_comments` to `format_code_in_doc_comment`
|
||||
- Update base64 requirement from 0.11 to 0.12 ([#204](https://github.com/ron-rs/ron/pull/204))
|
||||
- Update base64 requirement from 0.10 to 0.11 ([#195](https://github.com/ron-rs/ron/pull/195))
|
||||
- Update `serde_bytes` to 0.11 ([#164](https://github.com/ron-rs/ron/pull/164))
|
||||
|
||||
## [0.5.1] - 2019-04-05
|
||||
### Fixes
|
||||
- Increase source compability from Rust `1.34.0` to `1.31.0` by not relying on `as _` imports ([#156](https://github.com/ron-rs/ron/pull/156))
|
||||
|
||||
## [0.5.0] - 2019-03-31
|
||||
### Additions
|
||||
- Don't insert new lines in empty arrays or maps ([#150](https://github.com/ron-rs/ron/pull/150))
|
||||
### Changes
|
||||
- Transition to Rust 2018 ([#149](https://github.com/ron-rs/ron/pull/149))
|
||||
|
||||
## [0.4.2] - 2019-03-01
|
||||
### Additions
|
||||
- Add integer check for deserializer ([#148](https://github.com/ron-rs/ron/pull/148))
|
||||
- Implement `Value::into_rust` ([#146](https://github.com/ron-rs/ron/pull/146))
|
||||
|
||||
## [0.4.1] - 2019-01-09
|
||||
### Additions
|
||||
- Allow underscores in integers ([#135](https://github.com/ron-rs/ron/pull/135))
|
||||
- Added extension documentation ([#130](https://github.com/ron-rs/ron/pull/130))
|
||||
### Changes
|
||||
- Move sublime text syntax to separate repo ([#138](https://github.com/ron-rs/ron/pull/138))
|
||||
- Update `base64` crate dependency to 0.10 ([#137](https://github.com/ron-rs/ron/pull/137))
|
||||
|
||||
## [0.4.0] - 2018-08-11
|
||||
### Fixes
|
||||
- Handle tuple deserialization in deserialize_any properly ([#124](https://github.com/ron-rs/ron/pull/124))
|
||||
### Changes
|
||||
- Add raw string syntax to grammar ([#125](https://github.com/ron-rs/ron/pull/125))
|
||||
- Reexport `Value` at root ([#120](https://github.com/ron-rs/ron/pull/120))
|
||||
|
||||
## [0.3.0] - 2018-06-15
|
||||
### Additions
|
||||
- `serde_bytes` fields to be encoded using base64. ([#109](https://github.com/ron-rs/ron/pull/109))
|
||||
### Fixes
|
||||
- Allow raw string literals ([#114](https://github.com/ron-rs/ron/pull/114))
|
||||
### Changes
|
||||
- Now depends on `base64` 0.9.2.
|
||||
|
||||
## [0.2.2] - 2018-05-19
|
||||
### Fixes
|
||||
- Allow whitespace in newtype variants ([#104](https://github.com/ron-rs/ron/pull/104))
|
||||
|
||||
## [0.2.1] - 2018-05-04
|
||||
### Additions
|
||||
- Add multi-line comments ([#98](https://github.com/ron-rs/ron/pull/98))
|
||||
### Fixes
|
||||
- Allow more whitespace inside newtypes ([#103](https://github.com/ron-rs/ron/pull/103))
|
||||
|
||||
## [0.2.0] - 2018-02-14
|
||||
### Additions
|
||||
- Limit the pretty depth ([#93](https://github.com/ron-rs/ron/pull/93))
|
||||
- Add support for `\x??` and improve unicode escapes ([#84](https://github.com/ron-rs/ron/pull/84))
|
||||
|
||||
## [0.1.7] - 2018-01-24
|
||||
### Additions
|
||||
- Deep array indexing ([#88](https://github.com/ron-rs/ron/pull/88))
|
||||
- Pretty sequence indexing ([#86](https://github.com/ron-rs/ron/pull/86))
|
||||
- Add unicode support for chars ([#80](https://github.com/ron-rs/ron/pull/80))
|
||||
- Add support for hex, oct and bin numbers ([#78](https://github.com/ron-rs/ron/pull/78))
|
||||
- Allow implicit Some ([#75](https://github.com/ron-rs/ron/pull/75))
|
||||
- Add grammar specification ([#73](https://github.com/ron-rs/ron/pull/73))
|
||||
- Add extension support and first extension, unwrap_newtypes ([#72](https://github.com/ron-rs/ron/pull/72))
|
||||
### Fixes
|
||||
- Directly serialize `f32` ([#81](https://github.com/ron-rs/ron/pull/81))
|
||||
|
||||
## [0.1.6] - 2018-01-24
|
||||
### Additions
|
||||
- Implement sequence indexing ([#87](https://github.com/ron-rs/ron/pull/87))
|
||||
### Fixes
|
||||
- Remove ident variable from Sublime syntax ([#71](https://github.com/ron-rs/ron/pull/71))
|
||||
|
||||
## [0.1.5] - 2017-12-27
|
||||
### Additions
|
||||
- Allow creating a new serializer ([#70](https://github.com/ron-rs/ron/pull/70))
|
||||
- Sublime syntax highlighter ([#67](https://github.com/ron-rs/ron/pull/67))
|
||||
- Add support for integers ([#65](https://github.com/ron-rs/ron/pull/65))
|
||||
- Implement `Deserializer` for `Value` ([#64](https://github.com/ron-rs/ron/pull/64))
|
||||
|
||||
## [0.1.4] - 2017-10-12
|
||||
### Additions
|
||||
- Add `PrettyConfig` ([#61](https://github.com/ron-rs/ron/pull/61))
|
||||
- impl `deserialize_ignored_any` for `id` ([#60](https://github.com/ron-rs/ron/pull/60))
|
||||
### Fixes
|
||||
- Fix deserializing of ignored fields ([#62](https://github.com/ron-rs/ron/pull/62))
|
||||
|
||||
## [0.1.3] - 2017-10-06
|
||||
### Fixes
|
||||
- Removed indentation from tuple variant pretty encoder ([#57](https://github.com/ron-rs/ron/pull/57))
|
||||
|
||||
## [0.1.2] - 2017-10-06
|
||||
### Fixes
|
||||
- Fix decoding of string literals ([#56](https://github.com/ron-rs/ron/pull/56))
|
||||
- Add `Value` and implement `deserialize_any` ([#53](https://github.com/ron-rs/ron/pull/53))
|
||||
|
||||
## [0.1.1] - 2017-08-07
|
||||
### Fixes
|
||||
- Be more permissive wrt whitespace decoding ([#41](https://github.com/ron-rs/ron/pull/41))
|
||||
### Additions
|
||||
- Add utility function to deserialize from `std::io::Read` ([#42](https://github.com/ron-rs/ron/pull/42))
|
||||
|
||||
## [0.1.0] - 2015-08-04
|
||||
### Changes
|
||||
- Reorganize deserialization modules ([#30](https://github.com/ron-rs/ron/pull/30))
|
||||
- Rework deserializer not to require `pom` crate [#27](https://github.com/ron-rs/ron/pull/27), ([#38](https://github.com/ron-rs/ron/pull/38))
|
||||
- Dual license under Apache 2.0 and MIT ([#26](https://github.com/ron-rs/ron/pull/26))
|
||||
### Fixes
|
||||
- Use CRLF for serializatio on Windows ([#32](https://github.com/ron-rs/ron/pull/32))
|
||||
- Fix bors-ng to work with travis ([#31](https://github.com/ron-rs/ron/pull/31))
|
||||
- Handle escapes ([#23](https://github.com/ron-rs/ron/pull/23))
|
||||
### Additions
|
||||
- Improve error reporting ([#29](https://github.com/ron-rs/ron/pull/29))
|
||||
- Allow decoding of comments ([#28](https://github.com/ron-rs/ron/pull/28))
|
||||
- Add `pretty` option to serializer ([#25](https://github.com/ron-rs/ron/pull/25))
|
||||
- Add roundtrip tests ([#24](https://github.com/ron-rs/ron/pull/24))
|
||||
|
||||
## [0.0.1] - 2015-07-30
|
||||
Initial release
|
32
crates/ron/Cargo.toml
Normal file
32
crates/ron/Cargo.toml
Normal file
|
@ -0,0 +1,32 @@
|
|||
[package]
|
||||
name = "ron"
|
||||
# Memo: update version in src/lib.rs too (doc link)
|
||||
version = "0.6.0"
|
||||
license = "MIT/Apache-2.0"
|
||||
keywords = ["parser", "serde", "serialization"]
|
||||
authors = [
|
||||
"Christopher Durham <cad97@cad97.com>",
|
||||
"Dzmitry Malyshau <kvarkus@gmail.com>",
|
||||
"Thomas Schaller <torkleyy@gmail.com>",
|
||||
]
|
||||
edition = "2018"
|
||||
description = "Rusty Object Notation"
|
||||
categories = ["encoding"]
|
||||
readme = "README.md"
|
||||
homepage = "https://github.com/ron-rs/ron"
|
||||
repository = "https://github.com/ron-rs/ron"
|
||||
documentation = "https://docs.rs/ron/"
|
||||
exclude = ["bors.toml", ".travis.yml"]
|
||||
|
||||
[lib]
|
||||
name = "ron"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.12"
|
||||
bitflags = "1.0.4"
|
||||
indexmap = { version = "1.0.2", features = ["serde-1"], optional = true }
|
||||
serde = { version = "1.0.60", features = ["serde_derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_bytes = "0.11"
|
||||
serde_json = "1"
|
201
crates/ron/LICENSE-APACHE
Normal file
201
crates/ron/LICENSE-APACHE
Normal file
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
25
crates/ron/LICENSE-MIT
Normal file
25
crates/ron/LICENSE-MIT
Normal file
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2017 RON developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
157
crates/ron/README.md
Normal file
157
crates/ron/README.md
Normal file
|
@ -0,0 +1,157 @@
|
|||
# Rusty Object Notation
|
||||
|
||||
[![Build Status](https://travis-ci.org/ron-rs/ron.svg?branch=master)](https://travis-ci.org/ron-rs/ron)
|
||||
[![Crates.io](https://img.shields.io/crates/v/ron.svg)](https://crates.io/crates/ron)
|
||||
[![Docs](https://docs.rs/ron/badge.svg)](https://docs.rs/ron)
|
||||
[![Gitter](https://badges.gitter.im/ron-rs/ron.svg)](https://gitter.im/ron-rs/ron)
|
||||
|
||||
RON is a simple readable data serialization format that looks similar to Rust syntax.
|
||||
It's designed to support all of [Serde's data model](https://serde.rs/data-model.html), so
|
||||
structs, enums, tuples, arrays, generic maps, and primitive values.
|
||||
|
||||
## Example
|
||||
|
||||
```rust
|
||||
GameConfig( // optional struct name
|
||||
window_size: (800, 600),
|
||||
window_title: "PAC-MAN",
|
||||
fullscreen: false,
|
||||
|
||||
mouse_sensitivity: 1.4,
|
||||
key_bindings: {
|
||||
"up": Up,
|
||||
"down": Down,
|
||||
"left": Left,
|
||||
"right": Right,
|
||||
|
||||
// Uncomment to enable WASD controls
|
||||
/*
|
||||
"W": Up,
|
||||
"A": Down,
|
||||
"S": Left,
|
||||
"D": Right,
|
||||
*/
|
||||
},
|
||||
|
||||
difficulty_options: (
|
||||
start_difficulty: Easy,
|
||||
adaptive: false,
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
## Why RON?
|
||||
|
||||
### Example in JSON
|
||||
|
||||
```json
|
||||
{
|
||||
"materials": {
|
||||
"metal": {
|
||||
"reflectivity": 1.0
|
||||
},
|
||||
"plastic": {
|
||||
"reflectivity": 0.5
|
||||
}
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"name": "hero",
|
||||
"material": "metal"
|
||||
},
|
||||
{
|
||||
"name": "monster",
|
||||
"material": "plastic"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Notice these issues:
|
||||
1. Struct and maps are the same
|
||||
- random order of exported fields
|
||||
- annoying and inconvenient for reading
|
||||
- doesn't work well with version control
|
||||
- quoted field names
|
||||
- too verbose
|
||||
- no support for enums
|
||||
2. No trailing comma allowed
|
||||
3. No comments allowed
|
||||
|
||||
### Same example in RON
|
||||
|
||||
```rust
|
||||
Scene( // class name is optional
|
||||
materials: { // this is a map
|
||||
"metal": (
|
||||
reflectivity: 1.0,
|
||||
),
|
||||
"plastic": (
|
||||
reflectivity: 0.5,
|
||||
),
|
||||
},
|
||||
entities: [ // this is an array
|
||||
(
|
||||
name: "hero",
|
||||
material: "metal",
|
||||
),
|
||||
(
|
||||
name: "monster",
|
||||
material: "plastic",
|
||||
),
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
The new format uses `(`..`)` brackets for *heterogeneous* structures (classes),
|
||||
while preserving the `{`..`}` for maps, and `[`..`]` for *homogeneous* structures (arrays).
|
||||
This distinction allows us to solve the biggest problem with JSON.
|
||||
|
||||
Here are the general rules to parse the heterogeneous structures:
|
||||
|
||||
| class is named? | fields are named? | what is it? | example |
|
||||
| --------------- | ------------------| ------------------------- | ------------------- |
|
||||
| no | no | tuple | `(a, b)` |
|
||||
| yes/no | no | tuple struct | `Name(a, b)` |
|
||||
| yes | no | enum value | `Variant(a, b)` |
|
||||
| yes/no | yes | struct | `(f1: a, f2: b,)` |
|
||||
|
||||
### Specification
|
||||
|
||||
There is a very basic, work in progress specification available on
|
||||
[the wiki page](https://github.com/ron-rs/ron/wiki/Specification).
|
||||
A more formal and complete grammar is available [here](docs/grammar.md).
|
||||
|
||||
### Appendix
|
||||
|
||||
Why not XML?
|
||||
- too verbose
|
||||
- unclear how to treat attributes vs contents
|
||||
|
||||
Why not YAML?
|
||||
- significant white-space
|
||||
- specification is too big
|
||||
|
||||
Why not TOML?
|
||||
- alien syntax
|
||||
- absolute paths are not scalable
|
||||
|
||||
Why not XXX?
|
||||
- if you know a better format, tell me!
|
||||
|
||||
## Tooling
|
||||
|
||||
IntelliJ: https://vultix.github.io/intellij-ron-plugin/
|
||||
|
||||
VS Code: https://github.com/a5huynh/vscode-ron
|
||||
|
||||
Sublime Text: https://packagecontrol.io/packages/RON
|
||||
|
||||
Atom: https://atom.io/packages/language-ron
|
||||
|
||||
Vim: https://github.com/ron-rs/ron.vim
|
||||
|
||||
## License
|
||||
|
||||
RON is dual-licensed under Apache-2.0 and MIT.
|
||||
|
67
crates/ron/docs/extensions.md
Normal file
67
crates/ron/docs/extensions.md
Normal file
|
@ -0,0 +1,67 @@
|
|||
## RON extensions
|
||||
|
||||
RON has extensions that can be enabled by adding the following attribute at the top of your RON document:
|
||||
|
||||
`#![enable(...)]`
|
||||
|
||||
# unwrap_newtypes
|
||||
|
||||
You can add this extension by adding the following attribute at the top of your RON document:
|
||||
|
||||
`#![enable(unwrap_newtypes)]`
|
||||
|
||||
This feature enables RON to automatically unwrap simple tuples.
|
||||
|
||||
```rust
|
||||
struct NewType(u32);
|
||||
struct Object {
|
||||
pub new_type: NewType,
|
||||
}
|
||||
```
|
||||
|
||||
Without `unwrap_newtypes`, because the value `5` can not be saved into `NewType(u32)`, your RON document would look like this:
|
||||
|
||||
``` ron
|
||||
(
|
||||
new_type: (5),
|
||||
)
|
||||
```
|
||||
|
||||
With the `unwrap_newtypes` extension, this coercion is done automatically. So `5` will be interpreted as `(5)`.
|
||||
|
||||
``` ron
|
||||
#![enable(unwrap_newtypes)]
|
||||
(
|
||||
new_type: 5,
|
||||
)
|
||||
```
|
||||
|
||||
# implicit_some
|
||||
|
||||
You can add this extension by adding the following attribute at the top of your RON document:
|
||||
|
||||
`#![enable(implicit_some)]`
|
||||
|
||||
This feature enables RON to automatically convert any value to `Some(value)` if the deserialized struct requires it.
|
||||
|
||||
```rust
|
||||
struct Object {
|
||||
pub value: Option<u32>,
|
||||
}
|
||||
```
|
||||
|
||||
Without this feature, you would have to write this RON document.
|
||||
|
||||
```ron
|
||||
(
|
||||
value: Some(5),
|
||||
)
|
||||
```
|
||||
|
||||
Enabling the feature would automatically infer `Some(x)` if `x` is given. In this case, RON automatically casts this `5` into a `Some(5)`.
|
||||
|
||||
```ron
|
||||
(
|
||||
value: 5,
|
||||
)
|
||||
```
|
135
crates/ron/docs/grammar.md
Normal file
135
crates/ron/docs/grammar.md
Normal file
|
@ -0,0 +1,135 @@
|
|||
# RON grammar
|
||||
|
||||
This file describes the structure of a RON file in [EBNF notation][ebnf].
|
||||
If extensions are enabled, some rules will be replaced. For that, see the
|
||||
[extensions document][exts] which describes all extensions and what they override.
|
||||
|
||||
[ebnf]: https://en.wikipedia.org/wiki/Extended_Backus–Naur_form
|
||||
[exts]: ./extensions.md
|
||||
|
||||
## RON file
|
||||
|
||||
```ebnf
|
||||
RON = [extensions], ws, value, ws;
|
||||
```
|
||||
|
||||
## Whitespace and comments
|
||||
|
||||
```ebnf
|
||||
ws = { ws_single, comment };
|
||||
ws_single = "\n" | "\t" | "\r" | " ";
|
||||
comment = ["//", { no_newline }, "\n"];
|
||||
```
|
||||
|
||||
## Commas
|
||||
|
||||
```ebnf
|
||||
comma = ws, ",", ws;
|
||||
```
|
||||
|
||||
## Extensions
|
||||
|
||||
```ebnf
|
||||
extensions = { "#", ws, "!", ws, "[", ws, extensions_inner, ws, "]", ws };
|
||||
extensions_inner = "enable", ws, "(", extension_name, { comma, extension_name }, [comma], ws, ")";
|
||||
```
|
||||
|
||||
For the extension names see the [`extensions.md`][exts] document.
|
||||
|
||||
## Value
|
||||
|
||||
```ebnf
|
||||
value = unsigned | signed | float | string | char | bool | option | list | map | tuple | struct | enum_variant;
|
||||
```
|
||||
|
||||
## Numbers
|
||||
|
||||
```ebnf
|
||||
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
|
||||
unsigned = ["0", ("x" | "b" | "o")], digit, { digit | '_' };
|
||||
signed = ["+" | "-"], unsigned;
|
||||
float = float_std | float_frac;
|
||||
float_std = ["+" | "-"], digit, { digit }, ".", {digit}, [float_exp];
|
||||
float_frac = ".", digit, {digit}, [float_exp];
|
||||
float_exp = ("e" | "E"), digit, {digit};
|
||||
```
|
||||
|
||||
## String
|
||||
|
||||
```ebnf
|
||||
string = string_std | string_raw;
|
||||
string_std = "\"", { no_double_quotation_marks | string_escape }, "\"";
|
||||
string_escape = "\\", ("\"" | "\\" | "b" | "f" | "n" | "r" | "t" | ("u", unicode_hex));
|
||||
string_raw = "r" string_raw_content;
|
||||
string_raw_content = ("#", string_raw_content, "#") | "\"", { unicode_non_greedy }, "\"";
|
||||
```
|
||||
|
||||
> Note: Raw strings start with an `r`, followed by n `#`s and a quotation mark
|
||||
`"`. They may contain any characters or escapes (except the end sequence).
|
||||
A raw string ends with a quotation mark (`"`), followed by n `#`s. n may be
|
||||
any number, including zero.
|
||||
Example:
|
||||
```rust
|
||||
r##"This is a "raw string". It can contain quotations or
|
||||
backslashes (\)!"##
|
||||
```
|
||||
Raw strings cannot be written in EBNF, as they are context-sensitive.
|
||||
Also see [the Rust document] about context-sensitivity of raw strings.
|
||||
|
||||
[the Rust document]: https://github.com/rust-lang/rust/blob/d046ffddc4bd50e04ffc3ff9f766e2ac71f74d50/src/grammar/raw-string-literal-ambiguity.md
|
||||
|
||||
## Char
|
||||
|
||||
```ebnf
|
||||
char = "'", (no_apostrophe | "\\\\" | "\\'"), "'";
|
||||
```
|
||||
|
||||
## Boolean
|
||||
|
||||
```ebnf
|
||||
bool = "true" | "false";
|
||||
```
|
||||
|
||||
## Optional
|
||||
|
||||
```ebnf
|
||||
option = "Some", ws, "(", ws, value, ws, ")";
|
||||
```
|
||||
|
||||
## List
|
||||
|
||||
```ebnf
|
||||
list = "[", [value, { comma, value }, [comma]], "]";
|
||||
```
|
||||
|
||||
## Map
|
||||
|
||||
```ebnf
|
||||
map = "{", [map_entry, { comma, map_entry }, [comma]], "}";
|
||||
map_entry = value, ws, ":", ws, value;
|
||||
```
|
||||
|
||||
## Tuple
|
||||
|
||||
```ebnf
|
||||
tuple = "(", [value, { comma, value }, [comma]], ")";
|
||||
```
|
||||
|
||||
## Struct
|
||||
|
||||
```ebnf
|
||||
struct = unit_struct | tuple_struct | named_struct;
|
||||
unit_struct = ident | "()";
|
||||
tuple_struct = [ident], ws, tuple;
|
||||
named_struct = [ident], ws, "(", [named_field, { comma, named_field }, [comma]], ")";
|
||||
named_field = ident, ws, ":", value;
|
||||
```
|
||||
|
||||
## Enum
|
||||
|
||||
```ebnf
|
||||
enum_variant = enum_variant_unit | enum_variant_tuple | enum_variant_named;
|
||||
enum_variant_unit = ident;
|
||||
enum_variant_tuple = ident, ws, tuple;
|
||||
enum_variant_named = ident, ws, "(", [named_field, { comma, named_field }, [comma]], ")";
|
||||
```
|
65
crates/ron/examples/decode.rs
Normal file
65
crates/ron/examples/decode.rs
Normal file
|
@ -0,0 +1,65 @@
|
|||
use ron::de::from_str;
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Config {
|
||||
boolean: bool,
|
||||
float: f32,
|
||||
map: HashMap<u8, char>,
|
||||
nested: Nested,
|
||||
option: Option<String>,
|
||||
tuple: (u32, u32),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Nested {
|
||||
a: String,
|
||||
b: char,
|
||||
}
|
||||
|
||||
const CONFIG: &str = "
|
||||
/*
|
||||
* RON now has multi-line (C-style) block comments!
|
||||
* They can be freely nested:
|
||||
* /* This is a nested comment */
|
||||
* If you just want a single-line comment,
|
||||
* do it like here:
|
||||
// Just put two slashes before the comment and the rest of the line
|
||||
// can be used freely!
|
||||
*/
|
||||
|
||||
// Note that block comments can not be started in a line comment
|
||||
// (Putting a /* here will have no effect)
|
||||
|
||||
(
|
||||
boolean: true,
|
||||
float: 8.2,
|
||||
map: {
|
||||
1: '1',
|
||||
2: '4',
|
||||
3: '9',
|
||||
4: '1',
|
||||
5: '2',
|
||||
6: '3',
|
||||
},
|
||||
nested: Nested(
|
||||
a: \"Decode me!\",
|
||||
b: 'z',
|
||||
),
|
||||
option: Some(\t \"Weird formatting!\" \n\n ),
|
||||
tuple: (3 /*(2 + 1)*/, 7 /*(2 * 5 - 3)*/),
|
||||
)";
|
||||
|
||||
fn main() {
|
||||
let config: Config = match from_str(CONFIG) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
println!("Failed to load config: {}", e);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
println!("Config: {:?}", &config);
|
||||
}
|
33
crates/ron/examples/decode_file.rs
Normal file
33
crates/ron/examples/decode_file.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use ron::de::from_reader;
|
||||
use serde::Deserialize;
|
||||
use std::{collections::HashMap, fs::File};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Config {
|
||||
boolean: bool,
|
||||
float: f32,
|
||||
map: HashMap<u8, char>,
|
||||
nested: Nested,
|
||||
tuple: (u32, u32),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Nested {
|
||||
a: String,
|
||||
b: char,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let input_path = format!("{}/examples/example.ron", env!("CARGO_MANIFEST_DIR"));
|
||||
let f = File::open(&input_path).expect("Failed opening file");
|
||||
let config: Config = match from_reader(f) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
println!("Failed to load config: {}", e);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
println!("Config: {:?}", &config);
|
||||
}
|
49
crates/ron/examples/encode.rs
Normal file
49
crates/ron/examples/encode.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use ron::ser::{to_string_pretty, PrettyConfig};
|
||||
use serde::Serialize;
|
||||
use std::{collections::HashMap, iter::FromIterator};
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Config {
|
||||
float: (f32, f64),
|
||||
tuple: TupleStruct,
|
||||
map: HashMap<u8, char>,
|
||||
nested: Nested,
|
||||
var: Variant,
|
||||
array: Vec<()>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct TupleStruct((), bool);
|
||||
|
||||
#[derive(Serialize)]
|
||||
enum Variant {
|
||||
A(u8, &'static str),
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Nested {
|
||||
a: String,
|
||||
b: char,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let data = Config {
|
||||
float: (2.18, -1.1),
|
||||
tuple: TupleStruct((), false),
|
||||
map: HashMap::from_iter(vec![(0, '1'), (1, '2'), (3, '5'), (8, '1')]),
|
||||
nested: Nested {
|
||||
a: "Hello from \"RON\"".to_string(),
|
||||
b: 'b',
|
||||
},
|
||||
var: Variant::A(!0, ""),
|
||||
array: vec![(); 3],
|
||||
};
|
||||
|
||||
let pretty = PrettyConfig::new()
|
||||
.with_depth_limit(2)
|
||||
.with_separate_tuple_members(true)
|
||||
.with_enumerate_arrays(true);
|
||||
let s = to_string_pretty(&data, pretty).expect("Serialization failed");
|
||||
|
||||
println!("{}", s);
|
||||
}
|
17
crates/ron/examples/example.ron
Normal file
17
crates/ron/examples/example.ron
Normal file
|
@ -0,0 +1,17 @@
|
|||
(
|
||||
boolean: true,
|
||||
float: 8.2,
|
||||
map: {
|
||||
1: '1',
|
||||
2: '4',
|
||||
3: '9',
|
||||
4: '1',
|
||||
5: '2',
|
||||
6: '3',
|
||||
},
|
||||
nested: Nested(
|
||||
a: "Decode me!",
|
||||
b: 'z',
|
||||
),
|
||||
tuple: (3, 7),
|
||||
)
|
31
crates/ron/examples/transcode.rs
Normal file
31
crates/ron/examples/transcode.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use ron::value::Value;
|
||||
use serde::Serialize;
|
||||
|
||||
fn main() {
|
||||
let data = r#"
|
||||
Scene( // class name is optional
|
||||
materials: { // this is a map
|
||||
"metal": (
|
||||
reflectivity: 1.0,
|
||||
),
|
||||
"plastic": (
|
||||
reflectivity: 0.5,
|
||||
),
|
||||
},
|
||||
entities: [ // this is an array
|
||||
(
|
||||
name: "hero",
|
||||
material: "metal",
|
||||
),
|
||||
(
|
||||
name: "monster",
|
||||
material: "plastic",
|
||||
),
|
||||
],
|
||||
)
|
||||
"#;
|
||||
|
||||
let value: Value = data.parse().expect("Failed to deserialize");
|
||||
let mut ser = serde_json::Serializer::pretty(std::io::stdout());
|
||||
value.serialize(&mut ser).expect("Failed to serialize");
|
||||
}
|
4
crates/ron/rustfmt.toml
Normal file
4
crates/ron/rustfmt.toml
Normal file
|
@ -0,0 +1,4 @@
|
|||
hard_tabs = false
|
||||
use_field_init_shorthand = true
|
||||
use_try_shorthand = true
|
||||
edition = "2018"
|
244
crates/ron/src/de/id.rs
Normal file
244
crates/ron/src/de/id.rs
Normal file
|
@ -0,0 +1,244 @@
|
|||
use serde::de::{self, Visitor};
|
||||
|
||||
use super::{Deserializer, Error, Result};
|
||||
|
||||
pub struct IdDeserializer<'a, 'b: 'a> {
|
||||
d: &'a mut Deserializer<'b>,
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a> IdDeserializer<'a, 'b> {
|
||||
pub fn new(d: &'a mut Deserializer<'b>) -> Self {
|
||||
IdDeserializer { d }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, 'c> de::Deserializer<'b> for &'c mut IdDeserializer<'a, 'b> {
|
||||
type Error = Error;
|
||||
|
||||
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_identifier(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.deserialize_identifier(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.deserialize_identifier(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_bool<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_i8<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_i16<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_i32<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_i64<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_i128<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_u8<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_u16<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_u32<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_u64<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_u128<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_f32<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_f64<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_char<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_string<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_bytes<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_byte_buf<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_option<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_unit<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_unit_struct<V>(self, _: &'static str, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_newtype_struct<V>(self, _: &'static str, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_seq<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_tuple<V>(self, _: usize, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_tuple_struct<V>(self, _: &'static str, _: usize, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_map<V>(self, _: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_struct<V>(
|
||||
self,
|
||||
_: &'static str,
|
||||
_: &'static [&'static str],
|
||||
_: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_enum<V>(
|
||||
self,
|
||||
_: &'static str,
|
||||
_: &'static [&'static str],
|
||||
_: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
unimplemented!("IdDeserializer may only be used for identifiers")
|
||||
}
|
||||
|
||||
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
}
|
685
crates/ron/src/de/mod.rs
Normal file
685
crates/ron/src/de/mod.rs
Normal file
|
@ -0,0 +1,685 @@
|
|||
/// Deserialization module.
|
||||
pub use crate::error::{Error, ErrorCode, Result};
|
||||
pub use crate::parse::Position;
|
||||
|
||||
use serde::de::{self, DeserializeSeed, Deserializer as SerdeError, Visitor};
|
||||
use std::{borrow::Cow, io, str};
|
||||
|
||||
use self::{id::IdDeserializer, tag::TagDeserializer};
|
||||
use crate::{
|
||||
extensions::Extensions,
|
||||
parse::{AnyNum, Bytes, ParsedStr},
|
||||
};
|
||||
|
||||
mod id;
|
||||
mod tag;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod value;
|
||||
|
||||
/// The RON deserializer.
|
||||
///
|
||||
/// If you just want to simply deserialize a value,
|
||||
/// you can use the `from_str` convenience function.
|
||||
pub struct Deserializer<'de> {
|
||||
bytes: Bytes<'de>,
|
||||
}
|
||||
|
||||
impl<'de> Deserializer<'de> {
|
||||
// Cannot implement trait here since output is tied to input lifetime 'de.
|
||||
#[allow(clippy::should_implement_trait)]
|
||||
pub fn from_str(input: &'de str) -> Result<Self> {
|
||||
Deserializer::from_bytes(input.as_bytes())
|
||||
}
|
||||
|
||||
pub fn from_bytes(input: &'de [u8]) -> Result<Self> {
|
||||
Ok(Deserializer {
|
||||
bytes: Bytes::new(input)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn remainder(&self) -> Cow<'_, str> {
|
||||
String::from_utf8_lossy(&self.bytes.bytes())
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience function for reading data from a reader
|
||||
/// and feeding into a deserializer.
|
||||
pub fn from_reader<R, T>(mut rdr: R) -> Result<T>
|
||||
where
|
||||
R: io::Read,
|
||||
T: de::DeserializeOwned,
|
||||
{
|
||||
let mut bytes = Vec::new();
|
||||
rdr.read_to_end(&mut bytes)?;
|
||||
|
||||
from_bytes(&bytes)
|
||||
}
|
||||
|
||||
/// A convenience function for building a deserializer
|
||||
/// and deserializing a value of type `T` from a string.
|
||||
pub fn from_str<'a, T>(s: &'a str) -> Result<T>
|
||||
where
|
||||
T: de::Deserialize<'a>,
|
||||
{
|
||||
from_bytes(s.as_bytes())
|
||||
}
|
||||
|
||||
/// A convenience function for building a deserializer
|
||||
/// and deserializing a value of type `T` from bytes.
|
||||
pub fn from_bytes<'a, T>(s: &'a [u8]) -> Result<T>
|
||||
where
|
||||
T: de::Deserialize<'a>,
|
||||
{
|
||||
let mut deserializer = Deserializer::from_bytes(s)?;
|
||||
let t = T::deserialize(&mut deserializer)?;
|
||||
|
||||
deserializer.end()?;
|
||||
|
||||
Ok(t)
|
||||
}
|
||||
|
||||
impl<'de> Deserializer<'de> {
|
||||
/// Check if the remaining bytes are whitespace only,
|
||||
/// otherwise return an error.
|
||||
pub fn end(&mut self) -> Result<()> {
|
||||
self.bytes.skip_ws()?;
|
||||
|
||||
if self.bytes.bytes().is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
self.bytes.err(ErrorCode::TrailingCharacters)
|
||||
}
|
||||
}
|
||||
|
||||
/// Called from `deserialize_any` when a struct was detected. Decides if
|
||||
/// there is a unit, tuple or usual struct and deserializes it
|
||||
/// accordingly.
|
||||
///
|
||||
/// This method assumes there is no identifier left.
|
||||
fn handle_any_struct<V>(&mut self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
// Create a working copy
|
||||
let mut bytes = self.bytes;
|
||||
|
||||
if bytes.consume("(") {
|
||||
bytes.skip_ws()?;
|
||||
|
||||
if bytes.check_tuple_struct()? {
|
||||
// first argument is technically incorrect, but ignored anyway
|
||||
self.deserialize_tuple(0, visitor)
|
||||
} else {
|
||||
// first two arguments are technically incorrect, but ignored anyway
|
||||
self.deserialize_struct("", &[], visitor)
|
||||
}
|
||||
} else {
|
||||
visitor.visit_unit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
|
||||
type Error = Error;
|
||||
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
if self.bytes.consume_ident("true") {
|
||||
return visitor.visit_bool(true);
|
||||
} else if self.bytes.consume_ident("false") {
|
||||
return visitor.visit_bool(false);
|
||||
} else if self.bytes.check_ident("Some") {
|
||||
return self.deserialize_option(visitor);
|
||||
} else if self.bytes.consume_ident("None") {
|
||||
return visitor.visit_none();
|
||||
} else if self.bytes.consume("()") {
|
||||
return visitor.visit_unit();
|
||||
} else if self.bytes.consume_ident("inf") {
|
||||
return visitor.visit_f64(std::f64::INFINITY);
|
||||
} else if self.bytes.consume_ident("-inf") {
|
||||
return visitor.visit_f64(std::f64::NEG_INFINITY);
|
||||
} else if self.bytes.consume_ident("NaN") {
|
||||
return visitor.visit_f64(std::f64::NAN);
|
||||
}
|
||||
|
||||
// `identifier` does not change state if it fails
|
||||
let ident = self.bytes.identifier().ok();
|
||||
|
||||
if ident.is_some() {
|
||||
self.bytes.skip_ws()?;
|
||||
|
||||
return self.handle_any_struct(visitor);
|
||||
}
|
||||
|
||||
match self.bytes.peek_or_eof()? {
|
||||
b'(' => self.handle_any_struct(visitor),
|
||||
b'[' => self.deserialize_seq(visitor),
|
||||
b'{' => self.deserialize_map(visitor),
|
||||
b'0'..=b'9' | b'+' | b'-' => {
|
||||
let any_num: AnyNum = self.bytes.any_num()?;
|
||||
|
||||
match any_num {
|
||||
AnyNum::F32(x) => visitor.visit_f32(x),
|
||||
AnyNum::F64(x) => visitor.visit_f64(x),
|
||||
AnyNum::I8(x) => visitor.visit_i8(x),
|
||||
AnyNum::U8(x) => visitor.visit_u8(x),
|
||||
AnyNum::I16(x) => visitor.visit_i16(x),
|
||||
AnyNum::U16(x) => visitor.visit_u16(x),
|
||||
AnyNum::I32(x) => visitor.visit_i32(x),
|
||||
AnyNum::U32(x) => visitor.visit_u32(x),
|
||||
AnyNum::I64(x) => visitor.visit_i64(x),
|
||||
AnyNum::U64(x) => visitor.visit_u64(x),
|
||||
AnyNum::I128(x) => visitor.visit_i128(x),
|
||||
AnyNum::U128(x) => visitor.visit_u128(x),
|
||||
}
|
||||
}
|
||||
b'.' => self.deserialize_f64(visitor),
|
||||
b'"' | b'r' => self.deserialize_string(visitor),
|
||||
b'\'' => self.deserialize_char(visitor),
|
||||
other => self.bytes.err(ErrorCode::UnexpectedByte(other as char)),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_bool(self.bytes.bool()?)
|
||||
}
|
||||
|
||||
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i8(self.bytes.signed_integer()?)
|
||||
}
|
||||
|
||||
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i16(self.bytes.signed_integer()?)
|
||||
}
|
||||
|
||||
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i32(self.bytes.signed_integer()?)
|
||||
}
|
||||
|
||||
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i64(self.bytes.signed_integer()?)
|
||||
}
|
||||
|
||||
fn deserialize_i128<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i128(self.bytes.signed_integer()?)
|
||||
}
|
||||
|
||||
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u8(self.bytes.unsigned_integer()?)
|
||||
}
|
||||
|
||||
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u16(self.bytes.unsigned_integer()?)
|
||||
}
|
||||
|
||||
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u32(self.bytes.unsigned_integer()?)
|
||||
}
|
||||
|
||||
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u64(self.bytes.unsigned_integer()?)
|
||||
}
|
||||
|
||||
fn deserialize_u128<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u128(self.bytes.unsigned_integer()?)
|
||||
}
|
||||
|
||||
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_f32(self.bytes.float()?)
|
||||
}
|
||||
|
||||
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_f64(self.bytes.float()?)
|
||||
}
|
||||
|
||||
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_char(self.bytes.char()?)
|
||||
}
|
||||
|
||||
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match self.bytes.string()? {
|
||||
ParsedStr::Allocated(s) => visitor.visit_string(s),
|
||||
ParsedStr::Slice(s) => visitor.visit_borrowed_str(s),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_str(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_byte_buf(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let res = {
|
||||
let string = self.bytes.string()?;
|
||||
let base64_str = match string {
|
||||
ParsedStr::Allocated(ref s) => s.as_str(),
|
||||
ParsedStr::Slice(ref s) => s,
|
||||
};
|
||||
base64::decode(base64_str)
|
||||
};
|
||||
|
||||
match res {
|
||||
Ok(byte_buf) => visitor.visit_byte_buf(byte_buf),
|
||||
Err(err) => self.bytes.err(ErrorCode::Base64Error(err)),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
if self.bytes.consume("None") {
|
||||
visitor.visit_none()
|
||||
} else if self.bytes.exts.contains(Extensions::IMPLICIT_SOME) {
|
||||
visitor.visit_some(&mut *self)
|
||||
} else if self.bytes.consume("Some") && {
|
||||
self.bytes.skip_ws()?;
|
||||
self.bytes.consume("(")
|
||||
} {
|
||||
self.bytes.skip_ws()?;
|
||||
|
||||
let v = visitor.visit_some(&mut *self)?;
|
||||
|
||||
self.bytes.skip_ws()?;
|
||||
|
||||
if self.bytes.consume(")") {
|
||||
Ok(v)
|
||||
} else {
|
||||
self.bytes.err(ErrorCode::ExpectedOptionEnd)
|
||||
}
|
||||
} else {
|
||||
self.bytes.err(ErrorCode::ExpectedOption)
|
||||
}
|
||||
}
|
||||
|
||||
// In Serde, unit means an anonymous value containing no data.
|
||||
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
if self.bytes.consume("()") {
|
||||
visitor.visit_unit()
|
||||
} else {
|
||||
self.bytes.err(ErrorCode::ExpectedUnit)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_unit_struct<V>(self, name: &'static str, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
if self.bytes.consume(name) {
|
||||
visitor.visit_unit()
|
||||
} else {
|
||||
self.deserialize_unit(visitor)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_newtype_struct<V>(self, name: &'static str, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
if self.bytes.exts.contains(Extensions::UNWRAP_NEWTYPES) {
|
||||
return visitor.visit_newtype_struct(&mut *self);
|
||||
}
|
||||
|
||||
self.bytes.consume(name);
|
||||
|
||||
self.bytes.skip_ws()?;
|
||||
|
||||
if self.bytes.consume("(") {
|
||||
self.bytes.skip_ws()?;
|
||||
let value = visitor.visit_newtype_struct(&mut *self)?;
|
||||
self.bytes.comma()?;
|
||||
|
||||
if self.bytes.consume(")") {
|
||||
Ok(value)
|
||||
} else {
|
||||
self.bytes.err(ErrorCode::ExpectedStructEnd)
|
||||
}
|
||||
} else {
|
||||
self.bytes.err(ErrorCode::ExpectedStruct)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_seq<V>(mut self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
if self.bytes.consume("[") {
|
||||
let value = visitor.visit_seq(CommaSeparated::new(b']', &mut self))?;
|
||||
self.bytes.comma()?;
|
||||
|
||||
if self.bytes.consume("]") {
|
||||
Ok(value)
|
||||
} else {
|
||||
self.bytes.err(ErrorCode::ExpectedArrayEnd)
|
||||
}
|
||||
} else {
|
||||
self.bytes.err(ErrorCode::ExpectedArray)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_tuple<V>(mut self, _len: usize, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
if self.bytes.consume("(") {
|
||||
let value = visitor.visit_seq(CommaSeparated::new(b')', &mut self))?;
|
||||
self.bytes.comma()?;
|
||||
|
||||
if self.bytes.consume(")") {
|
||||
Ok(value)
|
||||
} else {
|
||||
self.bytes.err(ErrorCode::ExpectedArrayEnd)
|
||||
}
|
||||
} else {
|
||||
self.bytes.err(ErrorCode::ExpectedArray)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_tuple_struct<V>(
|
||||
self,
|
||||
name: &'static str,
|
||||
len: usize,
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.bytes.consume(name);
|
||||
self.deserialize_tuple(len, visitor)
|
||||
}
|
||||
|
||||
fn deserialize_map<V>(mut self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
if self.bytes.consume("{") {
|
||||
let value = visitor.visit_map(CommaSeparated::new(b'}', &mut self))?;
|
||||
self.bytes.comma()?;
|
||||
|
||||
if self.bytes.consume("}") {
|
||||
Ok(value)
|
||||
} else {
|
||||
self.bytes.err(ErrorCode::ExpectedMapEnd)
|
||||
}
|
||||
} else {
|
||||
self.bytes.err(ErrorCode::ExpectedMap)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_struct<V>(
|
||||
mut self,
|
||||
name: &'static str,
|
||||
_fields: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.bytes.consume(name);
|
||||
|
||||
self.bytes.skip_ws()?;
|
||||
|
||||
if self.bytes.consume("(") {
|
||||
let value = visitor.visit_map(CommaSeparated::new(b')', &mut self))?;
|
||||
self.bytes.comma()?;
|
||||
|
||||
if self.bytes.consume(")") {
|
||||
Ok(value)
|
||||
} else {
|
||||
self.bytes.err(ErrorCode::ExpectedStructEnd)
|
||||
}
|
||||
} else {
|
||||
self.bytes.err(ErrorCode::ExpectedStruct)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_enum<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variants: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_enum(Enum::new(self))
|
||||
}
|
||||
|
||||
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_str(
|
||||
str::from_utf8(self.bytes.identifier()?).map_err(|e| self.bytes.error(e.into()))?,
|
||||
)
|
||||
}
|
||||
|
||||
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct CommaSeparated<'a, 'de: 'a> {
|
||||
de: &'a mut Deserializer<'de>,
|
||||
terminator: u8,
|
||||
had_comma: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'de> CommaSeparated<'a, 'de> {
|
||||
fn new(terminator: u8, de: &'a mut Deserializer<'de>) -> Self {
|
||||
CommaSeparated {
|
||||
de,
|
||||
terminator,
|
||||
had_comma: true,
|
||||
}
|
||||
}
|
||||
|
||||
fn err<T>(&self, kind: ErrorCode) -> Result<T> {
|
||||
self.de.bytes.err(kind)
|
||||
}
|
||||
|
||||
fn has_element(&mut self) -> Result<bool> {
|
||||
self.de.bytes.skip_ws()?;
|
||||
|
||||
Ok(self.had_comma && self.de.bytes.peek_or_eof()? != self.terminator)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'a> de::SeqAccess<'de> for CommaSeparated<'a, 'de> {
|
||||
type Error = Error;
|
||||
|
||||
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
||||
where
|
||||
T: DeserializeSeed<'de>,
|
||||
{
|
||||
if self.has_element()? {
|
||||
let res = seed.deserialize(&mut *self.de)?;
|
||||
|
||||
self.had_comma = self.de.bytes.comma()?;
|
||||
|
||||
Ok(Some(res))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'a> de::MapAccess<'de> for CommaSeparated<'a, 'de> {
|
||||
type Error = Error;
|
||||
|
||||
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
|
||||
where
|
||||
K: DeserializeSeed<'de>,
|
||||
{
|
||||
if self.has_element()? {
|
||||
if self.terminator == b')' {
|
||||
seed.deserialize(&mut IdDeserializer::new(&mut *self.de))
|
||||
.map(Some)
|
||||
} else {
|
||||
seed.deserialize(&mut *self.de).map(Some)
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
|
||||
where
|
||||
V: DeserializeSeed<'de>,
|
||||
{
|
||||
self.de.bytes.skip_ws()?;
|
||||
|
||||
if self.de.bytes.consume(":") {
|
||||
self.de.bytes.skip_ws()?;
|
||||
|
||||
let res = seed.deserialize(&mut TagDeserializer::new(&mut *self.de))?;
|
||||
|
||||
self.had_comma = self.de.bytes.comma()?;
|
||||
|
||||
Ok(res)
|
||||
} else {
|
||||
self.err(ErrorCode::ExpectedMapColon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Enum<'a, 'de: 'a> {
|
||||
de: &'a mut Deserializer<'de>,
|
||||
}
|
||||
|
||||
impl<'a, 'de> Enum<'a, 'de> {
|
||||
fn new(de: &'a mut Deserializer<'de>) -> Self {
|
||||
Enum { de }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'a> de::EnumAccess<'de> for Enum<'a, 'de> {
|
||||
type Error = Error;
|
||||
type Variant = Self;
|
||||
|
||||
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
|
||||
where
|
||||
V: DeserializeSeed<'de>,
|
||||
{
|
||||
self.de.bytes.skip_ws()?;
|
||||
|
||||
let value = seed.deserialize(&mut *self.de)?;
|
||||
|
||||
Ok((value, self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'a> de::VariantAccess<'de> for Enum<'a, 'de> {
|
||||
type Error = Error;
|
||||
|
||||
fn unit_variant(self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
|
||||
where
|
||||
T: DeserializeSeed<'de>,
|
||||
{
|
||||
self.de.bytes.skip_ws()?;
|
||||
|
||||
if self.de.bytes.consume("(") {
|
||||
self.de.bytes.skip_ws()?;
|
||||
|
||||
let val = seed.deserialize(&mut *self.de)?;
|
||||
|
||||
self.de.bytes.comma()?;
|
||||
|
||||
if self.de.bytes.consume(")") {
|
||||
Ok(val)
|
||||
} else {
|
||||
self.de.bytes.err(ErrorCode::ExpectedStructEnd)
|
||||
}
|
||||
} else {
|
||||
self.de.bytes.err(ErrorCode::ExpectedStruct)
|
||||
}
|
||||
}
|
||||
|
||||
fn tuple_variant<V>(self, len: usize, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.de.bytes.skip_ws()?;
|
||||
|
||||
self.de.deserialize_tuple(len, visitor)
|
||||
}
|
||||
|
||||
fn struct_variant<V>(self, fields: &'static [&'static str], visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.de.bytes.skip_ws()?;
|
||||
|
||||
self.de.deserialize_struct("", fields, visitor)
|
||||
}
|
||||
}
|
249
crates/ron/src/de/tag.rs
Normal file
249
crates/ron/src/de/tag.rs
Normal file
|
@ -0,0 +1,249 @@
|
|||
use serde::de::{self, Visitor};
|
||||
|
||||
use super::{Deserializer, Error, Result};
|
||||
|
||||
pub struct TagDeserializer<'a, 'b: 'a> {
|
||||
d: &'a mut Deserializer<'b>,
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a> TagDeserializer<'a, 'b> {
|
||||
pub fn new(d: &'a mut Deserializer<'b>) -> Self {
|
||||
TagDeserializer { d }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, 'c> de::Deserializer<'b> for &'c mut TagDeserializer<'a, 'b> {
|
||||
type Error = Error;
|
||||
|
||||
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_str(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.deserialize_str(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_any(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_bool(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_i8(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_i16(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_i32(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_i64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i128<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_i128(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_u8(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_u16(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_u32(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_u64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u128<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_u128(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_f32(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_f64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_char(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_string(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_bytes(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_byte_buf(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_option(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_unit(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_unit_struct<V>(self, name: &'static str, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_unit_struct(name, visitor)
|
||||
}
|
||||
|
||||
fn deserialize_newtype_struct<V>(self, name: &'static str, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_newtype_struct(name, visitor)
|
||||
}
|
||||
|
||||
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_seq(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_tuple(len, visitor)
|
||||
}
|
||||
|
||||
fn deserialize_tuple_struct<V>(
|
||||
self,
|
||||
name: &'static str,
|
||||
len: usize,
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_tuple_struct(name, len, visitor)
|
||||
}
|
||||
|
||||
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_map(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_struct<V>(
|
||||
self,
|
||||
name: &'static str,
|
||||
fields: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_struct(name, fields, visitor)
|
||||
}
|
||||
|
||||
fn deserialize_enum<V>(
|
||||
self,
|
||||
name: &'static str,
|
||||
variants: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_enum(name, variants, visitor)
|
||||
}
|
||||
|
||||
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'b>,
|
||||
{
|
||||
self.d.deserialize_ignored_any(visitor)
|
||||
}
|
||||
}
|
328
crates/ron/src/de/tests.rs
Normal file
328
crates/ron/src/de/tests.rs
Normal file
|
@ -0,0 +1,328 @@
|
|||
use serde::Deserialize;
|
||||
use serde_bytes;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
struct EmptyStruct1;
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
struct EmptyStruct2 {}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Deserialize)]
|
||||
struct MyStruct {
|
||||
x: f32,
|
||||
y: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Deserialize)]
|
||||
enum MyEnum {
|
||||
A,
|
||||
B(bool),
|
||||
C(bool, f32),
|
||||
D { a: i32, b: i32 },
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
struct BytesStruct {
|
||||
small: Vec<u8>,
|
||||
#[serde(with = "serde_bytes")]
|
||||
large: Vec<u8>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_struct() {
|
||||
assert_eq!(Ok(EmptyStruct1), from_str("EmptyStruct1"));
|
||||
assert_eq!(Ok(EmptyStruct2 {}), from_str("EmptyStruct2()"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct() {
|
||||
let my_struct = MyStruct { x: 4.0, y: 7.0 };
|
||||
|
||||
assert_eq!(Ok(my_struct), from_str("MyStruct(x:4,y:7,)"));
|
||||
assert_eq!(Ok(my_struct), from_str("(x:4,y:7)"));
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
struct NewType(i32);
|
||||
|
||||
assert_eq!(Ok(NewType(42)), from_str("NewType(42)"));
|
||||
assert_eq!(Ok(NewType(33)), from_str("(33)"));
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
struct TupleStruct(f32, f32);
|
||||
|
||||
assert_eq!(Ok(TupleStruct(2.0, 5.0)), from_str("TupleStruct(2,5,)"));
|
||||
assert_eq!(Ok(TupleStruct(3.0, 4.0)), from_str("(3,4)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_option() {
|
||||
assert_eq!(Ok(Some(1u8)), from_str("Some(1)"));
|
||||
assert_eq!(Ok(None::<u8>), from_str("None"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enum() {
|
||||
assert_eq!(Ok(MyEnum::A), from_str("A"));
|
||||
assert_eq!(Ok(MyEnum::B(true)), from_str("B(true,)"));
|
||||
assert_eq!(Ok(MyEnum::C(true, 3.5)), from_str("C(true,3.5,)"));
|
||||
assert_eq!(Ok(MyEnum::D { a: 2, b: 3 }), from_str("D(a:2,b:3,)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_array() {
|
||||
let empty: [i32; 0] = [];
|
||||
assert_eq!(Ok(empty), from_str("()"));
|
||||
let empty_array = empty.to_vec();
|
||||
assert_eq!(Ok(empty_array), from_str("[]"));
|
||||
|
||||
assert_eq!(Ok([2, 3, 4i32]), from_str("(2,3,4,)"));
|
||||
assert_eq!(Ok([2, 3, 4i32].to_vec()), from_str("[2,3,4,]"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map() {
|
||||
use std::collections::HashMap;
|
||||
|
||||
let mut map = HashMap::new();
|
||||
map.insert((true, false), 4);
|
||||
map.insert((false, false), 123);
|
||||
|
||||
assert_eq!(
|
||||
Ok(map),
|
||||
from_str(
|
||||
"{
|
||||
(true,false,):4,
|
||||
(false,false,):123,
|
||||
}"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string() {
|
||||
let s: String = from_str("\"String\"").unwrap();
|
||||
assert_eq!("String", s);
|
||||
|
||||
let raw: String = from_str("r\"String\"").unwrap();
|
||||
assert_eq!("String", raw);
|
||||
|
||||
let raw_hashes: String = from_str("r#\"String\"#").unwrap();
|
||||
assert_eq!("String", raw_hashes);
|
||||
|
||||
let raw_hashes_multiline: String = from_str("r#\"String with\nmultiple\nlines\n\"#").unwrap();
|
||||
assert_eq!("String with\nmultiple\nlines\n", raw_hashes_multiline);
|
||||
|
||||
let raw_hashes_quote: String = from_str("r##\"String with \"#\"##").unwrap();
|
||||
assert_eq!("String with \"#", raw_hashes_quote);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_char() {
|
||||
assert_eq!(Ok('c'), from_str("'c'"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_escape_char() {
|
||||
assert_eq!('\'', from_str::<char>("'\\''").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_escape() {
|
||||
assert_eq!("\"Quoted\"", from_str::<String>(r#""\"Quoted\"""#).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_comment() {
|
||||
assert_eq!(
|
||||
MyStruct { x: 1.0, y: 2.0 },
|
||||
from_str(
|
||||
"(
|
||||
x: 1.0, // x is just 1
|
||||
// There is another comment in the very next line..
|
||||
// And y is indeed
|
||||
y: 2.0 // 2!
|
||||
)"
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
fn err<T>(kind: ErrorCode, line: usize, col: usize) -> Result<T> {
|
||||
Err(Error {
|
||||
code: kind,
|
||||
position: Position { line, col },
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_err_wrong_value() {
|
||||
use self::ErrorCode::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
assert_eq!(from_str::<f32>("'c'"), err(ExpectedFloat, 1, 1));
|
||||
assert_eq!(from_str::<String>("'c'"), err(ExpectedString, 1, 1));
|
||||
assert_eq!(from_str::<HashMap<u32, u32>>("'c'"), err(ExpectedMap, 1, 1));
|
||||
assert_eq!(from_str::<[u8; 5]>("'c'"), err(ExpectedArray, 1, 1));
|
||||
assert_eq!(from_str::<Vec<u32>>("'c'"), err(ExpectedArray, 1, 1));
|
||||
assert_eq!(from_str::<MyEnum>("'c'"), err(ExpectedIdentifier, 1, 1));
|
||||
assert_eq!(from_str::<MyStruct>("'c'"), err(ExpectedStruct, 1, 1));
|
||||
assert_eq!(from_str::<(u8, bool)>("'c'"), err(ExpectedArray, 1, 1));
|
||||
assert_eq!(from_str::<bool>("notabool"), err(ExpectedBoolean, 1, 1));
|
||||
|
||||
assert_eq!(
|
||||
from_str::<MyStruct>("MyStruct(\n x: true)"),
|
||||
err(ExpectedFloat, 2, 8)
|
||||
);
|
||||
assert_eq!(
|
||||
from_str::<MyStruct>("MyStruct(\n x: 3.5, \n y:)"),
|
||||
err(ExpectedFloat, 3, 7)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_perm_ws() {
|
||||
assert_eq!(
|
||||
from_str::<MyStruct>("\nMyStruct \t ( \n x : 3.5 , \t y\n: 4.5 \n ) \t\n"),
|
||||
Ok(MyStruct { x: 3.5, y: 4.5 })
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn untagged() {
|
||||
#[derive(Deserialize, Debug, PartialEq)]
|
||||
#[serde(untagged)]
|
||||
enum Untagged {
|
||||
U8(u8),
|
||||
Bool(bool),
|
||||
}
|
||||
|
||||
assert_eq!(from_str::<Untagged>("true").unwrap(), Untagged::Bool(true));
|
||||
assert_eq!(from_str::<Untagged>("8").unwrap(), Untagged::U8(8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forgot_apostrophes() {
|
||||
let de: Result<(i32, String)> = from_str("(4, \"Hello)");
|
||||
|
||||
assert!(match de {
|
||||
Err(Error {
|
||||
code: ErrorCode::ExpectedStringEnd,
|
||||
position: _,
|
||||
}) => true,
|
||||
_ => false,
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expected_attribute() {
|
||||
let de: Result<String> = from_str("#\"Hello\"");
|
||||
|
||||
assert_eq!(de, err(ErrorCode::ExpectedAttribute, 1, 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expected_attribute_end() {
|
||||
let de: Result<String> = from_str("#![enable(unwrap_newtypes) \"Hello\"");
|
||||
|
||||
assert_eq!(de, err(ErrorCode::ExpectedAttributeEnd, 1, 28));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_attribute() {
|
||||
let de: Result<String> = from_str("#![enable(invalid)] \"Hello\"");
|
||||
|
||||
assert_eq!(
|
||||
de,
|
||||
err(ErrorCode::NoSuchExtension("invalid".to_string()), 1, 18)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_attributes() {
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
struct New(String);
|
||||
let de: Result<New> =
|
||||
from_str("#![enable(unwrap_newtypes)] #![enable(unwrap_newtypes)] \"Hello\"");
|
||||
|
||||
assert_eq!(de, Ok(New("Hello".to_owned())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uglified_attribute() {
|
||||
let de: Result<()> = from_str(
|
||||
"# !\
|
||||
// We definitely want to add a comment here
|
||||
[\t\tenable( // best style ever
|
||||
unwrap_newtypes ) ] ()",
|
||||
);
|
||||
|
||||
assert_eq!(de, Ok(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn implicit_some() {
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
fn de<T: DeserializeOwned>(s: &str) -> Option<T> {
|
||||
let enable = "#![enable(implicit_some)]\n".to_string();
|
||||
|
||||
from_str::<Option<T>>(&(enable + s)).unwrap()
|
||||
}
|
||||
|
||||
assert_eq!(de("'c'"), Some('c'));
|
||||
assert_eq!(de("5"), Some(5));
|
||||
assert_eq!(de("\"Hello\""), Some("Hello".to_owned()));
|
||||
assert_eq!(de("false"), Some(false));
|
||||
assert_eq!(
|
||||
de("MyStruct(x: .4, y: .5)"),
|
||||
Some(MyStruct { x: 0.4, y: 0.5 })
|
||||
);
|
||||
|
||||
assert_eq!(de::<char>("None"), None);
|
||||
|
||||
// Not concise
|
||||
assert_eq!(de::<Option<Option<char>>>("None"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ws_tuple_newtype_variant() {
|
||||
assert_eq!(Ok(MyEnum::B(true)), from_str("B ( \n true \n ) "));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_byte_stream() {
|
||||
assert_eq!(
|
||||
Ok(BytesStruct {
|
||||
small: vec![1, 2],
|
||||
large: vec![1, 2, 3, 4]
|
||||
}),
|
||||
from_str("BytesStruct( small:[1, 2], large:\"AQIDBA==\" )"),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_numbers() {
|
||||
assert_eq!(
|
||||
Ok(vec![1234, 12345, 123456, 1234567, 555_555]),
|
||||
from_str("[1_234, 12_345, 1_2_3_4_5_6, 1_234_567, 5_55_55_5]"),
|
||||
);
|
||||
}
|
||||
|
||||
fn de_any_number(s: &str) -> AnyNum {
|
||||
let mut bytes = Bytes::new(s.as_bytes()).unwrap();
|
||||
|
||||
bytes.any_num().unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_any_number_precision() {
|
||||
assert_eq!(de_any_number("1"), AnyNum::U8(1));
|
||||
assert_eq!(de_any_number("+1"), AnyNum::I8(1));
|
||||
assert_eq!(de_any_number("-1"), AnyNum::I8(-1));
|
||||
assert_eq!(de_any_number("-1.0"), AnyNum::F32(-1.0));
|
||||
assert_eq!(de_any_number("1."), AnyNum::F32(1.));
|
||||
assert_eq!(de_any_number("-1."), AnyNum::F32(-1.));
|
||||
assert_eq!(de_any_number("0.3"), AnyNum::F64(0.3));
|
||||
}
|
335
crates/ron/src/de/value.rs
Normal file
335
crates/ron/src/de/value.rs
Normal file
|
@ -0,0 +1,335 @@
|
|||
use std::fmt;
|
||||
|
||||
use serde::{
|
||||
de::{Error, MapAccess, SeqAccess, Visitor},
|
||||
Deserialize, Deserializer,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
de,
|
||||
value::{Map, Number, Value},
|
||||
};
|
||||
|
||||
impl std::str::FromStr for Value {
|
||||
type Err = de::Error;
|
||||
|
||||
/// Creates a value from a string reference.
|
||||
fn from_str(s: &str) -> de::Result<Self> {
|
||||
let mut de = super::Deserializer::from_str(s)?;
|
||||
|
||||
let val = Value::deserialize(&mut de)?;
|
||||
de.end()?;
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Value {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_any(ValueVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct ValueVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for ValueVisitor {
|
||||
type Value = Value;
|
||||
|
||||
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "a RON value")
|
||||
}
|
||||
|
||||
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
Ok(Value::Bool(v))
|
||||
}
|
||||
|
||||
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
Ok(Value::Number(Number::new(v)))
|
||||
}
|
||||
|
||||
fn visit_i128<E>(self, v: i128) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
self.visit_f64(v as f64)
|
||||
}
|
||||
|
||||
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
Ok(Value::Number(Number::new(v)))
|
||||
}
|
||||
|
||||
fn visit_u128<E>(self, v: u128) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
self.visit_f64(v as f64)
|
||||
}
|
||||
|
||||
fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
Ok(Value::Number(Number::new(v)))
|
||||
}
|
||||
|
||||
fn visit_char<E>(self, v: char) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
Ok(Value::Char(v))
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
self.visit_string(v.to_owned())
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
Ok(Value::String(v))
|
||||
}
|
||||
|
||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
self.visit_byte_buf(v.to_vec())
|
||||
}
|
||||
|
||||
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
self.visit_string(String::from_utf8(v).map_err(|e| Error::custom(format!("{}", e)))?)
|
||||
}
|
||||
|
||||
fn visit_none<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
Ok(Value::Option(None))
|
||||
}
|
||||
|
||||
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(Value::Option(Some(Box::new(
|
||||
deserializer.deserialize_any(ValueVisitor)?,
|
||||
))))
|
||||
}
|
||||
|
||||
fn visit_unit<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
Ok(Value::Unit)
|
||||
}
|
||||
|
||||
fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_any(ValueVisitor)
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: SeqAccess<'de>,
|
||||
{
|
||||
let mut vec = Vec::new();
|
||||
if let Some(cap) = seq.size_hint() {
|
||||
vec.reserve_exact(cap);
|
||||
}
|
||||
|
||||
while let Some(x) = seq.next_element()? {
|
||||
vec.push(x);
|
||||
}
|
||||
|
||||
Ok(Value::Seq(vec))
|
||||
}
|
||||
|
||||
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: MapAccess<'de>,
|
||||
{
|
||||
let mut res: Map = Map::new();
|
||||
|
||||
while let Some(entry) = map.next_entry()? {
|
||||
res.insert(entry.0, entry.1);
|
||||
}
|
||||
|
||||
Ok(Value::Map(res))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
|
||||
fn eval(s: &str) -> Value {
|
||||
s.parse().expect("Failed to parse")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_none() {
|
||||
assert_eq!(eval("None"), Value::Option(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_some() {
|
||||
assert_eq!(eval("Some(())"), Value::Option(Some(Box::new(Value::Unit))));
|
||||
assert_eq!(
|
||||
eval("Some ( () )"),
|
||||
Value::Option(Some(Box::new(Value::Unit)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tuples_basic() {
|
||||
assert_eq!(
|
||||
eval("(3, 4.0, 5.0)"),
|
||||
Value::Seq(vec![
|
||||
Value::Number(Number::new(3)),
|
||||
Value::Number(Number::new(4.0)),
|
||||
Value::Number(Number::new(5.0)),
|
||||
],),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tuples_ident() {
|
||||
assert_eq!(
|
||||
eval("(true, 3, 4, 5.0)"),
|
||||
Value::Seq(vec![
|
||||
Value::Bool(true),
|
||||
Value::Number(Number::new(3)),
|
||||
Value::Number(Number::new(4)),
|
||||
Value::Number(Number::new(5.0)),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tuples_error() {
|
||||
use crate::de::{Error, ErrorCode, Position};
|
||||
|
||||
assert_eq!(
|
||||
Value::from_str("Foo:").unwrap_err(),
|
||||
Error {
|
||||
code: ErrorCode::TrailingCharacters,
|
||||
position: Position { col: 4, line: 1 }
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_floats() {
|
||||
assert_eq!(
|
||||
eval("(inf, -inf, NaN)"),
|
||||
Value::Seq(vec![
|
||||
Value::Number(Number::new(std::f64::INFINITY)),
|
||||
Value::Number(Number::new(std::f64::NEG_INFINITY)),
|
||||
Value::Number(Number::new(std::f64::NAN)),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_complex() {
|
||||
assert_eq!(
|
||||
eval(
|
||||
"Some([
|
||||
Room ( width: 20, height: 5, name: \"The Room\" ),
|
||||
|
||||
(
|
||||
width: 10.0,
|
||||
height: 10.0,
|
||||
name: \"Another room\",
|
||||
enemy_levels: {
|
||||
\"Enemy1\": 3,
|
||||
\"Enemy2\": 5,
|
||||
\"Enemy3\": 7,
|
||||
},
|
||||
),
|
||||
])"
|
||||
),
|
||||
Value::Option(Some(Box::new(Value::Seq(vec![
|
||||
Value::Map(
|
||||
vec![
|
||||
(
|
||||
Value::String("width".to_owned()),
|
||||
Value::Number(Number::new(20)),
|
||||
),
|
||||
(
|
||||
Value::String("height".to_owned()),
|
||||
Value::Number(Number::new(5)),
|
||||
),
|
||||
(
|
||||
Value::String("name".to_owned()),
|
||||
Value::String("The Room".to_owned()),
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
Value::Map(
|
||||
vec![
|
||||
(
|
||||
Value::String("width".to_owned()),
|
||||
Value::Number(Number::new(10.0)),
|
||||
),
|
||||
(
|
||||
Value::String("height".to_owned()),
|
||||
Value::Number(Number::new(10.0)),
|
||||
),
|
||||
(
|
||||
Value::String("name".to_owned()),
|
||||
Value::String("Another room".to_owned()),
|
||||
),
|
||||
(
|
||||
Value::String("enemy_levels".to_owned()),
|
||||
Value::Map(
|
||||
vec![
|
||||
(
|
||||
Value::String("Enemy1".to_owned()),
|
||||
Value::Number(Number::new(3)),
|
||||
),
|
||||
(
|
||||
Value::String("Enemy2".to_owned()),
|
||||
Value::Number(Number::new(5)),
|
||||
),
|
||||
(
|
||||
Value::String("Enemy3".to_owned()),
|
||||
Value::Number(Number::new(7)),
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
]))))
|
||||
);
|
||||
}
|
||||
}
|
164
crates/ron/src/error.rs
Normal file
164
crates/ron/src/error.rs
Normal file
|
@ -0,0 +1,164 @@
|
|||
use serde::{de, ser};
|
||||
use std::{error::Error as StdError, fmt, io, str::Utf8Error, string::FromUtf8Error};
|
||||
|
||||
pub use crate::parse::Position;
|
||||
|
||||
/// This type represents all possible errors that can occur when
|
||||
/// serializing or deserializing RON data.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Error {
|
||||
pub code: ErrorCode,
|
||||
pub position: Position,
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ErrorCode {
|
||||
Io(String),
|
||||
Message(String),
|
||||
Base64Error(base64::DecodeError),
|
||||
Eof,
|
||||
ExpectedArray,
|
||||
ExpectedArrayEnd,
|
||||
ExpectedAttribute,
|
||||
ExpectedAttributeEnd,
|
||||
ExpectedBoolean,
|
||||
ExpectedComma,
|
||||
// ExpectedEnum,
|
||||
ExpectedChar,
|
||||
ExpectedFloat,
|
||||
ExpectedInteger,
|
||||
ExpectedOption,
|
||||
ExpectedOptionEnd,
|
||||
ExpectedMap,
|
||||
ExpectedMapColon,
|
||||
ExpectedMapEnd,
|
||||
ExpectedStruct,
|
||||
ExpectedStructEnd,
|
||||
ExpectedUnit,
|
||||
// ExpectedStructName,
|
||||
ExpectedString,
|
||||
ExpectedStringEnd,
|
||||
ExpectedIdentifier,
|
||||
|
||||
InvalidEscape(&'static str),
|
||||
|
||||
IntegerOutOfBounds,
|
||||
|
||||
NoSuchExtension(String),
|
||||
|
||||
UnclosedBlockComment,
|
||||
UnderscoreAtBeginning,
|
||||
UnexpectedByte(char),
|
||||
|
||||
Utf8Error(Utf8Error),
|
||||
TrailingCharacters,
|
||||
|
||||
#[doc(hidden)]
|
||||
__Nonexhaustive,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if (self.position == Position { line: 0, col: 0 }) {
|
||||
write!(f, "{}", self.code)
|
||||
} else {
|
||||
write!(f, "{}: {}", self.position, self.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ErrorCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
ErrorCode::Io(ref s) => f.write_str(s),
|
||||
ErrorCode::Message(ref s) => f.write_str(s),
|
||||
ErrorCode::Base64Error(ref e) => fmt::Display::fmt(e, f),
|
||||
ErrorCode::Eof => f.write_str("Unexpected end of file"),
|
||||
ErrorCode::ExpectedArray => f.write_str("Expected array"),
|
||||
ErrorCode::ExpectedArrayEnd => f.write_str("Expected end of array"),
|
||||
ErrorCode::ExpectedAttribute => f.write_str("Expected an enable attribute"),
|
||||
ErrorCode::ExpectedAttributeEnd => {
|
||||
f.write_str("Expected closing `)` and `]` after the attribute")
|
||||
}
|
||||
ErrorCode::ExpectedBoolean => f.write_str("Expected boolean"),
|
||||
ErrorCode::ExpectedComma => f.write_str("Expected comma"),
|
||||
// ErrorCode::ExpectedEnum => f.write_str("Expected enum"),
|
||||
ErrorCode::ExpectedChar => f.write_str("Expected char"),
|
||||
ErrorCode::ExpectedFloat => f.write_str("Expected float"),
|
||||
ErrorCode::ExpectedInteger => f.write_str("Expected integer"),
|
||||
ErrorCode::ExpectedOption => f.write_str("Expected option"),
|
||||
ErrorCode::ExpectedOptionEnd => f.write_str("Expected end of option"),
|
||||
ErrorCode::ExpectedMap => f.write_str("Expected map"),
|
||||
ErrorCode::ExpectedMapColon => f.write_str("Expected colon"),
|
||||
ErrorCode::ExpectedMapEnd => f.write_str("Expected end of map"),
|
||||
ErrorCode::ExpectedStruct => f.write_str("Expected struct"),
|
||||
ErrorCode::ExpectedStructEnd => f.write_str("Expected end of struct"),
|
||||
ErrorCode::ExpectedUnit => f.write_str("Expected unit"),
|
||||
// ErrorCode::ExpectedStructName => f.write_str("Expected struct name"),
|
||||
ErrorCode::ExpectedString => f.write_str("Expected string"),
|
||||
ErrorCode::ExpectedStringEnd => f.write_str("Expected string end"),
|
||||
ErrorCode::ExpectedIdentifier => f.write_str("Expected identifier"),
|
||||
ErrorCode::InvalidEscape(_) => f.write_str("Invalid escape sequence"),
|
||||
ErrorCode::IntegerOutOfBounds => f.write_str("Integer is out of bounds"),
|
||||
ErrorCode::NoSuchExtension(_) => f.write_str("No such RON extension"),
|
||||
ErrorCode::Utf8Error(ref e) => fmt::Display::fmt(e, f),
|
||||
ErrorCode::UnclosedBlockComment => f.write_str("Unclosed block comment"),
|
||||
ErrorCode::UnderscoreAtBeginning => f.write_str("Found underscore at the beginning"),
|
||||
ErrorCode::UnexpectedByte(_) => f.write_str("Unexpected byte"),
|
||||
ErrorCode::TrailingCharacters => f.write_str("Non-whitespace trailing characters"),
|
||||
_ => f.write_str("Unknown ErrorCode"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl de::Error for Error {
|
||||
fn custom<T: fmt::Display>(msg: T) -> Self {
|
||||
Error {
|
||||
code: ErrorCode::Message(msg.to_string()),
|
||||
position: Position { line: 0, col: 0 },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ser::Error for Error {
|
||||
fn custom<T: fmt::Display>(msg: T) -> Self {
|
||||
Error {
|
||||
code: ErrorCode::Message(msg.to_string()),
|
||||
position: Position { line: 0, col: 0 },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for Error {}
|
||||
|
||||
impl From<Utf8Error> for ErrorCode {
|
||||
fn from(e: Utf8Error) -> Self {
|
||||
ErrorCode::Utf8Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromUtf8Error> for ErrorCode {
|
||||
fn from(e: FromUtf8Error) -> Self {
|
||||
ErrorCode::Utf8Error(e.utf8_error())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Utf8Error> for Error {
|
||||
fn from(e: Utf8Error) -> Self {
|
||||
Error {
|
||||
code: ErrorCode::Utf8Error(e),
|
||||
position: Position { line: 0, col: 0 },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(e: io::Error) -> Self {
|
||||
Error {
|
||||
code: ErrorCode::Io(e.to_string()),
|
||||
position: Position { line: 0, col: 0 },
|
||||
}
|
||||
}
|
||||
}
|
26
crates/ron/src/extensions.rs
Normal file
26
crates/ron/src/extensions.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Extensions: usize {
|
||||
const UNWRAP_NEWTYPES = 0x1;
|
||||
const IMPLICIT_SOME = 0x2;
|
||||
}
|
||||
}
|
||||
|
||||
impl Extensions {
|
||||
/// Creates an extension flag from an ident.
|
||||
pub fn from_ident(ident: &[u8]) -> Option<Extensions> {
|
||||
match ident {
|
||||
b"unwrap_newtypes" => Some(Extensions::UNWRAP_NEWTYPES),
|
||||
b"implicit_some" => Some(Extensions::IMPLICIT_SOME),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Extensions {
|
||||
fn default() -> Self {
|
||||
Extensions::empty()
|
||||
}
|
||||
}
|
75
crates/ron/src/lib.rs
Normal file
75
crates/ron/src/lib.rs
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*!
|
||||
RON is a simple config format which looks similar to Rust syntax.
|
||||
|
||||
## Features
|
||||
|
||||
* Data types
|
||||
* Structs, typename optional
|
||||
* Tuples
|
||||
* Enums
|
||||
* Lists
|
||||
* Maps
|
||||
* Units (`()`)
|
||||
* Optionals
|
||||
* Primitives: booleans, numbers, string, char
|
||||
* Allows nested layout (similar to JSON)
|
||||
* Supports comments
|
||||
* Trailing commas
|
||||
* Pretty serialization
|
||||
|
||||
## Syntax example
|
||||
|
||||
```rust,ignore
|
||||
Game(
|
||||
title: "Hello, RON!",
|
||||
level: Level( // We could just leave the `Level` out
|
||||
buildings: [
|
||||
(
|
||||
size: (10, 20),
|
||||
color: Yellow, // This as an enum variant
|
||||
owner: None,
|
||||
),
|
||||
(
|
||||
size: (20, 25),
|
||||
color: Custom(0.1, 0.8, 1.0),
|
||||
owner: Some("guy"),
|
||||
),
|
||||
],
|
||||
characters: {
|
||||
"guy": (
|
||||
friendly: true,
|
||||
),
|
||||
},
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Just add it to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
ron = "*"
|
||||
```
|
||||
|
||||
Serializing / Deserializing is as simple as calling `to_string` / `from_str`.
|
||||
|
||||
!*/
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/ron/0.6.0")]
|
||||
|
||||
pub mod de;
|
||||
pub mod ser;
|
||||
|
||||
pub mod error;
|
||||
pub mod value;
|
||||
|
||||
pub mod extensions;
|
||||
|
||||
pub use de::{from_str, Deserializer};
|
||||
pub use error::{Error, Result};
|
||||
pub use ser::{to_string, Serializer};
|
||||
pub use value::{Map, Number, Value};
|
||||
|
||||
mod parse;
|
871
crates/ron/src/parse.rs
Normal file
871
crates/ron/src/parse.rs
Normal file
|
@ -0,0 +1,871 @@
|
|||
use std::{
|
||||
char::from_u32 as char_from_u32,
|
||||
fmt::{Display, Formatter, Result as FmtResult},
|
||||
str::{from_utf8, from_utf8_unchecked, FromStr},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::{Error, ErrorCode, Result},
|
||||
extensions::Extensions,
|
||||
};
|
||||
|
||||
const DIGITS: &[u8] = b"0123456789ABCDEFabcdef_";
|
||||
const FLOAT_CHARS: &[u8] = b"0123456789.+-eE";
|
||||
const IDENT_FIRST: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_";
|
||||
const IDENT_CHAR: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789";
|
||||
const WHITE_SPACE: &[u8] = b"\n\t\r ";
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum AnyNum {
|
||||
F32(f32),
|
||||
F64(f64),
|
||||
I8(i8),
|
||||
U8(u8),
|
||||
I16(i16),
|
||||
U16(u16),
|
||||
I32(i32),
|
||||
U32(u32),
|
||||
I64(i64),
|
||||
U64(u64),
|
||||
I128(i128),
|
||||
U128(u128),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Bytes<'a> {
|
||||
/// Bits set according to `Extension` enum.
|
||||
pub exts: Extensions,
|
||||
bytes: &'a [u8],
|
||||
column: usize,
|
||||
line: usize,
|
||||
}
|
||||
|
||||
impl<'a> Bytes<'a> {
|
||||
pub fn new(bytes: &'a [u8]) -> Result<Self> {
|
||||
let mut b = Bytes {
|
||||
bytes,
|
||||
column: 1,
|
||||
exts: Extensions::empty(),
|
||||
line: 1,
|
||||
};
|
||||
|
||||
b.skip_ws()?;
|
||||
// Loop over all extensions attributes
|
||||
loop {
|
||||
let attribute = b.extensions()?;
|
||||
|
||||
if attribute.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
b.exts |= attribute;
|
||||
b.skip_ws()?;
|
||||
}
|
||||
|
||||
Ok(b)
|
||||
}
|
||||
|
||||
pub fn advance(&mut self, bytes: usize) -> Result<()> {
|
||||
for _ in 0..bytes {
|
||||
self.advance_single()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn advance_single(&mut self) -> Result<()> {
|
||||
if self.peek_or_eof()? == b'\n' {
|
||||
self.line += 1;
|
||||
self.column = 1;
|
||||
} else {
|
||||
self.column += 1;
|
||||
}
|
||||
|
||||
self.bytes = &self.bytes[1..];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn any_integer<T: Num>(&mut self, sign: i8) -> Result<T> {
|
||||
let base = if self.peek() == Some(b'0') {
|
||||
match self.bytes.get(1).cloned() {
|
||||
Some(b'x') => 16,
|
||||
Some(b'b') => 2,
|
||||
Some(b'o') => 8,
|
||||
_ => 10,
|
||||
}
|
||||
} else {
|
||||
10
|
||||
};
|
||||
|
||||
if base != 10 {
|
||||
// If we have `0x45A` for example,
|
||||
// cut it to `45A`.
|
||||
let _ = self.advance(2);
|
||||
}
|
||||
|
||||
let num_bytes = self.next_bytes_contained_in(DIGITS);
|
||||
|
||||
if num_bytes == 0 {
|
||||
return self.err(ErrorCode::ExpectedInteger);
|
||||
}
|
||||
|
||||
let s = unsafe { from_utf8_unchecked(&self.bytes[0..num_bytes]) };
|
||||
|
||||
if s.as_bytes()[0] == b'_' {
|
||||
return self.err(ErrorCode::UnderscoreAtBeginning);
|
||||
}
|
||||
|
||||
fn calc_num<T: Num>(
|
||||
bytes: &Bytes,
|
||||
s: &str,
|
||||
base: u8,
|
||||
mut f: impl FnMut(&mut T, u8) -> bool,
|
||||
) -> Result<T> {
|
||||
let mut num_acc = T::from_u8(0);
|
||||
|
||||
for &byte in s.as_bytes() {
|
||||
if byte == b'_' {
|
||||
continue;
|
||||
}
|
||||
|
||||
if num_acc.checked_mul_ext(base) {
|
||||
return bytes.err(ErrorCode::IntegerOutOfBounds);
|
||||
}
|
||||
|
||||
let digit = bytes.decode_hex(byte)?;
|
||||
|
||||
if digit >= base {
|
||||
return bytes.err(ErrorCode::ExpectedInteger);
|
||||
}
|
||||
|
||||
if f(&mut num_acc, digit) {
|
||||
return bytes.err(ErrorCode::IntegerOutOfBounds);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(num_acc)
|
||||
};
|
||||
|
||||
let res = if sign > 0 {
|
||||
calc_num(&*self, s, base, T::checked_add_ext)
|
||||
} else {
|
||||
calc_num(&*self, s, base, T::checked_sub_ext)
|
||||
};
|
||||
|
||||
let _ = self.advance(num_bytes);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn any_num(&mut self) -> Result<AnyNum> {
|
||||
// We are not doing float comparisons here in the traditional sense.
|
||||
// Instead, this code checks if a f64 fits inside an f32.
|
||||
#[allow(clippy::float_cmp)]
|
||||
fn any_float(f: f64) -> Result<AnyNum> {
|
||||
if f == f as f32 as f64 {
|
||||
Ok(AnyNum::F32(f as f32))
|
||||
} else {
|
||||
Ok(AnyNum::F64(f))
|
||||
}
|
||||
}
|
||||
|
||||
let bytes_backup = self.bytes;
|
||||
|
||||
let first_byte = self.peek_or_eof()?;
|
||||
let is_signed = first_byte == b'-' || first_byte == b'+';
|
||||
let is_float = self.next_bytes_is_float();
|
||||
|
||||
if is_float {
|
||||
let f = self.float::<f64>()?;
|
||||
|
||||
any_float(f)
|
||||
} else {
|
||||
let max_u8 = std::u8::MAX as u128;
|
||||
let max_u16 = std::u16::MAX as u128;
|
||||
let max_u32 = std::u32::MAX as u128;
|
||||
let max_u64 = std::u64::MAX as u128;
|
||||
|
||||
let min_i8 = std::i8::MIN as i128;
|
||||
let max_i8 = std::i8::MAX as i128;
|
||||
let min_i16 = std::i16::MIN as i128;
|
||||
let max_i16 = std::i16::MAX as i128;
|
||||
let min_i32 = std::i32::MIN as i128;
|
||||
let max_i32 = std::i32::MAX as i128;
|
||||
let min_i64 = std::i64::MIN as i128;
|
||||
let max_i64 = std::i64::MAX as i128;
|
||||
|
||||
if is_signed {
|
||||
match self.signed_integer::<i128>() {
|
||||
Ok(x) => {
|
||||
if x >= min_i8 && x <= max_i8 {
|
||||
Ok(AnyNum::I8(x as i8))
|
||||
} else if x >= min_i16 && x <= max_i16 {
|
||||
Ok(AnyNum::I16(x as i16))
|
||||
} else if x >= min_i32 && x <= max_i32 {
|
||||
Ok(AnyNum::I32(x as i32))
|
||||
} else if x >= min_i64 && x <= max_i64 {
|
||||
Ok(AnyNum::I64(x as i64))
|
||||
} else {
|
||||
Ok(AnyNum::I128(x))
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
self.bytes = bytes_backup;
|
||||
|
||||
any_float(self.float::<f64>()?)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match self.unsigned_integer::<u128>() {
|
||||
Ok(x) => {
|
||||
if x <= max_u8 {
|
||||
Ok(AnyNum::U8(x as u8))
|
||||
} else if x <= max_u16 {
|
||||
Ok(AnyNum::U16(x as u16))
|
||||
} else if x <= max_u32 {
|
||||
Ok(AnyNum::U32(x as u32))
|
||||
} else if x <= max_u64 {
|
||||
Ok(AnyNum::U64(x as u64))
|
||||
} else {
|
||||
Ok(AnyNum::U128(x))
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
self.bytes = bytes_backup;
|
||||
|
||||
any_float(self.float::<f64>()?)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bool(&mut self) -> Result<bool> {
|
||||
if self.consume("true") {
|
||||
Ok(true)
|
||||
} else if self.consume("false") {
|
||||
Ok(false)
|
||||
} else {
|
||||
self.err(ErrorCode::ExpectedBoolean)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
|
||||
pub fn char(&mut self) -> Result<char> {
|
||||
if !self.consume("'") {
|
||||
return self.err(ErrorCode::ExpectedChar);
|
||||
}
|
||||
|
||||
let c = self.peek_or_eof()?;
|
||||
|
||||
let c = if c == b'\\' {
|
||||
let _ = self.advance(1);
|
||||
|
||||
self.parse_escape()?
|
||||
} else {
|
||||
// Check where the end of the char (') is and try to
|
||||
// interpret the rest as UTF-8
|
||||
|
||||
let max = self.bytes.len().min(5);
|
||||
let pos: usize = self.bytes[..max]
|
||||
.iter()
|
||||
.position(|&x| x == b'\'')
|
||||
.ok_or_else(|| self.error(ErrorCode::ExpectedChar))?;
|
||||
let s = from_utf8(&self.bytes[0..pos]).map_err(|e| self.error(e.into()))?;
|
||||
let mut chars = s.chars();
|
||||
|
||||
let first = chars
|
||||
.next()
|
||||
.ok_or_else(|| self.error(ErrorCode::ExpectedChar))?;
|
||||
if chars.next().is_some() {
|
||||
return self.err(ErrorCode::ExpectedChar);
|
||||
}
|
||||
|
||||
let _ = self.advance(pos);
|
||||
|
||||
first
|
||||
};
|
||||
|
||||
if !self.consume("'") {
|
||||
return self.err(ErrorCode::ExpectedChar);
|
||||
}
|
||||
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
pub fn comma(&mut self) -> Result<bool> {
|
||||
self.skip_ws()?;
|
||||
|
||||
if self.consume(",") {
|
||||
self.skip_ws()?;
|
||||
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Only returns true if the char after `ident` cannot belong
|
||||
/// to an identifier.
|
||||
pub fn check_ident(&mut self, ident: &str) -> bool {
|
||||
self.test_for(ident) && !self.check_ident_char(ident.len())
|
||||
}
|
||||
|
||||
fn check_ident_char(&self, index: usize) -> bool {
|
||||
self.bytes
|
||||
.get(index)
|
||||
.map_or(false, |b| IDENT_CHAR.contains(b))
|
||||
}
|
||||
|
||||
/// Should only be used on a working copy
|
||||
pub fn check_tuple_struct(mut self) -> Result<bool> {
|
||||
if self.identifier().is_err() {
|
||||
// if there's no field ident, this is a tuple struct
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
self.skip_ws()?;
|
||||
|
||||
// if there is no colon after the ident, this can only be a unit struct
|
||||
self.eat_byte().map(|c| c != b':')
|
||||
}
|
||||
|
||||
/// Only returns true if the char after `ident` cannot belong
|
||||
/// to an identifier.
|
||||
pub fn consume_ident(&mut self, ident: &str) -> bool {
|
||||
if self.check_ident(ident) {
|
||||
let _ = self.advance(ident.len());
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn consume(&mut self, s: &str) -> bool {
|
||||
if self.test_for(s) {
|
||||
let _ = self.advance(s.len());
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_all(&mut self, all: &[&str]) -> Result<bool> {
|
||||
all.iter()
|
||||
.map(|elem| {
|
||||
if self.consume(elem) {
|
||||
self.skip_ws()?;
|
||||
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
})
|
||||
.fold(Ok(true), |acc, x| acc.and_then(|val| x.map(|x| x && val)))
|
||||
}
|
||||
|
||||
pub fn eat_byte(&mut self) -> Result<u8> {
|
||||
let peek = self.peek_or_eof()?;
|
||||
let _ = self.advance_single();
|
||||
|
||||
Ok(peek)
|
||||
}
|
||||
|
||||
pub fn err<T>(&self, kind: ErrorCode) -> Result<T> {
|
||||
Err(self.error(kind))
|
||||
}
|
||||
|
||||
pub fn error(&self, kind: ErrorCode) -> Error {
|
||||
Error {
|
||||
code: kind,
|
||||
position: Position {
|
||||
line: self.line,
|
||||
col: self.column,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_byte(&mut self, byte: u8, error: ErrorCode) -> Result<()> {
|
||||
self.eat_byte()
|
||||
.and_then(|b| if b == byte { Ok(()) } else { self.err(error) })
|
||||
}
|
||||
|
||||
/// Returns the extensions bit mask.
|
||||
fn extensions(&mut self) -> Result<Extensions> {
|
||||
if self.peek() != Some(b'#') {
|
||||
return Ok(Extensions::empty());
|
||||
}
|
||||
|
||||
if !self.consume_all(&["#", "!", "[", "enable", "("])? {
|
||||
return self.err(ErrorCode::ExpectedAttribute);
|
||||
}
|
||||
|
||||
self.skip_ws()?;
|
||||
let mut extensions = Extensions::empty();
|
||||
|
||||
loop {
|
||||
let ident = self.identifier()?;
|
||||
let extension = Extensions::from_ident(ident).ok_or_else(|| {
|
||||
self.error(ErrorCode::NoSuchExtension(
|
||||
from_utf8(ident).unwrap().to_owned(),
|
||||
))
|
||||
})?;
|
||||
|
||||
extensions |= extension;
|
||||
|
||||
let comma = self.comma()?;
|
||||
|
||||
// If we have no comma but another item, return an error
|
||||
if !comma && self.check_ident_char(0) {
|
||||
return self.err(ErrorCode::ExpectedComma);
|
||||
}
|
||||
|
||||
// If there's no comma, assume the list ended.
|
||||
// If there is, it might be a trailing one, thus we only
|
||||
// continue the loop if we get an ident char.
|
||||
if !comma || !self.check_ident_char(0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.skip_ws()?;
|
||||
|
||||
if self.consume_all(&[")", "]"])? {
|
||||
Ok(extensions)
|
||||
} else {
|
||||
Err(self.error(ErrorCode::ExpectedAttributeEnd))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn float<T>(&mut self) -> Result<T>
|
||||
where
|
||||
T: FromStr,
|
||||
{
|
||||
for literal in &["inf", "-inf", "NaN"] {
|
||||
if self.consume_ident(literal) {
|
||||
return FromStr::from_str(literal).map_err(|_| unreachable!()); // must not fail
|
||||
}
|
||||
}
|
||||
|
||||
let num_bytes = self.next_bytes_contained_in(FLOAT_CHARS);
|
||||
|
||||
let s = unsafe { from_utf8_unchecked(&self.bytes[0..num_bytes]) };
|
||||
let res = FromStr::from_str(s).map_err(|_| self.error(ErrorCode::ExpectedFloat));
|
||||
|
||||
let _ = self.advance(num_bytes);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn identifier(&mut self) -> Result<&'a [u8]> {
|
||||
let bytes = self.identifier_len()?;
|
||||
let ident = &self.bytes[..bytes];
|
||||
let _ = self.advance(bytes);
|
||||
|
||||
Ok(ident)
|
||||
}
|
||||
|
||||
pub fn identifier_len(&self) -> Result<usize> {
|
||||
let next = self.peek_or_eof()?;
|
||||
if IDENT_FIRST.contains(&next) {
|
||||
// If the next two bytes signify the start of a raw string literal,
|
||||
// return an error.
|
||||
if next == b'r' {
|
||||
let second = self
|
||||
.bytes
|
||||
.get(1)
|
||||
.ok_or_else(|| self.error(ErrorCode::Eof))?;
|
||||
if *second == b'"' || *second == b'#' {
|
||||
return self.err(ErrorCode::ExpectedIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
let bytes = self.next_bytes_contained_in(IDENT_CHAR);
|
||||
|
||||
Ok(bytes)
|
||||
} else {
|
||||
self.err(ErrorCode::ExpectedIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_bytes_contained_in(&self, allowed: &[u8]) -> usize {
|
||||
self.bytes
|
||||
.iter()
|
||||
.take_while(|b| allowed.contains(b))
|
||||
.count()
|
||||
}
|
||||
|
||||
pub fn next_bytes_is_float(&self) -> bool {
|
||||
if let Some(byte) = self.peek() {
|
||||
let skip = match byte {
|
||||
b'+' | b'-' => 1,
|
||||
_ => 0,
|
||||
};
|
||||
let flen = self
|
||||
.bytes
|
||||
.iter()
|
||||
.skip(skip)
|
||||
.take_while(|b| FLOAT_CHARS.contains(b))
|
||||
.count();
|
||||
let ilen = self
|
||||
.bytes
|
||||
.iter()
|
||||
.skip(skip)
|
||||
.take_while(|b| DIGITS.contains(b))
|
||||
.count();
|
||||
flen > ilen
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn skip_ws(&mut self) -> Result<()> {
|
||||
while self.peek().map_or(false, |c| WHITE_SPACE.contains(&c)) {
|
||||
let _ = self.advance_single();
|
||||
}
|
||||
|
||||
if self.skip_comment()? {
|
||||
self.skip_ws()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn peek(&self) -> Option<u8> {
|
||||
self.bytes.get(0).cloned()
|
||||
}
|
||||
|
||||
pub fn peek_or_eof(&self) -> Result<u8> {
|
||||
self.bytes
|
||||
.get(0)
|
||||
.cloned()
|
||||
.ok_or_else(|| self.error(ErrorCode::Eof))
|
||||
}
|
||||
|
||||
pub fn signed_integer<T>(&mut self) -> Result<T>
|
||||
where
|
||||
T: Num,
|
||||
{
|
||||
match self.peek_or_eof()? {
|
||||
b'+' => {
|
||||
let _ = self.advance_single();
|
||||
|
||||
self.any_integer(1)
|
||||
}
|
||||
b'-' => {
|
||||
let _ = self.advance_single();
|
||||
|
||||
self.any_integer(-1)
|
||||
}
|
||||
_ => self.any_integer(1),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn string(&mut self) -> Result<ParsedStr<'a>> {
|
||||
if self.consume("\"") {
|
||||
self.escaped_string()
|
||||
} else if self.consume("r") {
|
||||
self.raw_string()
|
||||
} else {
|
||||
self.err(ErrorCode::ExpectedString)
|
||||
}
|
||||
}
|
||||
|
||||
fn escaped_string(&mut self) -> Result<ParsedStr<'a>> {
|
||||
use std::iter::repeat;
|
||||
|
||||
let (i, end_or_escape) = self
|
||||
.bytes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|&(_, &b)| b == b'\\' || b == b'"')
|
||||
.ok_or_else(|| self.error(ErrorCode::ExpectedStringEnd))?;
|
||||
|
||||
if *end_or_escape == b'"' {
|
||||
let s = from_utf8(&self.bytes[..i]).map_err(|e| self.error(e.into()))?;
|
||||
|
||||
// Advance by the number of bytes of the string
|
||||
// + 1 for the `"`.
|
||||
let _ = self.advance(i + 1);
|
||||
|
||||
Ok(ParsedStr::Slice(s))
|
||||
} else {
|
||||
let mut i = i;
|
||||
let mut s: Vec<_> = self.bytes[..i].to_vec();
|
||||
|
||||
loop {
|
||||
let _ = self.advance(i + 1);
|
||||
let character = self.parse_escape()?;
|
||||
match character.len_utf8() {
|
||||
1 => s.push(character as u8),
|
||||
len => {
|
||||
let start = s.len();
|
||||
s.extend(repeat(0).take(len));
|
||||
character.encode_utf8(&mut s[start..]);
|
||||
}
|
||||
}
|
||||
|
||||
let (new_i, end_or_escape) = self
|
||||
.bytes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|&(_, &b)| b == b'\\' || b == b'"')
|
||||
.ok_or(ErrorCode::Eof)
|
||||
.map_err(|e| self.error(e))?;
|
||||
|
||||
i = new_i;
|
||||
s.extend_from_slice(&self.bytes[..i]);
|
||||
|
||||
if *end_or_escape == b'"' {
|
||||
let _ = self.advance(i + 1);
|
||||
|
||||
let s = String::from_utf8(s).map_err(|e| self.error(e.into()))?;
|
||||
break Ok(ParsedStr::Allocated(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_string(&mut self) -> Result<ParsedStr<'a>> {
|
||||
let num_hashes = self.bytes.iter().take_while(|&&b| b == b'#').count();
|
||||
let hashes = &self.bytes[..num_hashes];
|
||||
let _ = self.advance(num_hashes);
|
||||
|
||||
if !self.consume("\"") {
|
||||
return self.err(ErrorCode::ExpectedString);
|
||||
}
|
||||
|
||||
let ending = [&[b'"'], hashes].concat();
|
||||
let i = self
|
||||
.bytes
|
||||
.windows(num_hashes + 1)
|
||||
.position(|window| window == ending.as_slice())
|
||||
.ok_or_else(|| self.error(ErrorCode::ExpectedStringEnd))?;
|
||||
|
||||
let s = from_utf8(&self.bytes[..i]).map_err(|e| self.error(e.into()))?;
|
||||
|
||||
// Advance by the number of bytes of the string
|
||||
// + `num_hashes` + 1 for the `"`.
|
||||
let _ = self.advance(i + num_hashes + 1);
|
||||
|
||||
Ok(ParsedStr::Slice(s))
|
||||
}
|
||||
|
||||
fn test_for(&self, s: &str) -> bool {
|
||||
s.bytes()
|
||||
.enumerate()
|
||||
.all(|(i, b)| self.bytes.get(i).map_or(false, |t| *t == b))
|
||||
}
|
||||
|
||||
pub fn unsigned_integer<T: Num>(&mut self) -> Result<T> {
|
||||
self.any_integer(1)
|
||||
}
|
||||
|
||||
fn decode_ascii_escape(&mut self) -> Result<u8> {
|
||||
let mut n = 0;
|
||||
for _ in 0..2 {
|
||||
n <<= 4;
|
||||
let byte = self.eat_byte()?;
|
||||
let decoded = self.decode_hex(byte)?;
|
||||
n |= decoded;
|
||||
}
|
||||
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn decode_hex(&self, c: u8) -> Result<u8> {
|
||||
match c {
|
||||
c @ b'0'..=b'9' => Ok(c - b'0'),
|
||||
c @ b'a'..=b'f' => Ok(10 + c - b'a'),
|
||||
c @ b'A'..=b'F' => Ok(10 + c - b'A'),
|
||||
_ => self.err(ErrorCode::InvalidEscape("Non-hex digit found")),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_escape(&mut self) -> Result<char> {
|
||||
let c = match self.eat_byte()? {
|
||||
b'\'' => '\'',
|
||||
b'"' => '"',
|
||||
b'\\' => '\\',
|
||||
b'n' => '\n',
|
||||
b'r' => '\r',
|
||||
b't' => '\t',
|
||||
b'x' => self.decode_ascii_escape()? as char,
|
||||
b'u' => {
|
||||
self.expect_byte(b'{', ErrorCode::InvalidEscape("Missing {"))?;
|
||||
|
||||
let mut bytes: u32 = 0;
|
||||
let mut num_digits = 0;
|
||||
|
||||
while num_digits < 6 {
|
||||
let byte = self.peek_or_eof()?;
|
||||
|
||||
if byte == b'}' {
|
||||
break;
|
||||
} else {
|
||||
self.advance_single()?;
|
||||
}
|
||||
|
||||
let byte = self.decode_hex(byte)?;
|
||||
bytes <<= 4;
|
||||
bytes |= byte as u32;
|
||||
|
||||
num_digits += 1;
|
||||
}
|
||||
|
||||
if num_digits == 0 {
|
||||
return self.err(ErrorCode::InvalidEscape(
|
||||
"Expected 1-6 digits, got 0 digits",
|
||||
));
|
||||
}
|
||||
|
||||
self.expect_byte(b'}', ErrorCode::InvalidEscape("No } at the end"))?;
|
||||
char_from_u32(bytes)
|
||||
.ok_or_else(|| self.error(ErrorCode::InvalidEscape("Not a valid char")))?
|
||||
}
|
||||
_ => {
|
||||
return self.err(ErrorCode::InvalidEscape("Unknown escape character"));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
fn skip_comment(&mut self) -> Result<bool> {
|
||||
if self.consume("/") {
|
||||
match self.eat_byte()? {
|
||||
b'/' => {
|
||||
let bytes = self.bytes.iter().take_while(|&&b| b != b'\n').count();
|
||||
|
||||
let _ = self.advance(bytes);
|
||||
}
|
||||
b'*' => {
|
||||
let mut level = 1;
|
||||
|
||||
while level > 0 {
|
||||
let bytes = self
|
||||
.bytes
|
||||
.iter()
|
||||
.take_while(|&&b| b != b'/' && b != b'*')
|
||||
.count();
|
||||
|
||||
if self.bytes.is_empty() {
|
||||
return self.err(ErrorCode::UnclosedBlockComment);
|
||||
}
|
||||
|
||||
let _ = self.advance(bytes);
|
||||
|
||||
// check whether / or * and take action
|
||||
if self.consume("/*") {
|
||||
level += 1;
|
||||
} else if self.consume("*/") {
|
||||
level -= 1;
|
||||
} else {
|
||||
self.eat_byte()
|
||||
.map_err(|_| self.error(ErrorCode::UnclosedBlockComment))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
b => return self.err(ErrorCode::UnexpectedByte(b as char)),
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Num {
|
||||
fn from_u8(x: u8) -> Self;
|
||||
|
||||
/// Returns `true` on overflow
|
||||
fn checked_mul_ext(&mut self, x: u8) -> bool;
|
||||
|
||||
/// Returns `true` on overflow
|
||||
fn checked_add_ext(&mut self, x: u8) -> bool;
|
||||
|
||||
/// Returns `true` on overflow
|
||||
fn checked_sub_ext(&mut self, x: u8) -> bool;
|
||||
}
|
||||
|
||||
macro_rules! impl_num {
|
||||
($ty:ident) => {
|
||||
impl Num for $ty {
|
||||
fn from_u8(x: u8) -> Self {
|
||||
x as $ty
|
||||
}
|
||||
|
||||
fn checked_mul_ext(&mut self, x: u8) -> bool {
|
||||
match self.checked_mul(Self::from_u8(x)) {
|
||||
Some(n) => {
|
||||
*self = n;
|
||||
false
|
||||
}
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn checked_add_ext(&mut self, x: u8) -> bool {
|
||||
match self.checked_add(Self::from_u8(x)) {
|
||||
Some(n) => {
|
||||
*self = n;
|
||||
false
|
||||
}
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn checked_sub_ext(&mut self, x: u8) -> bool {
|
||||
match self.checked_sub(Self::from_u8(x)) {
|
||||
Some(n) => {
|
||||
*self = n;
|
||||
false
|
||||
}
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
($($tys:ident)*) => {
|
||||
$( impl_num!($tys); )*
|
||||
};
|
||||
}
|
||||
|
||||
impl_num!(u8 u16 u32 u64 u128 i8 i16 i32 i64 i128);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ParsedStr<'a> {
|
||||
Allocated(String),
|
||||
Slice(&'a str),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Position {
|
||||
pub col: usize,
|
||||
pub line: usize,
|
||||
}
|
||||
|
||||
impl Display for Position {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
write!(f, "{}:{}", self.line, self.col)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn decode_x10() {
|
||||
let mut bytes = Bytes::new(b"10").unwrap();
|
||||
assert_eq!(bytes.decode_ascii_escape(), Ok(0x10));
|
||||
}
|
||||
}
|
1074
crates/ron/src/ser/mod.rs
Normal file
1074
crates/ron/src/ser/mod.rs
Normal file
File diff suppressed because it is too large
Load diff
23
crates/ron/src/ser/value.rs
Normal file
23
crates/ron/src/ser/value.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use serde::ser::{Serialize, Serializer};
|
||||
|
||||
use crate::value::{Number, Value};
|
||||
|
||||
impl Serialize for Value {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match *self {
|
||||
Value::Bool(b) => serializer.serialize_bool(b),
|
||||
Value::Char(c) => serializer.serialize_char(c),
|
||||
Value::Map(ref m) => Serialize::serialize(m, serializer),
|
||||
Value::Number(Number::Float(ref f)) => serializer.serialize_f64(f.get()),
|
||||
Value::Number(Number::Integer(i)) => serializer.serialize_i64(i),
|
||||
Value::Option(Some(ref o)) => serializer.serialize_some(o.as_ref()),
|
||||
Value::Option(None) => serializer.serialize_none(),
|
||||
Value::String(ref s) => serializer.serialize_str(s),
|
||||
Value::Seq(ref s) => Serialize::serialize(s, serializer),
|
||||
Value::Unit => serializer.serialize_unit(),
|
||||
}
|
||||
}
|
||||
}
|
553
crates/ron/src/value.rs
Normal file
553
crates/ron/src/value.rs
Normal file
|
@ -0,0 +1,553 @@
|
|||
//! Value module.
|
||||
|
||||
use serde::{
|
||||
de::{
|
||||
DeserializeOwned, DeserializeSeed, Deserializer, Error as SerdeError, MapAccess, SeqAccess,
|
||||
Visitor,
|
||||
},
|
||||
forward_to_deserialize_any, Deserialize, Serialize,
|
||||
};
|
||||
use std::{
|
||||
cmp::{Eq, Ordering},
|
||||
hash::{Hash, Hasher},
|
||||
iter::FromIterator,
|
||||
ops::{Index, IndexMut},
|
||||
};
|
||||
|
||||
use crate::de::{Error as RonError, Result};
|
||||
|
||||
/// A `Value` to `Value` map.
|
||||
///
|
||||
/// This structure either uses a [BTreeMap](std::collections::BTreeMap) or the
|
||||
/// [IndexMap](indexmap::IndexMap) internally.
|
||||
/// The latter can be used by enabling the `indexmap` feature. This can be used
|
||||
/// to preserve the order of the parsed map.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub struct Map(MapInner);
|
||||
|
||||
impl Map {
|
||||
/// Creates a new, empty `Map`.
|
||||
pub fn new() -> Map {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Returns the number of elements in the map.
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
/// Returns `true` if `self.len() == 0`, `false` otherwise.
|
||||
pub fn is_empty(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
/// Inserts a new element, returning the previous element with this `key` if
|
||||
/// there was any.
|
||||
pub fn insert(&mut self, key: Value, value: Value) -> Option<Value> {
|
||||
self.0.insert(key, value)
|
||||
}
|
||||
|
||||
/// Removes an element by its `key`.
|
||||
pub fn remove(&mut self, key: &Value) -> Option<Value> {
|
||||
self.0.remove(key)
|
||||
}
|
||||
|
||||
/// Iterate all key-value pairs.
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Value, &Value)> + DoubleEndedIterator {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
/// Iterate all key-value pairs mutably.
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&Value, &mut Value)> + DoubleEndedIterator {
|
||||
self.0.iter_mut()
|
||||
}
|
||||
|
||||
/// Iterate all keys.
|
||||
pub fn keys(&self) -> impl Iterator<Item = &Value> + DoubleEndedIterator {
|
||||
self.0.keys()
|
||||
}
|
||||
|
||||
/// Iterate all values.
|
||||
pub fn values(&self) -> impl Iterator<Item = &Value> + DoubleEndedIterator {
|
||||
self.0.values()
|
||||
}
|
||||
|
||||
/// Iterate all values mutably.
|
||||
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut Value> + DoubleEndedIterator {
|
||||
self.0.values_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<(Value, Value)> for Map {
|
||||
fn from_iter<T: IntoIterator<Item = (Value, Value)>>(iter: T) -> Self {
|
||||
Map(MapInner::from_iter(iter))
|
||||
}
|
||||
}
|
||||
|
||||
/// Note: equality is only given if both values and order of values match
|
||||
impl Eq for Map {}
|
||||
|
||||
impl Hash for Map {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.iter().for_each(|x| x.hash(state));
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<&Value> for Map {
|
||||
type Output = Value;
|
||||
|
||||
fn index(&self, index: &Value) -> &Self::Output {
|
||||
&self.0[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<&Value> for Map {
|
||||
fn index_mut(&mut self, index: &Value) -> &mut Self::Output {
|
||||
self.0.get_mut(index).expect("no entry found for key")
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Map {
|
||||
fn cmp(&self, other: &Map) -> Ordering {
|
||||
self.iter().cmp(other.iter())
|
||||
}
|
||||
}
|
||||
|
||||
/// Note: equality is only given if both values and order of values match
|
||||
impl PartialEq for Map {
|
||||
fn eq(&self, other: &Map) -> bool {
|
||||
self.iter().zip(other.iter()).all(|(a, b)| a == b)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Map {
|
||||
fn partial_cmp(&self, other: &Map) -> Option<Ordering> {
|
||||
self.iter().partial_cmp(other.iter())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "indexmap"))]
|
||||
type MapInner = std::collections::BTreeMap<Value, Value>;
|
||||
#[cfg(feature = "indexmap")]
|
||||
type MapInner = indexmap::IndexMap<Value, Value>;
|
||||
|
||||
/// A wrapper for a number, which can be either `f64` or `i64`.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Ord)]
|
||||
pub enum Number {
|
||||
Integer(i64),
|
||||
Float(Float),
|
||||
}
|
||||
|
||||
/// A wrapper for `f64`, which guarantees that the inner value
|
||||
/// is finite and thus implements `Eq`, `Hash` and `Ord`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Float(f64);
|
||||
|
||||
impl Float {
|
||||
/// Construct a new `Float`.
|
||||
pub fn new(v: f64) -> Self {
|
||||
Float(v)
|
||||
}
|
||||
|
||||
/// Returns the wrapped float.
|
||||
pub fn get(self) -> f64 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Number {
|
||||
/// Construct a new number.
|
||||
pub fn new(v: impl Into<Number>) -> Self {
|
||||
v.into()
|
||||
}
|
||||
|
||||
/// Returns the `f64` representation of the number regardless of whether the number is stored
|
||||
/// as a float or integer.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use ron::value::Number;
|
||||
/// let i = Number::new(5);
|
||||
/// let f = Number::new(2.0);
|
||||
/// assert_eq!(i.into_f64(), 5.0);
|
||||
/// assert_eq!(f.into_f64(), 2.0);
|
||||
/// ```
|
||||
pub fn into_f64(self) -> f64 {
|
||||
self.map_to(|i| i as f64, |f| f)
|
||||
}
|
||||
|
||||
/// If the `Number` is a float, return it. Otherwise return `None`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use ron::value::Number;
|
||||
/// let i = Number::new(5);
|
||||
/// let f = Number::new(2.0);
|
||||
/// assert_eq!(i.as_f64(), None);
|
||||
/// assert_eq!(f.as_f64(), Some(2.0));
|
||||
/// ```
|
||||
pub fn as_f64(self) -> Option<f64> {
|
||||
self.map_to(|_| None, Some)
|
||||
}
|
||||
|
||||
/// If the `Number` is an integer, return it. Otherwise return `None`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use ron::value::Number;
|
||||
/// let i = Number::new(5);
|
||||
/// let f = Number::new(2.0);
|
||||
/// assert_eq!(i.as_i64(), Some(5));
|
||||
/// assert_eq!(f.as_i64(), None);
|
||||
/// ```
|
||||
pub fn as_i64(self) -> Option<i64> {
|
||||
self.map_to(Some, |_| None)
|
||||
}
|
||||
|
||||
/// Map this number to a single type using the appropriate closure.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use ron::value::Number;
|
||||
/// let i = Number::new(5);
|
||||
/// let f = Number::new(2.0);
|
||||
/// assert!(i.map_to(|i| i > 3, |f| f > 3.0));
|
||||
/// assert!(!f.map_to(|i| i > 3, |f| f > 3.0));
|
||||
/// ```
|
||||
pub fn map_to<T>(
|
||||
self,
|
||||
integer_fn: impl FnOnce(i64) -> T,
|
||||
float_fn: impl FnOnce(f64) -> T,
|
||||
) -> T {
|
||||
match self {
|
||||
Number::Integer(i) => integer_fn(i),
|
||||
Number::Float(Float(f)) => float_fn(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Number {
|
||||
fn from(f: f64) -> Number {
|
||||
Number::Float(Float(f))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Number {
|
||||
fn from(i: i64) -> Number {
|
||||
Number::Integer(i)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Number {
|
||||
fn from(i: i32) -> Number {
|
||||
Number::Integer(i as i64)
|
||||
}
|
||||
}
|
||||
|
||||
// The following number conversion checks if the integer fits losslessly into an i64, before
|
||||
// constructing a Number::Integer variant. If not, the conversion defaults to float.
|
||||
|
||||
impl From<u64> for Number {
|
||||
fn from(i: u64) -> Number {
|
||||
if i as i64 as u64 == i {
|
||||
Number::Integer(i as i64)
|
||||
} else {
|
||||
Number::new(i as f64)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Partial equality comparison
|
||||
/// In order to be able to use `Number` as a mapping key, NaN floating values
|
||||
/// wrapped in `Float` are equals to each other. It is not the case for
|
||||
/// underlying `f64` values itself.
|
||||
impl PartialEq for Float {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.is_nan() && other.0.is_nan() || self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Equality comparison
|
||||
/// In order to be able to use `Float` as a mapping key, NaN floating values
|
||||
/// wrapped in `Float` are equals to each other. It is not the case for
|
||||
/// underlying `f64` values itself.
|
||||
impl Eq for Float {}
|
||||
|
||||
impl Hash for Float {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u64(self.0 as u64);
|
||||
}
|
||||
}
|
||||
|
||||
/// Partial ordering comparison
|
||||
/// In order to be able to use `Number` as a mapping key, NaN floating values
|
||||
/// wrapped in `Number` are equals to each other and are less then any other
|
||||
/// floating value. It is not the case for the underlying `f64` values themselves.
|
||||
/// ```
|
||||
/// use ron::value::Number;
|
||||
/// assert!(Number::new(std::f64::NAN) < Number::new(std::f64::NEG_INFINITY));
|
||||
/// assert_eq!(Number::new(std::f64::NAN), Number::new(std::f64::NAN));
|
||||
/// ```
|
||||
impl PartialOrd for Float {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
match (self.0.is_nan(), other.0.is_nan()) {
|
||||
(true, true) => Some(Ordering::Equal),
|
||||
(true, false) => Some(Ordering::Less),
|
||||
(false, true) => Some(Ordering::Greater),
|
||||
_ => self.0.partial_cmp(&other.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Ordering comparison
|
||||
/// In order to be able to use `Float` as a mapping key, NaN floating values
|
||||
/// wrapped in `Float` are equals to each other and are less then any other
|
||||
/// floating value. It is not the case for underlying `f64` values itself. See
|
||||
/// the `PartialEq` implementation.
|
||||
impl Ord for Float {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.partial_cmp(other).expect("Bug: Contract violation")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub enum Value {
|
||||
Bool(bool),
|
||||
Char(char),
|
||||
Map(Map),
|
||||
Number(Number),
|
||||
Option(Option<Box<Value>>),
|
||||
String(String),
|
||||
Seq(Vec<Value>),
|
||||
Unit,
|
||||
}
|
||||
|
||||
impl Value {
|
||||
/// Tries to deserialize this `Value` into `T`.
|
||||
pub fn into_rust<T>(self) -> Result<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
T::deserialize(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserializer implementation for RON `Value`.
|
||||
/// This does not support enums (because `Value` doesn't store them).
|
||||
impl<'de> Deserializer<'de> for Value {
|
||||
type Error = RonError;
|
||||
|
||||
forward_to_deserialize_any! {
|
||||
bool f32 f64 char str string bytes
|
||||
byte_buf option unit unit_struct newtype_struct seq tuple
|
||||
tuple_struct map struct enum identifier ignored_any
|
||||
}
|
||||
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match self {
|
||||
Value::Bool(b) => visitor.visit_bool(b),
|
||||
Value::Char(c) => visitor.visit_char(c),
|
||||
Value::Map(m) => visitor.visit_map(MapAccessor {
|
||||
keys: m.keys().cloned().rev().collect(),
|
||||
values: m.values().cloned().rev().collect(),
|
||||
}),
|
||||
Value::Number(Number::Float(ref f)) => visitor.visit_f64(f.get()),
|
||||
Value::Number(Number::Integer(i)) => visitor.visit_i64(i),
|
||||
Value::Option(Some(o)) => visitor.visit_some(*o),
|
||||
Value::Option(None) => visitor.visit_none(),
|
||||
Value::String(s) => visitor.visit_string(s),
|
||||
Value::Seq(mut seq) => {
|
||||
seq.reverse();
|
||||
visitor.visit_seq(Seq { seq })
|
||||
}
|
||||
Value::Unit => visitor.visit_unit(),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_i64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_i64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_i64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match self {
|
||||
Value::Number(Number::Integer(i)) => visitor.visit_i64(i),
|
||||
v => Err(RonError::custom(format!("Expected a number, got {:?}", v))),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_u64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_u64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_u64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match self {
|
||||
Value::Number(Number::Integer(i)) => visitor.visit_u64(i as u64),
|
||||
v => Err(RonError::custom(format!("Expected a number, got {:?}", v))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MapAccessor {
|
||||
keys: Vec<Value>,
|
||||
values: Vec<Value>,
|
||||
}
|
||||
|
||||
impl<'de> MapAccess<'de> for MapAccessor {
|
||||
type Error = RonError;
|
||||
|
||||
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
|
||||
where
|
||||
K: DeserializeSeed<'de>,
|
||||
{
|
||||
// The `Vec` is reversed, so we can pop to get the originally first element
|
||||
self.keys
|
||||
.pop()
|
||||
.map_or(Ok(None), |v| seed.deserialize(v).map(Some))
|
||||
}
|
||||
|
||||
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
|
||||
where
|
||||
V: DeserializeSeed<'de>,
|
||||
{
|
||||
// The `Vec` is reversed, so we can pop to get the originally first element
|
||||
self.values
|
||||
.pop()
|
||||
.map(|v| seed.deserialize(v))
|
||||
.expect("Contract violation")
|
||||
}
|
||||
}
|
||||
|
||||
struct Seq {
|
||||
seq: Vec<Value>,
|
||||
}
|
||||
|
||||
impl<'de> SeqAccess<'de> for Seq {
|
||||
type Error = RonError;
|
||||
|
||||
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
||||
where
|
||||
T: DeserializeSeed<'de>,
|
||||
{
|
||||
// The `Vec` is reversed, so we can pop to get the originally first element
|
||||
self.seq
|
||||
.pop()
|
||||
.map_or(Ok(None), |v| seed.deserialize(v).map(Some))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde::Deserialize;
|
||||
use std::{collections::BTreeMap, fmt::Debug};
|
||||
|
||||
fn assert_same<'de, T>(s: &'de str)
|
||||
where
|
||||
T: Debug + Deserialize<'de> + PartialEq,
|
||||
{
|
||||
use crate::de::from_str;
|
||||
|
||||
let direct: T = from_str(s).unwrap();
|
||||
let value: Value = from_str(s).unwrap();
|
||||
let value = T::deserialize(value).unwrap();
|
||||
|
||||
assert_eq!(direct, value, "Deserialization for {:?} is not the same", s);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boolean() {
|
||||
assert_same::<bool>("true");
|
||||
assert_same::<bool>("false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float() {
|
||||
assert_same::<f64>("0.123");
|
||||
assert_same::<f64>("-4.19");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int() {
|
||||
assert_same::<u32>("626");
|
||||
assert_same::<i32>("-50");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn char() {
|
||||
assert_same::<char>("'4'");
|
||||
assert_same::<char>("'c'");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map() {
|
||||
assert_same::<BTreeMap<char, String>>(
|
||||
"{
|
||||
'a': \"Hello\",
|
||||
'b': \"Bye\",
|
||||
}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option() {
|
||||
assert_same::<Option<char>>("Some('a')");
|
||||
assert_same::<Option<char>>("None");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seq() {
|
||||
assert_same::<Vec<f64>>("[1.0, 2.0, 3.0, 4.0]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unit() {
|
||||
assert_same::<()>("()");
|
||||
}
|
||||
}
|
59
crates/ron/tests/117_untagged_tuple_variant.rs
Normal file
59
crates/ron/tests/117_untagged_tuple_variant.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use ron::{de::from_str, ser::to_string};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct BuildSystem<'m> {
|
||||
version: Cow<'m, str>,
|
||||
flags: Vec<Flag<'m>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Flag<'m> {
|
||||
Value(Cow<'m, str>),
|
||||
If(Cow<'m, str>, Vec<Cow<'m, str>>),
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ebkalderon_case() {
|
||||
let file = r#"BuildSystem(
|
||||
version: "1.0.0",
|
||||
flags: [
|
||||
"--enable-thing",
|
||||
"--enable-other-thing",
|
||||
If("some-conditional", ["--enable-third-thing"]),
|
||||
]
|
||||
)
|
||||
"#;
|
||||
|
||||
assert_eq!(
|
||||
from_str::<BuildSystem>(file).unwrap(),
|
||||
BuildSystem {
|
||||
version: "1.0.0".into(),
|
||||
flags: vec![
|
||||
Flag::Value("--enable-thing".into()),
|
||||
Flag::Value("--enable-other-thing".into()),
|
||||
Flag::If(
|
||||
"some-conditional".into(),
|
||||
vec!["--enable-third-thing".into()]
|
||||
)
|
||||
]
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(untagged)]
|
||||
enum Foo {
|
||||
Bar(usize),
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vessd_case() {
|
||||
let foo_vec = vec![Foo::Bar(0); 5];
|
||||
let foo_str = to_string(&foo_vec).unwrap();
|
||||
assert_eq!(foo_str.as_str(), "[0,0,0,0,0]");
|
||||
assert_eq!(from_str::<Vec<Foo>>(&foo_str).unwrap(), foo_vec);
|
||||
}
|
273
crates/ron/tests/123_enum_representation.rs
Normal file
273
crates/ron/tests/123_enum_representation.rs
Normal file
|
@ -0,0 +1,273 @@
|
|||
use ron::{de::from_str, ser::to_string};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{cmp::PartialEq, fmt::Debug};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
enum Inner {
|
||||
Foo,
|
||||
Bar,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
enum EnumStructExternally {
|
||||
VariantA { foo: u32, bar: u32, different: u32 },
|
||||
VariantB { foo: u32, bar: u32 },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
enum EnumStructInternally {
|
||||
VariantA { foo: u32, bar: u32, different: u32 },
|
||||
VariantB { foo: u32, bar: u32 },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", content = "content")]
|
||||
enum EnumStructAdjacently {
|
||||
VariantA {
|
||||
foo: u32,
|
||||
bar: u32,
|
||||
different: Inner,
|
||||
},
|
||||
VariantB {
|
||||
foo: u32,
|
||||
bar: u32,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum EnumStructUntagged {
|
||||
VariantA { foo: u32, bar: u32, different: u32 },
|
||||
VariantB { foo: u32, bar: u32 },
|
||||
}
|
||||
|
||||
fn test_ser<T: Serialize>(value: &T, expected: &str) {
|
||||
let actual = to_string(value).expect("Failed to serialize");
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
fn test_de<T>(s: &str, expected: T)
|
||||
where
|
||||
T: for<'a> Deserialize<'a> + Debug + PartialEq,
|
||||
{
|
||||
let actual: Result<T, _> = from_str(s);
|
||||
assert_eq!(actual, Ok(expected));
|
||||
}
|
||||
|
||||
fn test_roundtrip<T>(value: T)
|
||||
where
|
||||
T: Serialize + for<'a> Deserialize<'a> + Debug + PartialEq,
|
||||
{
|
||||
let s = to_string(&value).expect("Failed to serialize");
|
||||
let actual: Result<T, _> = from_str(&s);
|
||||
assert_eq!(actual, Ok(value));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_externally_a_ser() {
|
||||
let v = EnumStructExternally::VariantA {
|
||||
foo: 1,
|
||||
bar: 2,
|
||||
different: 3,
|
||||
};
|
||||
let e = "VariantA(foo:1,bar:2,different:3)";
|
||||
test_ser(&v, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_externally_b_ser() {
|
||||
let v = EnumStructExternally::VariantB { foo: 1, bar: 2 };
|
||||
let e = "VariantB(foo:1,bar:2)";
|
||||
test_ser(&v, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_internally_a_ser() {
|
||||
let v = EnumStructInternally::VariantA {
|
||||
foo: 1,
|
||||
bar: 2,
|
||||
different: 3,
|
||||
};
|
||||
let e = "(type:\"VariantA\",foo:1,bar:2,different:3)";
|
||||
test_ser(&v, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_internally_b_ser() {
|
||||
let v = EnumStructInternally::VariantB { foo: 1, bar: 2 };
|
||||
let e = "(type:\"VariantB\",foo:1,bar:2)";
|
||||
test_ser(&v, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adjacently_a_ser() {
|
||||
let v = EnumStructAdjacently::VariantA {
|
||||
foo: 1,
|
||||
bar: 2,
|
||||
different: Inner::Foo,
|
||||
};
|
||||
let e = "(type:\"VariantA\",content:(foo:1,bar:2,different:Foo))";
|
||||
test_ser(&v, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adjacently_b_ser() {
|
||||
let v = EnumStructAdjacently::VariantB { foo: 1, bar: 2 };
|
||||
let e = "(type:\"VariantB\",content:(foo:1,bar:2))";
|
||||
test_ser(&v, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_untagged_a_ser() {
|
||||
let v = EnumStructUntagged::VariantA {
|
||||
foo: 1,
|
||||
bar: 2,
|
||||
different: 3,
|
||||
};
|
||||
let e = "(foo:1,bar:2,different:3)";
|
||||
test_ser(&v, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_untagged_b_ser() {
|
||||
let v = EnumStructUntagged::VariantB { foo: 1, bar: 2 };
|
||||
let e = "(foo:1,bar:2)";
|
||||
test_ser(&v, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_externally_a_de() {
|
||||
let s = "VariantA(foo:1,bar:2,different:3)";
|
||||
let e = EnumStructExternally::VariantA {
|
||||
foo: 1,
|
||||
bar: 2,
|
||||
different: 3,
|
||||
};
|
||||
test_de(s, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_externally_b_de() {
|
||||
let s = "VariantB(foo:1,bar:2)";
|
||||
let e = EnumStructExternally::VariantB { foo: 1, bar: 2 };
|
||||
test_de(s, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_internally_a_de() {
|
||||
let s = "(type:\"VariantA\",foo:1,bar:2,different:3)";
|
||||
let e = EnumStructInternally::VariantA {
|
||||
foo: 1,
|
||||
bar: 2,
|
||||
different: 3,
|
||||
};
|
||||
test_de(s, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_internally_b_de() {
|
||||
let s = "(type:\"VariantB\",foo:1,bar:2)";
|
||||
let e = EnumStructInternally::VariantB { foo: 1, bar: 2 };
|
||||
test_de(s, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adjacently_a_de() {
|
||||
let s = "(type:\"VariantA\",content:(foo:1,bar:2,different:Foo))";
|
||||
let e = EnumStructAdjacently::VariantA {
|
||||
foo: 1,
|
||||
bar: 2,
|
||||
different: Inner::Foo,
|
||||
};
|
||||
test_de(s, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adjacently_b_de() {
|
||||
let s = "(type:\"VariantB\",content:(foo:1,bar:2))";
|
||||
let e = EnumStructAdjacently::VariantB { foo: 1, bar: 2 };
|
||||
test_de(s, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_untagged_a_de() {
|
||||
let s = "(foo:1,bar:2,different:3)";
|
||||
let e = EnumStructUntagged::VariantA {
|
||||
foo: 1,
|
||||
bar: 2,
|
||||
different: 3,
|
||||
};
|
||||
test_de(s, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_untagged_b_de() {
|
||||
let s = "(foo:1,bar:2)";
|
||||
let e = EnumStructUntagged::VariantB { foo: 1, bar: 2 };
|
||||
test_de(s, e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_externally_a_roundtrip() {
|
||||
let v = EnumStructExternally::VariantA {
|
||||
foo: 1,
|
||||
bar: 2,
|
||||
different: 3,
|
||||
};
|
||||
test_roundtrip(v);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_externally_b_roundtrip() {
|
||||
let v = EnumStructExternally::VariantB { foo: 1, bar: 2 };
|
||||
test_roundtrip(v);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_internally_a_roundtrip() {
|
||||
let v = EnumStructInternally::VariantA {
|
||||
foo: 1,
|
||||
bar: 2,
|
||||
different: 3,
|
||||
};
|
||||
test_roundtrip(v);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_internally_b_roundtrip() {
|
||||
let v = EnumStructInternally::VariantB { foo: 1, bar: 2 };
|
||||
test_roundtrip(v);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adjacently_a_roundtrip() {
|
||||
let v = EnumStructAdjacently::VariantA {
|
||||
foo: 1,
|
||||
bar: 2,
|
||||
different: Inner::Foo,
|
||||
};
|
||||
test_roundtrip(v);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adjacently_b_roundtrip() {
|
||||
let v = EnumStructAdjacently::VariantB { foo: 1, bar: 2 };
|
||||
test_roundtrip(v);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_untagged_a_roundtrip() {
|
||||
let v = EnumStructUntagged::VariantA {
|
||||
foo: 1,
|
||||
bar: 2,
|
||||
different: 3,
|
||||
};
|
||||
test_roundtrip(v);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_untagged_b_roundtrip() {
|
||||
let v = EnumStructUntagged::VariantB { foo: 1, bar: 2 };
|
||||
test_roundtrip(v);
|
||||
}
|
76
crates/ron/tests/129_indexmap.rs
Normal file
76
crates/ron/tests/129_indexmap.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
#[cfg(feature = "indexmap")]
|
||||
use ron::{de::from_str, Value};
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "indexmap")]
|
||||
fn test_order_preserved() {
|
||||
let file = r#"(
|
||||
tasks: {
|
||||
"debug message": Dbg(
|
||||
msg: "test message. some text after it."
|
||||
),
|
||||
"shell command": Shell(
|
||||
command: "ls",
|
||||
args: Some([
|
||||
"-l",
|
||||
"-h",
|
||||
]),
|
||||
ch_dir: Some("/"),
|
||||
),
|
||||
},
|
||||
)
|
||||
"#;
|
||||
|
||||
let value: Value = from_str(file).unwrap();
|
||||
match value {
|
||||
Value::Map(map) => match &map[&Value::String("tasks".to_owned())] {
|
||||
Value::Map(map) => {
|
||||
assert_eq!(
|
||||
*map.keys().next().unwrap(),
|
||||
Value::String("debug message".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
*map.keys().skip(1).next().unwrap(),
|
||||
Value::String("shell command".to_string())
|
||||
);
|
||||
}
|
||||
_ => panic!(),
|
||||
},
|
||||
_ => panic!(),
|
||||
}
|
||||
|
||||
let file = r#"(
|
||||
tasks: {
|
||||
"shell command": Shell(
|
||||
command: "ls",
|
||||
args: Some([
|
||||
"-l",
|
||||
"-h",
|
||||
]),
|
||||
ch_dir: Some("/")
|
||||
),
|
||||
"debug message": Dbg(
|
||||
msg: "test message. some text after it."
|
||||
),
|
||||
}
|
||||
)
|
||||
"#;
|
||||
|
||||
let value: Value = from_str(file).unwrap();
|
||||
match value {
|
||||
Value::Map(map) => match &map[&Value::String("tasks".to_owned())] {
|
||||
Value::Map(map) => {
|
||||
assert_eq!(
|
||||
*map.keys().next().unwrap(),
|
||||
Value::String("shell command".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
*map.keys().skip(1).next().unwrap(),
|
||||
Value::String("debug message".to_string())
|
||||
);
|
||||
}
|
||||
_ => panic!(),
|
||||
},
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
69
crates/ron/tests/147_empty_sets_serialisation.rs
Normal file
69
crates/ron/tests/147_empty_sets_serialisation.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
struct UnitStruct;
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
struct NewType(f32);
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
struct TupleStruct(UnitStruct, i8);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
struct Key(u32);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
enum Enum {
|
||||
Unit,
|
||||
Bool(bool),
|
||||
Chars(char, String),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
struct Struct {
|
||||
tuple: ((), NewType, TupleStruct),
|
||||
vec: Vec<Option<UnitStruct>>,
|
||||
map: HashMap<Key, Enum>,
|
||||
deep_vec: HashMap<Key, Vec<()>>,
|
||||
deep_map: HashMap<Key, HashMap<Key, Enum>>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_sets_arrays() {
|
||||
let value = Struct {
|
||||
tuple: ((), NewType(0.5), TupleStruct(UnitStruct, -5)),
|
||||
vec: vec![],
|
||||
map: vec![].into_iter().collect(),
|
||||
deep_vec: vec![(Key(0), vec![])].into_iter().collect(),
|
||||
deep_map: vec![(Key(0), vec![].into_iter().collect())]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let pretty = ron::ser::PrettyConfig::new()
|
||||
.with_enumerate_arrays(true)
|
||||
.with_new_line("\n".to_string());
|
||||
let serial = ron::ser::to_string_pretty(&value, pretty).unwrap();
|
||||
|
||||
println!("Serialized: {}", serial);
|
||||
|
||||
assert_eq!(
|
||||
"(
|
||||
tuple: ((), (0.5), ((), -5)),
|
||||
vec: [],
|
||||
map: {},
|
||||
deep_vec: {
|
||||
(0): [],
|
||||
},
|
||||
deep_map: {
|
||||
(0): {},
|
||||
},
|
||||
)",
|
||||
serial
|
||||
);
|
||||
|
||||
let deserial = ron::de::from_str(&serial);
|
||||
|
||||
assert_eq!(Ok(value), deserial);
|
||||
}
|
79
crates/ron/tests/big_struct.rs
Normal file
79
crates/ron/tests/big_struct.rs
Normal file
|
@ -0,0 +1,79 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ImVec2 {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ImColorsSave {
|
||||
pub text: f32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ImGuiStyleSave {
|
||||
pub alpha: f32,
|
||||
pub window_padding: ImVec2,
|
||||
pub window_min_size: ImVec2,
|
||||
pub window_rounding: f32,
|
||||
pub window_title_align: ImVec2,
|
||||
pub child_window_rounding: f32,
|
||||
pub frame_padding: ImVec2,
|
||||
pub frame_rounding: f32,
|
||||
pub item_spacing: ImVec2,
|
||||
pub item_inner_spacing: ImVec2,
|
||||
pub touch_extra_padding: ImVec2,
|
||||
pub indent_spacing: f32,
|
||||
pub columns_min_spacing: f32,
|
||||
pub scrollbar_size: f32,
|
||||
pub scrollbar_rounding: f32,
|
||||
pub grab_min_size: f32,
|
||||
pub grab_rounding: f32,
|
||||
pub button_text_align: ImVec2,
|
||||
pub display_window_padding: ImVec2,
|
||||
pub display_safe_area_padding: ImVec2,
|
||||
pub anti_aliased_lines: bool,
|
||||
pub anti_aliased_shapes: bool,
|
||||
pub curve_tessellation_tol: f32,
|
||||
pub colors: ImColorsSave,
|
||||
pub new_type: NewType,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct NewType(i32);
|
||||
|
||||
const CONFIG: &str = "(
|
||||
alpha: 1.0,
|
||||
window_padding: (x: 8, y: 8),
|
||||
window_min_size: (x: 32, y: 32),
|
||||
window_rounding: 9.0,
|
||||
window_title_align: (x: 0.0, y: 0.5),
|
||||
child_window_rounding: 0.0,
|
||||
frame_padding: (x: 4, y: 3),
|
||||
frame_rounding: 0.0,
|
||||
item_spacing: (x: 8, y: 4),
|
||||
item_inner_spacing: (x: 4, y: 4),
|
||||
touch_extra_padding: (x: 0, y: 0),
|
||||
indent_spacing: 21.0,
|
||||
columns_min_spacing: 6.0,
|
||||
scrollbar_size: 16,
|
||||
scrollbar_rounding: 9,
|
||||
grab_min_size: 10,
|
||||
grab_rounding: 0,
|
||||
button_text_align: (x: 0.5, y: 0.5),
|
||||
display_window_padding: (x: 22, y: 22),
|
||||
display_safe_area_padding: (x: 4, y: 4),
|
||||
anti_aliased_lines: true,
|
||||
anti_aliased_shapes: true,
|
||||
curve_tessellation_tol: 1.25,
|
||||
colors: (text: 4),
|
||||
new_type: NewType( 1 ),
|
||||
|
||||
ignored_field: \"Totally ignored, not causing a panic. Hopefully.\",
|
||||
)";
|
||||
|
||||
#[test]
|
||||
fn deserialize_big_struct() {
|
||||
ron::de::from_str::<ImGuiStyleSave>(CONFIG).unwrap();
|
||||
}
|
16
crates/ron/tests/borrowed_str.rs
Normal file
16
crates/ron/tests/borrowed_str.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
struct Borrowed<'a> {
|
||||
value: &'a str,
|
||||
}
|
||||
|
||||
const BORROWED: &str = "Borrowed(value: \"test\")";
|
||||
|
||||
#[test]
|
||||
fn borrowed_str() {
|
||||
assert_eq!(
|
||||
ron::de::from_str(BORROWED).ok(),
|
||||
Some(Borrowed { value: "test" })
|
||||
);
|
||||
}
|
52
crates/ron/tests/comments.rs
Normal file
52
crates/ron/tests/comments.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
use ron::de::{from_str, Error as RonErr, ErrorCode, Position};
|
||||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
assert_eq!(
|
||||
from_str(
|
||||
"/*
|
||||
* We got a hexadecimal number here!
|
||||
*
|
||||
*/0x507"
|
||||
),
|
||||
Ok(0x507)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested() {
|
||||
assert_eq!(
|
||||
from_str(
|
||||
"/*
|
||||
/* quite * some * nesting * going * on * /* here /* (yeah, maybe a bit too much) */ */ */
|
||||
*/
|
||||
// The actual value comes.. /*
|
||||
// very soon, these are just checks that */
|
||||
// multi-line comments don't trigger in line comments /*
|
||||
\"THE VALUE\" /* This is the value /* :) */ */
|
||||
"
|
||||
),
|
||||
Ok("THE VALUE".to_owned())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unclosed() {
|
||||
assert_eq!(
|
||||
from_str::<String>(
|
||||
"/*
|
||||
/* quite * some * nesting * going * on * /* here /* (yeah, maybe a bit too much) */ */ */
|
||||
*/
|
||||
// The actual value comes.. /*
|
||||
// very soon, these are just checks that */
|
||||
// multi-line comments don't trigger in line comments /*
|
||||
/* Unfortunately, this comment won't get closed :(
|
||||
\"THE VALUE (which is invalid)\"
|
||||
"
|
||||
),
|
||||
Err(RonErr {
|
||||
code: ErrorCode::UnclosedBlockComment,
|
||||
position: Position { col: 1, line: 9 }
|
||||
})
|
||||
);
|
||||
}
|
59
crates/ron/tests/depth_limit.rs
Normal file
59
crates/ron/tests/depth_limit.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Config {
|
||||
float: (f32, f64),
|
||||
tuple: TupleStruct,
|
||||
map: HashMap<u8, char>,
|
||||
nested: Nested,
|
||||
var: Variant,
|
||||
array: Vec<()>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct TupleStruct((), bool);
|
||||
|
||||
#[derive(Serialize)]
|
||||
enum Variant {
|
||||
A(u8, &'static str),
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Nested {
|
||||
a: String,
|
||||
b: char,
|
||||
}
|
||||
|
||||
const EXPECTED: &str = "(
|
||||
float: (2.18,-1.1),
|
||||
tuple: ((),false),
|
||||
map: {8:'1'},
|
||||
nested: (a:\"a\",b:'b'),
|
||||
var: A(255,\"\"),
|
||||
array: [(),(),()],
|
||||
)";
|
||||
|
||||
#[test]
|
||||
fn depth_limit() {
|
||||
let data = Config {
|
||||
float: (2.18, -1.1),
|
||||
tuple: TupleStruct((), false),
|
||||
map: vec![(8, '1')].into_iter().collect(),
|
||||
nested: Nested {
|
||||
a: "a".to_owned(),
|
||||
b: 'b',
|
||||
},
|
||||
var: Variant::A(!0, ""),
|
||||
array: vec![(); 3],
|
||||
};
|
||||
|
||||
let pretty = ron::ser::PrettyConfig::new()
|
||||
.with_depth_limit(1)
|
||||
.with_separate_tuple_members(true)
|
||||
.with_enumerate_arrays(true)
|
||||
.with_new_line("\n".to_string());
|
||||
let s = ron::ser::to_string_pretty(&data, pretty);
|
||||
|
||||
assert_eq!(s, Ok(EXPECTED.to_string()));
|
||||
}
|
67
crates/ron/tests/escape.rs
Normal file
67
crates/ron/tests/escape.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
use ron::{de::from_str, ser::to_string};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{char::from_u32, fmt::Debug};
|
||||
|
||||
#[test]
|
||||
fn test_escape_basic() {
|
||||
assert_eq!(to_string(&"\x07").unwrap(), "\"\\u{7}\"");
|
||||
|
||||
assert_eq!(from_str::<String>("\"\\x07\"").unwrap(), "\x07");
|
||||
assert_eq!(from_str::<String>("\"\\u{7}\"").unwrap(), "\x07");
|
||||
}
|
||||
|
||||
fn check_same<T>(t: T)
|
||||
where
|
||||
T: Debug + for<'a> Deserialize<'a> + PartialEq + Serialize,
|
||||
{
|
||||
let s: String = to_string(&t).unwrap();
|
||||
|
||||
println!("Serialized: \n\n{}\n\n", s);
|
||||
|
||||
assert_eq!(from_str(&s), Ok(t));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ascii_10() {
|
||||
check_same("\u{10}".to_owned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ascii_chars() {
|
||||
(1..128).into_iter().flat_map(from_u32).for_each(check_same)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ascii_string() {
|
||||
let s: String = (1..128).into_iter().flat_map(from_u32).collect();
|
||||
|
||||
check_same(s);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_non_ascii() {
|
||||
assert_eq!(to_string(&"♠").unwrap(), "\"♠\"");
|
||||
assert_eq!(to_string(&"ß").unwrap(), "\"ß\"");
|
||||
assert_eq!(to_string(&"ä").unwrap(), "\"ä\"");
|
||||
assert_eq!(to_string(&"ö").unwrap(), "\"ö\"");
|
||||
assert_eq!(to_string(&"ü").unwrap(), "\"ü\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chars() {
|
||||
assert_eq!(to_string(&'♠').unwrap(), "'♠'");
|
||||
assert_eq!(to_string(&'ß').unwrap(), "'ß'");
|
||||
assert_eq!(to_string(&'ä').unwrap(), "'ä'");
|
||||
assert_eq!(to_string(&'ö').unwrap(), "'ö'");
|
||||
assert_eq!(to_string(&'ü').unwrap(), "'ü'");
|
||||
assert_eq!(to_string(&'\u{715}').unwrap(), "'\u{715}'");
|
||||
assert_eq!(
|
||||
from_str::<char>("'\u{715}'").unwrap(),
|
||||
from_str("'\\u{715}'").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nul_in_string() {
|
||||
check_same("Hello\0World!".to_owned());
|
||||
}
|
81
crates/ron/tests/extensions.rs
Normal file
81
crates/ron/tests/extensions.rs
Normal file
|
@ -0,0 +1,81 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
struct UnitStruct;
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
struct NewType(f32);
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
struct TupleStruct(UnitStruct, i8);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
struct Key(u32);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
enum Enum {
|
||||
Unit,
|
||||
Bool(bool),
|
||||
Chars(char, String),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
struct Struct {
|
||||
tuple: ((), NewType, TupleStruct),
|
||||
vec: Vec<Option<UnitStruct>>,
|
||||
map: HashMap<Key, Enum>,
|
||||
}
|
||||
|
||||
const CONFIG_U_NT: &str = "
|
||||
#![enable(unwrap_newtypes)]
|
||||
|
||||
(
|
||||
tuple: ((), 0.5, ((), -5)),
|
||||
vec: [
|
||||
None,
|
||||
Some(()),
|
||||
],
|
||||
map: {
|
||||
7: Bool(true),
|
||||
9: Chars('x', \"\"),
|
||||
6: Bool(false),
|
||||
5: Unit,
|
||||
},
|
||||
)
|
||||
";
|
||||
|
||||
#[test]
|
||||
fn unwrap_newtypes() {
|
||||
let d: Struct = ron::de::from_str(&CONFIG_U_NT).expect("Failed to deserialize");
|
||||
|
||||
println!("unwrap_newtypes: {:#?}", d);
|
||||
}
|
||||
|
||||
const CONFIG_I_S: &str = "
|
||||
#![enable(implicit_some)]
|
||||
|
||||
(
|
||||
tuple: ((), (0.5), ((), -5)),
|
||||
vec: [
|
||||
None,
|
||||
(),
|
||||
UnitStruct,
|
||||
None,
|
||||
(),
|
||||
],
|
||||
map: {
|
||||
(7): Bool(true),
|
||||
(9): Chars('x', \"\"),
|
||||
(6): Bool(false),
|
||||
(5): Unit,
|
||||
},
|
||||
)
|
||||
";
|
||||
|
||||
#[test]
|
||||
fn implicit_some() {
|
||||
let d: Struct = ron::de::from_str(&CONFIG_I_S).expect("Failed to deserialize");
|
||||
|
||||
println!("implicit_some: {:#?}", d);
|
||||
}
|
8
crates/ron/tests/floats.rs
Normal file
8
crates/ron/tests/floats.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use ron::de::from_str;
|
||||
|
||||
#[test]
|
||||
fn test_inf_and_nan() {
|
||||
assert_eq!(from_str("inf"), Ok(std::f64::INFINITY));
|
||||
assert_eq!(from_str("-inf"), Ok(std::f64::NEG_INFINITY));
|
||||
assert_eq!(from_str::<f64>("NaN").map(|n| n.is_nan()), Ok(true))
|
||||
}
|
14
crates/ron/tests/large_number.rs
Normal file
14
crates/ron/tests/large_number.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use ron::value::Number;
|
||||
|
||||
#[test]
|
||||
fn test_large_number() {
|
||||
use ron::value::Value;
|
||||
let test_var = Value::Number(Number::new(10000000000000000000000.0f64));
|
||||
let test_ser = ron::ser::to_string(&test_var).unwrap();
|
||||
let test_deser = ron::de::from_str::<Value>(&test_ser);
|
||||
|
||||
assert_eq!(
|
||||
test_deser.unwrap(),
|
||||
Value::Number(Number::new(10000000000000000000000.0))
|
||||
);
|
||||
}
|
65
crates/ron/tests/min_max.rs
Normal file
65
crates/ron/tests/min_max.rs
Normal file
|
@ -0,0 +1,65 @@
|
|||
use ron::{de::*, ser::*};
|
||||
|
||||
#[test]
|
||||
fn test_i32_min() {
|
||||
assert_eq!(
|
||||
std::i32::MIN,
|
||||
from_str(&to_string(&std::i32::MIN).unwrap()).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i32_max() {
|
||||
assert_eq!(
|
||||
std::i32::MAX,
|
||||
from_str(&to_string(&std::i32::MAX).unwrap()).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i64_min() {
|
||||
assert_eq!(
|
||||
std::i64::MIN,
|
||||
from_str(&to_string(&std::i64::MIN).unwrap()).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i64_max() {
|
||||
assert_eq!(
|
||||
std::i64::MAX,
|
||||
from_str(&to_string(&std::i64::MAX).unwrap()).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i128_min() {
|
||||
assert_eq!(
|
||||
std::i128::MIN,
|
||||
from_str(&to_string(&std::i128::MIN).unwrap()).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i128_max() {
|
||||
assert_eq!(
|
||||
std::i128::MAX,
|
||||
from_str(&to_string(&std::i128::MAX).unwrap()).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_u128_min() {
|
||||
assert_eq!(
|
||||
std::u128::MIN,
|
||||
from_str(&to_string(&std::u128::MIN).unwrap()).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_u128_max() {
|
||||
assert_eq!(
|
||||
std::u128::MAX,
|
||||
from_str(&to_string(&std::u128::MAX).unwrap()).unwrap()
|
||||
);
|
||||
}
|
22
crates/ron/tests/numbers.rs
Normal file
22
crates/ron/tests/numbers.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
use ron::de::from_str;
|
||||
|
||||
#[test]
|
||||
fn test_hex() {
|
||||
assert_eq!(from_str("0x507"), Ok(0x507));
|
||||
assert_eq!(from_str("0x1A5"), Ok(0x1A5));
|
||||
assert_eq!(from_str("0x53C537"), Ok(0x53C537));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bin() {
|
||||
assert_eq!(from_str("0b101"), Ok(0b101));
|
||||
assert_eq!(from_str("0b001"), Ok(0b001));
|
||||
assert_eq!(from_str("0b100100"), Ok(0b100100));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_oct() {
|
||||
assert_eq!(from_str("0o1461"), Ok(0o1461));
|
||||
assert_eq!(from_str("0o051"), Ok(0o051));
|
||||
assert_eq!(from_str("0o150700"), Ok(0o150700));
|
||||
}
|
53
crates/ron/tests/preserve_sequence.rs
Normal file
53
crates/ron/tests/preserve_sequence.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
use ron::{
|
||||
de::from_str,
|
||||
ser::{to_string_pretty, PrettyConfig},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct Config {
|
||||
boolean: bool,
|
||||
float: f32,
|
||||
map: BTreeMap<u8, char>,
|
||||
nested: Nested,
|
||||
tuple: (u32, u32),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct Nested {
|
||||
a: String,
|
||||
b: char,
|
||||
}
|
||||
|
||||
fn read_original(source: &str) -> String {
|
||||
source.to_string().replace("\r\n", "\n")
|
||||
}
|
||||
|
||||
fn make_roundtrip(source: &str) -> String {
|
||||
let config: Config = match from_str(source) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
println!("Failed to load config: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
let pretty = PrettyConfig::new()
|
||||
.with_depth_limit(3)
|
||||
.with_separate_tuple_members(true)
|
||||
.with_enumerate_arrays(true)
|
||||
.with_new_line("\n".into());
|
||||
to_string_pretty(&config, pretty).expect("Serialization failed")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sequence_ex1() {
|
||||
let file = include_str!("preserve_sequence_ex1.ron");
|
||||
assert_eq!(read_original(file), make_roundtrip(file));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sequence_ex2() {
|
||||
let file = include_str!("preserve_sequence_ex2.ron");
|
||||
assert_eq!(read_original(file), make_roundtrip(file));
|
||||
}
|
20
crates/ron/tests/preserve_sequence_ex1.ron
Normal file
20
crates/ron/tests/preserve_sequence_ex1.ron
Normal file
|
@ -0,0 +1,20 @@
|
|||
(
|
||||
boolean: true,
|
||||
float: 8.2,
|
||||
map: {
|
||||
1: '1',
|
||||
2: '4',
|
||||
3: '9',
|
||||
4: '1',
|
||||
5: '2',
|
||||
6: '3',
|
||||
},
|
||||
nested: (
|
||||
a: "Decode me!",
|
||||
b: 'z',
|
||||
),
|
||||
tuple: (
|
||||
3,
|
||||
7,
|
||||
),
|
||||
)
|
15
crates/ron/tests/preserve_sequence_ex2.ron
Normal file
15
crates/ron/tests/preserve_sequence_ex2.ron
Normal file
|
@ -0,0 +1,15 @@
|
|||
(
|
||||
boolean: true,
|
||||
float: 8.2,
|
||||
map: {
|
||||
1: '1',
|
||||
},
|
||||
nested: (
|
||||
a: "Decode me!",
|
||||
b: 'z',
|
||||
),
|
||||
tuple: (
|
||||
3,
|
||||
7,
|
||||
),
|
||||
)
|
121
crates/ron/tests/roundtrip.rs
Normal file
121
crates/ron/tests/roundtrip.rs
Normal file
|
@ -0,0 +1,121 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ron::extensions::Extensions;
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
struct UnitStruct;
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
struct NewType(f32);
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
struct TupleStruct(UnitStruct, i8);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
struct Key(u32);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
enum Enum {
|
||||
Unit,
|
||||
Bool(bool),
|
||||
Chars(char, String),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
struct Struct {
|
||||
tuple: ((), NewType, TupleStruct),
|
||||
vec: Vec<Option<UnitStruct>>,
|
||||
map: HashMap<Key, Enum>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip() {
|
||||
let value = Struct {
|
||||
tuple: ((), NewType(0.5), TupleStruct(UnitStruct, -5)),
|
||||
vec: vec![None, Some(UnitStruct)],
|
||||
map: vec![
|
||||
(Key(5), Enum::Unit),
|
||||
(Key(6), Enum::Bool(false)),
|
||||
(Key(7), Enum::Bool(true)),
|
||||
(Key(9), Enum::Chars('x', "".to_string())),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let serial = ron::ser::to_string(&value).unwrap();
|
||||
|
||||
println!("Serialized: {}", serial);
|
||||
|
||||
let deserial = ron::de::from_str(&serial);
|
||||
|
||||
assert_eq!(Ok(value), deserial);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip_pretty() {
|
||||
let value = Struct {
|
||||
tuple: ((), NewType(0.5), TupleStruct(UnitStruct, -5)),
|
||||
vec: vec![None, Some(UnitStruct)],
|
||||
map: vec![
|
||||
(Key(5), Enum::Unit),
|
||||
(Key(6), Enum::Bool(false)),
|
||||
(Key(7), Enum::Bool(true)),
|
||||
(Key(9), Enum::Chars('x', "".to_string())),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let pretty = ron::ser::PrettyConfig::new()
|
||||
.with_enumerate_arrays(true)
|
||||
.with_extensions(Extensions::IMPLICIT_SOME);
|
||||
let serial = ron::ser::to_string_pretty(&value, pretty).unwrap();
|
||||
|
||||
println!("Serialized: {}", serial);
|
||||
|
||||
let deserial = ron::de::from_str(&serial);
|
||||
|
||||
assert_eq!(Ok(value), deserial);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip_sep_tuple_members() {
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub enum FileOrMem {
|
||||
File(String),
|
||||
Memory,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
struct Both {
|
||||
a: Struct,
|
||||
b: FileOrMem,
|
||||
}
|
||||
|
||||
let a = Struct {
|
||||
tuple: ((), NewType(0.5), TupleStruct(UnitStruct, -5)),
|
||||
vec: vec![None, Some(UnitStruct)],
|
||||
map: vec![
|
||||
(Key(5), Enum::Unit),
|
||||
(Key(6), Enum::Bool(false)),
|
||||
(Key(7), Enum::Bool(true)),
|
||||
(Key(9), Enum::Chars('x', "".to_string())),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
};
|
||||
let b = FileOrMem::File("foo".to_owned());
|
||||
|
||||
let value = Both { a, b };
|
||||
|
||||
let pretty = ron::ser::PrettyConfig::new().with_separate_tuple_members(true);
|
||||
let serial = ron::ser::to_string_pretty(&value, pretty).unwrap();
|
||||
|
||||
println!("Serialized: {}", serial);
|
||||
|
||||
let deserial = ron::de::from_str(&serial);
|
||||
|
||||
assert_eq!(Ok(value), deserial);
|
||||
}
|
35
crates/ron/tests/struct_integers.rs
Normal file
35
crates/ron/tests/struct_integers.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
struct S {
|
||||
a: i8,
|
||||
b: i16,
|
||||
c: i32,
|
||||
d: i64,
|
||||
e: i128,
|
||||
f: u8,
|
||||
g: u16,
|
||||
h: u32,
|
||||
i: u64,
|
||||
j: u128,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip() {
|
||||
let s = S {
|
||||
a: std::i8::MIN,
|
||||
b: std::i16::MIN,
|
||||
c: std::i32::MIN,
|
||||
d: std::i64::MIN,
|
||||
e: std::i128::MIN,
|
||||
f: std::u8::MAX,
|
||||
g: std::u16::MAX,
|
||||
h: std::u32::MAX,
|
||||
i: std::u64::MAX,
|
||||
j: std::u128::MAX,
|
||||
};
|
||||
let serialized = ron::ser::to_string(&s).unwrap();
|
||||
dbg!(&serialized);
|
||||
let deserialized = ron::de::from_str(&serialized).unwrap();
|
||||
assert_eq!(s, deserialized,);
|
||||
}
|
13
crates/ron/tests/unicode.rs
Normal file
13
crates/ron/tests/unicode.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use ron::de::from_str;
|
||||
|
||||
#[test]
|
||||
fn test_char() {
|
||||
let de: char = from_str("'Փ'").unwrap();
|
||||
assert_eq!(de, 'Փ');
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string() {
|
||||
let de: String = from_str("\"My string: ऄ\"").unwrap();
|
||||
assert_eq!(de, "My string: ऄ");
|
||||
}
|
111
crates/ron/tests/value.rs
Normal file
111
crates/ron/tests/value.rs
Normal file
|
@ -0,0 +1,111 @@
|
|||
use ron::value::{Map, Number, Value};
|
||||
use serde::Serialize;
|
||||
|
||||
#[test]
|
||||
fn bool() {
|
||||
assert_eq!("true".parse(), Ok(Value::Bool(true)));
|
||||
assert_eq!("false".parse(), Ok(Value::Bool(false)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn char() {
|
||||
assert_eq!("'a'".parse(), Ok(Value::Char('a')));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map() {
|
||||
let mut map = Map::new();
|
||||
map.insert(Value::Char('a'), Value::Number(Number::new(1)));
|
||||
map.insert(Value::Char('b'), Value::Number(Number::new(2f64)));
|
||||
assert_eq!("{ 'a': 1, 'b': 2.0 }".parse(), Ok(Value::Map(map)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number() {
|
||||
assert_eq!("42".parse(), Ok(Value::Number(Number::new(42))));
|
||||
assert_eq!("3.1415".parse(), Ok(Value::Number(Number::new(3.1415f64))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option() {
|
||||
let opt = Some(Box::new(Value::Char('c')));
|
||||
assert_eq!("Some('c')".parse(), Ok(Value::Option(opt)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string() {
|
||||
let normal = "\"String\"";
|
||||
assert_eq!(normal.parse(), Ok(Value::String("String".into())));
|
||||
|
||||
let raw = "r\"Raw String\"";
|
||||
assert_eq!(raw.parse(), Ok(Value::String("Raw String".into())));
|
||||
|
||||
let raw_hashes = "r#\"Raw String\"#";
|
||||
assert_eq!(raw_hashes.parse(), Ok(Value::String("Raw String".into())));
|
||||
|
||||
let raw_escaped = "r##\"Contains \"#\"##";
|
||||
assert_eq!(
|
||||
raw_escaped.parse(),
|
||||
Ok(Value::String("Contains \"#".into()))
|
||||
);
|
||||
|
||||
let raw_multi_line = "r\"Multi\nLine\"";
|
||||
assert_eq!(
|
||||
raw_multi_line.parse(),
|
||||
Ok(Value::String("Multi\nLine".into()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seq() {
|
||||
let seq = vec![
|
||||
Value::Number(Number::new(1)),
|
||||
Value::Number(Number::new(2f64)),
|
||||
];
|
||||
assert_eq!("[1, 2.0]".parse(), Ok(Value::Seq(seq)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unit() {
|
||||
use ron::error::{Error, ErrorCode, Position};
|
||||
|
||||
assert_eq!("()".parse(), Ok(Value::Unit));
|
||||
assert_eq!("Foo".parse(), Ok(Value::Unit));
|
||||
|
||||
assert_eq!(
|
||||
"".parse::<Value>(),
|
||||
Err(Error {
|
||||
code: ErrorCode::Eof,
|
||||
position: Position { col: 1, line: 1 }
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Scene(Option<(u32, u32)>);
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Scene2 {
|
||||
foo: Option<(u32, u32)>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip() {
|
||||
use ron::{de::from_str, ser::to_string};
|
||||
|
||||
{
|
||||
let s = to_string(&Scene2 {
|
||||
foo: Some((122, 13)),
|
||||
})
|
||||
.unwrap();
|
||||
println!("{}", s);
|
||||
let scene: Value = from_str(&s).unwrap();
|
||||
println!("{:?}", scene);
|
||||
}
|
||||
{
|
||||
let s = to_string(&Scene(Some((13, 122)))).unwrap();
|
||||
println!("{}", s);
|
||||
let scene: Value = from_str(&s).unwrap();
|
||||
println!("{:?}", scene);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ use bevy::{
|
|||
property::SerializableProperties,
|
||||
scene::{DynamicScene, SceneEntity},
|
||||
};
|
||||
use serde::ser::Serialize;
|
||||
use serde::{de::Deserialize, ser::Serialize};
|
||||
|
||||
fn main() {
|
||||
App::build()
|
||||
|
@ -44,10 +44,13 @@ fn setup() {
|
|||
assert_eq!(test.a, 3);
|
||||
|
||||
let ser = SerializableProperties { props: &test };
|
||||
let pretty_config = ron::ser::PrettyConfig::default().with_decimal_floats(true);
|
||||
|
||||
let mut serializer = ron::ser::Serializer::new(Some(ron::ser::PrettyConfig::default()), false);
|
||||
let mut buf = Vec::new();
|
||||
let mut serializer =
|
||||
ron::ser::Serializer::new(&mut buf, Some(pretty_config.clone()), false).unwrap();
|
||||
ser.serialize(&mut serializer).unwrap();
|
||||
let ron_string = serializer.into_output_string();
|
||||
let ron_string = String::from_utf8(buf).unwrap();
|
||||
println!("{}", ron_string);
|
||||
|
||||
// let dynamic_scene = DynamicScene {
|
||||
|
@ -62,4 +65,11 @@ fn setup() {
|
|||
// println!("{}", serializer.into_output_string());
|
||||
|
||||
let mut deserializer = ron::de::Deserializer::from_str(&ron_string).unwrap();
|
||||
let dynamic_properties = DynamicProperties::deserialize(&mut deserializer).unwrap();
|
||||
let mut buf = Vec::new();
|
||||
let mut serializer = ron::ser::Serializer::new(&mut buf, Some(pretty_config), false).unwrap();
|
||||
dynamic_properties.serialize(&mut serializer).unwrap();
|
||||
let round_tripped = String::from_utf8(buf).unwrap();
|
||||
println!();
|
||||
println!("{}", round_tripped);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue