зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1433139 - Enable the WR capture feature in webrender_bindings. r=kats
MozReview-Commit-ID: 2dJMVMto5Ly
This commit is contained in:
Родитель
527781fae8
Коммит
a8e2dce594
|
@ -16,6 +16,7 @@ log = "0.3"
|
|||
path = "../webrender"
|
||||
version = "0.56.1"
|
||||
default-features = false
|
||||
features = ["capture"]
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
dwrote = "0.4.1"
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"files":{"Cargo.toml":"3084630437ca5b2ff223cc0aa4e6329250ccc599723cf0711c89979165a52721","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"fa0598d520497731445a5bc89a2b3d47896d95568bd2be255b7bc43d771b9994","README.md":"87cac8cd8c6d9ceb66802f9c810df6583762942567eff7baf71d4ad700df93ec","examples/decode.rs":"70e1eacea082e17dd57ce731c6963c8166aa161445857c1032819d87d89ce067","examples/decode_file.rs":"d046edcee9339855e3fca0d81cb75bf227a0f12a836faa46ee5420f78df14e51","examples/encode.rs":"502f79332a47950bcc172295e50665ff87d57dc68707d497a4dd9ccb5fcd298f","examples/example.ron":"c3355fcb6ee48f32da62ff56040dc69a69730c24f55e7744e235a28aa66a560e","examples/transcode.rs":"5cae0549acf44d522a1f93d41c72901bc52f640a393b55b25bfc735a3395dff4","highlight/RON.sublime-syntax":"3b44ac654381f86a48ecd294a116d645afc8b1b285ac54a98fdfe0ef44922ad8","src/de/error.rs":"354213757a5edadd2bb1cfb524aff4ab519abe126329372947cb5f1cd9042ce9","src/de/id.rs":"52f7890e13c451d3614e14cf1e41ff0006906af57c7a6d0151bfdf1431c6dd7d","src/de/mod.rs":"08b27a38cf0534f8b5831af3913570b1013663bb3d1a1e1dd8ab19d61e251d91","src/de/tests.rs":"319f4a0c04d351d1fc55b92a7b1f08462ac62760258d64a10f098b65cc7cb452","src/de/value.rs":"6195d0ed6db65c7c2c9cdff40502da3514f0510e59f2ad0c0ad0ad8c3bb5b4ff","src/lib.rs":"558446d7acc431ee04bbb1f54e31cd83a5457d04c0e5619dc2cbf47ec97b28d8","src/parse.rs":"168f02b2e3865cb4448c5b9991a65765a9c9c78b7edb1aa238b329715289200a","src/ser/mod.rs":"0b837bceb1f3c4bda47f6217f7abb07c8a39db26f91000adeaf6ed7e1edd56d6","src/ser/pretty.rs":"b1da2210842fae2f69f76e1429fdc91337805ec21f5e46e8d4fbf6b5ba01b3c5","src/ser/value.rs":"b3616892a500a67f1e14bc21d2b80c11e37387de9d58f877f30c443d0a372328","src/value.rs":"5a6102a111358c3c1f7bb55551ff1a1fd352383dc31e973c85ffe01cb06f38f9","tests/big_struct.rs":"04c78b4e5a707cb359ac01607769b3a9ee272a407e4f5c225bb801d432655254","tests/numbers.rs":"70fd0d8718b364e5512ced9ed8beffd3a3ef5a87109cfef7895bd2209be0929b","tests/roundtrip.rs":"d20e3c28c44ad7e6372e5a64afaf24a6d8db190acbdfde77d22f532ac2a7d1eb"},"package":"da06feaa07f69125ab9ddc769b11de29090122170b402547f64b86fe16ebc399"}
|
|
@ -0,0 +1,32 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "ron"
|
||||
version = "0.1.7"
|
||||
authors = ["Dzmitry Malyshau <kvarkus@gmail.com>", "Thomas Schaller <torkleyy@gmail.com>"]
|
||||
exclude = ["bors.toml", ".travis.yml"]
|
||||
description = "Rusty Object Notation"
|
||||
homepage = "https://github.com/ron-rs/ron"
|
||||
documentation = "https://docs.rs/ron/"
|
||||
keywords = ["parser", "serde", "serialization"]
|
||||
categories = ["encoding"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/ron-rs/ron"
|
||||
|
||||
[lib]
|
||||
name = "ron"
|
||||
[dependencies.serde]
|
||||
version = "1"
|
||||
features = ["serde_derive"]
|
||||
[dev-dependencies.serde_json]
|
||||
version = "1"
|
|
@ -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.
|
|
@ -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.
|
|
@ -0,0 +1,111 @@
|
|||
## Rusty Object Notation
|
||||
|
||||
[![Build Status](https://travis-ci.org/ron-rs/ron.png?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 in JSON
|
||||
|
||||
```json
|
||||
{
|
||||
"materials": {
|
||||
"metal": {
|
||||
"reflectivity": 1.0
|
||||
},
|
||||
"plastic": {
|
||||
"reflectivity": 0.5
|
||||
}
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"name": "hero",
|
||||
"material": "metal"
|
||||
},
|
||||
{
|
||||
"name": "moster",
|
||||
"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/kvark/ron/wiki/Specification).
|
||||
|
||||
### 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!
|
||||
|
||||
## License
|
||||
|
||||
RON is dual-licensed under Apache-2.0 and MIT.
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
extern crate ron;
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ron::de::from_str;
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
const CONFIG: &str = "(
|
||||
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),
|
||||
)";
|
||||
|
||||
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);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
extern crate ron;
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
|
||||
use ron::de::from_reader;
|
||||
|
||||
#[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);
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
extern crate ron;
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::default::Default;
|
||||
use std::fs::File;
|
||||
|
||||
use ron::ser::{PrettyConfig, to_string_pretty};
|
||||
|
||||
#[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() {
|
||||
use std::io::Write;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
let mut file = File::create("config.ron").expect("Failed to create file");
|
||||
|
||||
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 {
|
||||
separate_tuple_members: true,
|
||||
enumerate_arrays: true,
|
||||
..PrettyConfig::default()
|
||||
};
|
||||
let s = to_string_pretty(&data, pretty).expect("Serialization failed");
|
||||
|
||||
file.write(s.as_bytes()).expect("Failed to write data to 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),
|
||||
)
|
|
@ -0,0 +1,35 @@
|
|||
extern crate ron;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
|
||||
use ron::value::Value;
|
||||
use serde::ser::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::from_str(data).expect("Failed to deserialize");
|
||||
let mut ser = serde_json::Serializer::pretty(std::io::stdout());
|
||||
value.serialize(&mut ser).expect("Failed to serialize");
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
name: RON
|
||||
file_extensions:
|
||||
- ron
|
||||
scope: source.ron
|
||||
contexts:
|
||||
main:
|
||||
- include: value
|
||||
array:
|
||||
- match: '\['
|
||||
scope: punctuation.section.array.begin.ron
|
||||
push:
|
||||
- meta_scope: meta.structure.array.ron
|
||||
- match: '\]'
|
||||
scope: punctuation.section.array.end.ron
|
||||
pop: true
|
||||
- include: value
|
||||
- match: ','
|
||||
scope: punctuation.separator.array.ron
|
||||
- match: '[^\s\]]'
|
||||
scope: invalid.illegal.expected-array-separator.ron
|
||||
comments:
|
||||
- match: /\*\*(?!/)
|
||||
scope: punctuation.definition.comment.ron
|
||||
push:
|
||||
- meta_scope: comment.block.documentation.ron
|
||||
- match: \*/
|
||||
pop: true
|
||||
- match: /\*
|
||||
scope: punctuation.definition.comment.ron
|
||||
push:
|
||||
- meta_scope: comment.block.ron
|
||||
- match: \*/
|
||||
pop: true
|
||||
- match: (//).*$\n?
|
||||
scope: comment.line.double-slash.js
|
||||
captures:
|
||||
1: punctuation.definition.comment.ron
|
||||
constant:
|
||||
- match: \b(true|false)\b
|
||||
scope: constant.language.ron
|
||||
number:
|
||||
# handles integer and decimal numbers
|
||||
- match: |-
|
||||
(?x: # turn on extended mode
|
||||
-? # an optional minus
|
||||
(?:
|
||||
0 # a zero
|
||||
| # ...or...
|
||||
[1-9] # a 1-9 character
|
||||
\d* # followed by zero or more digits
|
||||
)
|
||||
(?:
|
||||
(?:
|
||||
\. # a period
|
||||
\d+ # followed by one or more digits
|
||||
)?
|
||||
(?:
|
||||
[eE] # an e character
|
||||
[+-]? # followed by an option +/-
|
||||
\d+ # followed by one or more digits
|
||||
)? # make exponent optional
|
||||
)? # make decimal portion optional
|
||||
)
|
||||
scope: constant.numeric.ron
|
||||
object:
|
||||
- match: '[A-Za-z_][A-Za-z_0-9]*'
|
||||
scope: entity.name.class.ron
|
||||
- match: '\('
|
||||
scope: punctuation.section.dictionary.begin.ron
|
||||
push:
|
||||
- meta_scope: meta.structure.entity.ron
|
||||
- match: '\)'
|
||||
scope: punctuation.section.dictionary.end.ron
|
||||
pop: true
|
||||
- match: '[a-z_][A-Za-z_0-9]*'
|
||||
scope: entity.name.tag.ron
|
||||
- match: '\:'
|
||||
scope: punctuation.separator.dictionary.key-value.ron
|
||||
- include: value
|
||||
- match: ','
|
||||
scope: punctuation.separator.dictionary.ron
|
||||
dictionary:
|
||||
- match: '\{'
|
||||
scope: punctuation.section.dictionary.begin.ron
|
||||
push:
|
||||
- meta_scope: meta.structure.dictionary.ron
|
||||
- match: '\}'
|
||||
scope: punctuation.section.dictionary.end.ron
|
||||
pop: true
|
||||
- include: value
|
||||
- match: ':'
|
||||
scope: punctuation.separator.dictionary.key-value.ron
|
||||
- include: value
|
||||
- match: ','
|
||||
scope: punctuation.separator.dictionary.ron
|
||||
string:
|
||||
- match: '"'
|
||||
scope: punctuation.definition.string.begin.ron
|
||||
push: inside-string
|
||||
inside-string:
|
||||
- meta_scope: string.quoted.double.ron
|
||||
- match: '"'
|
||||
scope: punctuation.definition.string.end.ron
|
||||
pop: true
|
||||
- include: string-escape
|
||||
- match: $\n?
|
||||
scope: invalid.illegal.unclosed-string.ron
|
||||
pop: true
|
||||
string-escape:
|
||||
- match: |-
|
||||
(?x: # turn on extended mode
|
||||
\\ # a literal backslash
|
||||
(?: # ...followed by...
|
||||
["\\/bfnrt] # one of these characters
|
||||
| # ...or...
|
||||
u # a u
|
||||
[0-9a-fA-F]{4} # and four hex digits
|
||||
)
|
||||
)
|
||||
scope: constant.character.escape.ron
|
||||
- match: \\.
|
||||
scope: invalid.illegal.unrecognized-string-escape.ron
|
||||
value:
|
||||
- include: constant
|
||||
- include: number
|
||||
- include: string
|
||||
- include: array
|
||||
- include: dictionary
|
||||
- include: object
|
||||
- include: comments
|
|
@ -0,0 +1,132 @@
|
|||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::str::Utf8Error;
|
||||
use std::io;
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
use serde::de;
|
||||
|
||||
use parse::Position;
|
||||
|
||||
/// Deserialization result.
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
IoError(String),
|
||||
Message(String),
|
||||
Parser(ParseError, Position),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ParseError {
|
||||
Eof,
|
||||
ExpectedArray,
|
||||
ExpectedArrayEnd,
|
||||
ExpectedBoolean,
|
||||
ExpectedComma,
|
||||
ExpectedEnum,
|
||||
ExpectedChar,
|
||||
ExpectedFloat,
|
||||
ExpectedInteger,
|
||||
ExpectedOption,
|
||||
ExpectedOptionEnd,
|
||||
ExpectedMap,
|
||||
ExpectedMapColon,
|
||||
ExpectedMapEnd,
|
||||
ExpectedStruct,
|
||||
ExpectedStructEnd,
|
||||
ExpectedUnit,
|
||||
ExpectedStructName,
|
||||
ExpectedString,
|
||||
ExpectedStringEnd,
|
||||
ExpectedIdentifier,
|
||||
|
||||
InvalidEscape,
|
||||
|
||||
UnexpectedByte(char),
|
||||
|
||||
Utf8Error(Utf8Error),
|
||||
TrailingCharacters,
|
||||
|
||||
#[doc(hidden)]
|
||||
__NonExhaustive,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::IoError(ref s) => write!(f, "{}", s),
|
||||
Error::Message(ref s) => write!(f, "{}", s),
|
||||
Error::Parser(_, pos) => write!(f, "{}: {}", pos, self.description()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl de::Error for Error {
|
||||
fn custom<T: fmt::Display>(msg: T) -> Self {
|
||||
Error::Message(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
Error::IoError(ref s) => s,
|
||||
Error::Message(ref e) => e,
|
||||
Error::Parser(ref kind, _) => match *kind {
|
||||
ParseError::Eof => "Unexpected end of file",
|
||||
ParseError::ExpectedArray => "Expected array",
|
||||
ParseError::ExpectedArrayEnd => "Expected end of array",
|
||||
ParseError::ExpectedBoolean => "Expected boolean",
|
||||
ParseError::ExpectedComma => "Expected comma",
|
||||
ParseError::ExpectedEnum => "Expected enum",
|
||||
ParseError::ExpectedChar => "Expected char",
|
||||
ParseError::ExpectedFloat => "Expected float",
|
||||
ParseError::ExpectedInteger => "Expected integer",
|
||||
ParseError::ExpectedOption => "Expected option",
|
||||
ParseError::ExpectedOptionEnd => "Expected end of option",
|
||||
ParseError::ExpectedMap => "Expected map",
|
||||
ParseError::ExpectedMapColon => "Expected colon",
|
||||
ParseError::ExpectedMapEnd => "Expected end of map",
|
||||
ParseError::ExpectedStruct => "Expected struct",
|
||||
ParseError::ExpectedStructEnd => "Expected end of struct",
|
||||
ParseError::ExpectedUnit => "Expected unit",
|
||||
ParseError::ExpectedStructName => "Expected struct name",
|
||||
ParseError::ExpectedString => "Expected string",
|
||||
ParseError::ExpectedIdentifier => "Expected identifier",
|
||||
|
||||
ParseError::InvalidEscape => "Invalid escape sequence",
|
||||
|
||||
ParseError::Utf8Error(ref e) => e.description(),
|
||||
ParseError::TrailingCharacters => "Non-whitespace trailing characters",
|
||||
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Utf8Error> for ParseError {
|
||||
fn from(e: Utf8Error) -> Self {
|
||||
ParseError::Utf8Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromUtf8Error> for ParseError {
|
||||
fn from(e: FromUtf8Error) -> Self {
|
||||
ParseError::Utf8Error(e.utf8_error())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Utf8Error> for Error {
|
||||
fn from(e: Utf8Error) -> Self {
|
||||
Error::Parser(ParseError::Utf8Error(e), Position { line : 0, col : 0})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(e: io::Error) -> Self {
|
||||
Error::IoError(e.description().to_string())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
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_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_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_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_str<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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,570 @@
|
|||
/// Deserialization module.
|
||||
///
|
||||
|
||||
pub use self::error::{Error, ParseError, Result};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::io;
|
||||
use std::str;
|
||||
|
||||
use serde::de::{self, Deserializer as Deserializer_, DeserializeSeed, Visitor};
|
||||
|
||||
use parse::Bytes;
|
||||
use self::id::IdDeserializer;
|
||||
|
||||
mod error;
|
||||
mod id;
|
||||
#[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> {
|
||||
pub fn from_str(input: &'de str) -> Self {
|
||||
Deserializer {
|
||||
bytes: Bytes::new(input.as_bytes()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_bytes(input: &'de [u8]) -> Self {
|
||||
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)?;
|
||||
let s = str::from_utf8(&bytes)?;
|
||||
from_str(s)
|
||||
}
|
||||
|
||||
/// A convenience function for building a deserializer
|
||||
/// and deserializing a value of type `T`.
|
||||
pub fn from_str<'a, T>(s: &'a str) -> Result<T>
|
||||
where T: de::Deserialize<'a>
|
||||
{
|
||||
let mut deserializer = Deserializer::from_str(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(ParseError::TrailingCharacters)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
if self.bytes.identifier().is_ok() {
|
||||
self.bytes.skip_ws();
|
||||
|
||||
return self.deserialize_struct("", &[], visitor);
|
||||
}
|
||||
|
||||
match self.bytes.peek_or_eof()? {
|
||||
b'(' => self.deserialize_struct("", &[], visitor),
|
||||
b'[' => self.deserialize_seq(visitor),
|
||||
b'{' => self.deserialize_map(visitor),
|
||||
b'0' ... b'9' | b'+' | b'-' | b'.' => self.deserialize_f64(visitor),
|
||||
b'"' => self.deserialize_string(visitor),
|
||||
b'\'' => self.deserialize_char(visitor),
|
||||
other => self.bytes.err(ParseError::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_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_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>
|
||||
{
|
||||
use parse::ParsedStr;
|
||||
|
||||
match self.bytes.string()? {
|
||||
ParsedStr::Allocated(s) => visitor.visit_string(s),
|
||||
ParsedStr::Slice(s) => visitor.visit_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_seq(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value>
|
||||
where V: Visitor<'de>
|
||||
{
|
||||
self.deserialize_seq(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
|
||||
where V: Visitor<'de>
|
||||
{
|
||||
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(ParseError::ExpectedOptionEnd)
|
||||
}
|
||||
|
||||
} else if self.bytes.consume("None") {
|
||||
visitor.visit_none()
|
||||
} else {
|
||||
self.bytes.err(ParseError::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(ParseError::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>
|
||||
{
|
||||
self.bytes.consume(name);
|
||||
|
||||
self.bytes.skip_ws();
|
||||
|
||||
if self.bytes.consume("(") {
|
||||
let value = visitor.visit_newtype_struct(&mut *self)?;
|
||||
self.bytes.comma();
|
||||
|
||||
if self.bytes.consume(")") {
|
||||
Ok(value)
|
||||
} else {
|
||||
self.bytes.err(ParseError::ExpectedStructEnd)
|
||||
}
|
||||
} else {
|
||||
self.bytes.err(ParseError::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(ParseError::ExpectedArrayEnd)
|
||||
}
|
||||
} else {
|
||||
self.bytes.err(ParseError::ExpectedArray)
|
||||
}
|
||||
}
|
||||
|
||||
// Tuples look just like sequences in JSON. Some formats may be able to
|
||||
// represent tuples more efficiently.
|
||||
//
|
||||
// As indicated by the length parameter, the `Deserialize` implementation
|
||||
// for a tuple in the Serde data model is required to know the length of the
|
||||
// tuple before even looking at the input data.
|
||||
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(ParseError::ExpectedArrayEnd)
|
||||
}
|
||||
} else {
|
||||
self.bytes.err(ParseError::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(ParseError::ExpectedMapEnd)
|
||||
}
|
||||
} else {
|
||||
self.bytes.err(ParseError::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(ParseError::ExpectedStructEnd)
|
||||
}
|
||||
} else {
|
||||
self.bytes.err(ParseError::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_bytes(self.bytes.identifier()?)
|
||||
}
|
||||
|
||||
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: ParseError) -> 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 *self.de)?;
|
||||
|
||||
self.had_comma = self.de.bytes.comma();
|
||||
|
||||
Ok(res)
|
||||
} else {
|
||||
self.err(ParseError::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>
|
||||
{
|
||||
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("(") {
|
||||
let val = seed.deserialize(&mut *self.de)?;
|
||||
|
||||
self.de.bytes.comma();
|
||||
|
||||
if self.de.bytes.consume(")") {
|
||||
Ok(val)
|
||||
} else {
|
||||
self.de.bytes.err(ParseError::ExpectedStructEnd)
|
||||
}
|
||||
} else {
|
||||
self.de.bytes.err(ParseError::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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
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 }
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
|
||||
#[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: ParseError, line: usize, col: usize) -> Result<T> {
|
||||
use parse::Position;
|
||||
|
||||
Err(Error::Parser(kind, Position { line, col }))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_err_wrong_value() {
|
||||
use self::ParseError::*;
|
||||
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));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forgot_apostrophes() {
|
||||
let de: Result<(i32, String)> = from_str("(4, \"Hello)");
|
||||
|
||||
assert!(match de {
|
||||
Err(Error::Parser(ParseError::ExpectedStringEnd, _)) => true,
|
||||
_ => false,
|
||||
});
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
|
||||
use serde::de::{Error, MapAccess, SeqAccess, Visitor};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
use de;
|
||||
use value::{Number, Value};
|
||||
|
||||
impl Value {
|
||||
/// Creates a value from a string reference.
|
||||
pub fn from_str(s: &str) -> de::Result<Self> {
|
||||
Self::deserialize(&mut super::Deserializer::from_str(s))
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
{
|
||||
self.visit_f64(v as f64)
|
||||
}
|
||||
|
||||
fn visit_u64<E>(self, v: u64) -> 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: BTreeMap<Value, Value> = BTreeMap::new();
|
||||
|
||||
while let Some(entry) = map.next_entry()? {
|
||||
res.insert(entry.0, entry.1);
|
||||
}
|
||||
|
||||
Ok(Value::Map(res))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn eval(s: &str) -> Value {
|
||||
Value::from_str(s).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_complex() {
|
||||
assert_eq!(eval("Some([
|
||||
Room ( width: 20, height: 5, name: \"The Room\" ),
|
||||
|
||||
(
|
||||
width: 10,
|
||||
height: 10,
|
||||
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.0))),
|
||||
(Value::String("height".to_owned()), Value::Number(Number::new(5.0))),
|
||||
(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.0))),
|
||||
(Value::String("Enemy2".to_owned()), Value::Number(Number::new(5.0))),
|
||||
(Value::String("Enemy3".to_owned()), Value::Number(Number::new(7.0))),
|
||||
].into_iter().collect()
|
||||
)),
|
||||
].into_iter().collect()),
|
||||
]
|
||||
)))));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*!
|
||||
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`.
|
||||
|
||||
!*/
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
|
||||
pub mod de;
|
||||
pub mod ser;
|
||||
pub mod value;
|
||||
|
||||
mod parse;
|
|
@ -0,0 +1,438 @@
|
|||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||
use std::ops::Neg;
|
||||
use std::result::Result as StdResult;
|
||||
use std::str::{FromStr, from_utf8, from_utf8_unchecked};
|
||||
|
||||
use de::{Error, ParseError, Result};
|
||||
|
||||
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, Copy, Debug)]
|
||||
pub struct Bytes<'a> {
|
||||
bytes: &'a [u8],
|
||||
column: usize,
|
||||
line: usize,
|
||||
}
|
||||
|
||||
impl<'a> Bytes<'a> {
|
||||
pub fn new(bytes: &'a [u8]) -> Self {
|
||||
let mut b = Bytes {
|
||||
bytes,
|
||||
column: 1,
|
||||
line: 1,
|
||||
};
|
||||
|
||||
b.skip_ws();
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
pub fn bool(&mut self) -> Result<bool> {
|
||||
if self.consume("true") {
|
||||
Ok(true)
|
||||
} else if self.consume("false") {
|
||||
Ok(false)
|
||||
} else {
|
||||
self.err(ParseError::ExpectedBoolean)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
|
||||
pub fn char(&mut self) -> Result<char> {
|
||||
if !self.consume("'") {
|
||||
return self.err(ParseError::ExpectedChar);
|
||||
}
|
||||
|
||||
let c = self.eat_byte()?;
|
||||
|
||||
let c = if c == b'\\' {
|
||||
let c = self.eat_byte()?;
|
||||
|
||||
if c != b'\\' && c != b'\'' {
|
||||
return self.err(ParseError::InvalidEscape);
|
||||
}
|
||||
|
||||
c
|
||||
} else {
|
||||
c
|
||||
};
|
||||
|
||||
if !self.consume("'") {
|
||||
return self.err(ParseError::ExpectedChar);
|
||||
}
|
||||
|
||||
Ok(c as char)
|
||||
}
|
||||
|
||||
pub fn comma(&mut self) -> bool {
|
||||
self.skip_ws();
|
||||
|
||||
if self.consume(",") {
|
||||
self.skip_ws();
|
||||
|
||||
true
|
||||
} else {
|
||||
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(|b| IDENT_CHAR.contains(b)).unwrap_or(false)
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
|
||||
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: ParseError) -> Result<T> {
|
||||
Err(self.error(kind))
|
||||
}
|
||||
|
||||
pub fn error(&self, kind: ParseError) -> Error {
|
||||
Error::Parser(kind, Position { line: self.line, col: self.column })
|
||||
}
|
||||
|
||||
pub fn float<T>(&mut self) -> Result<T>
|
||||
where T: FromStr
|
||||
{
|
||||
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(ParseError::ExpectedFloat));
|
||||
|
||||
let _ = self.advance(num_bytes);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn identifier(&mut self) -> Result<&[u8]> {
|
||||
if IDENT_FIRST.contains(&self.peek_or_eof()?) {
|
||||
let bytes = self.next_bytes_contained_in(IDENT_CHAR);
|
||||
|
||||
let ident = &self.bytes[..bytes];
|
||||
let _ = self.advance(bytes);
|
||||
|
||||
Ok(ident)
|
||||
} else {
|
||||
self.err(ParseError::ExpectedIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_bytes_contained_in(&self, allowed: &[u8]) -> usize {
|
||||
self.bytes
|
||||
.iter()
|
||||
.take_while(|b| allowed.contains(b))
|
||||
.fold(0, |acc, _| acc + 1)
|
||||
}
|
||||
|
||||
pub fn skip_ws(&mut self) {
|
||||
while self.peek().map(|c| WHITE_SPACE.contains(&c)).unwrap_or(false) {
|
||||
let _ = self.advance_single();
|
||||
}
|
||||
|
||||
if self.skip_comment() {
|
||||
self.skip_ws();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peek(&self) -> Option<u8> {
|
||||
self.bytes.get(0).map(|b| *b)
|
||||
}
|
||||
|
||||
pub fn peek_or_eof(&self) -> Result<u8> {
|
||||
self.bytes.get(0).map(|b| *b).ok_or(self.error(ParseError::Eof))
|
||||
}
|
||||
|
||||
pub fn signed_integer<T>(&mut self) -> Result<T>
|
||||
where T: Neg<Output=T> + Num,
|
||||
{
|
||||
match self.peek_or_eof()? {
|
||||
b'+' => {
|
||||
let _ = self.advance_single();
|
||||
|
||||
self.unsigned_integer()
|
||||
}
|
||||
b'-' => {
|
||||
let _ = self.advance_single();
|
||||
|
||||
self.unsigned_integer::<T>().map(Neg::neg)
|
||||
}
|
||||
_ => self.unsigned_integer(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn string(&mut self) -> Result<ParsedStr> {
|
||||
if !self.consume("\"") {
|
||||
return self.err(ParseError::ExpectedString);
|
||||
}
|
||||
|
||||
let (i, end_or_escape) = self.bytes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|&(_, &b)| b == b'\\' || b == b'"')
|
||||
.ok_or(self.error(ParseError::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);
|
||||
self.parse_str_escape(&mut s)?;
|
||||
|
||||
let (new_i, end_or_escape) = self.bytes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|&(_, &b)| b == b'\\' || b == b'"')
|
||||
.ok_or(ParseError::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);
|
||||
|
||||
break Ok(ParsedStr::Allocated(String::from_utf8(s)
|
||||
.map_err(|e| self.error(e.into()))?));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_for(&self, s: &str) -> bool {
|
||||
s.bytes().enumerate().all(|(i, b)| self.bytes.get(i).map(|t| *t == b).unwrap_or(false))
|
||||
}
|
||||
|
||||
pub fn unsigned_integer<T: Num>(&mut self) -> 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(ParseError::Eof);
|
||||
}
|
||||
|
||||
let res = Num::from_str(unsafe { from_utf8_unchecked(&self.bytes[0..num_bytes]) }, base)
|
||||
.map_err(|_| self.error(ParseError::ExpectedInteger));
|
||||
|
||||
let _ = self.advance(num_bytes);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
fn decode_hex_escape(&mut self) -> Result<u16> {
|
||||
let mut n = 0;
|
||||
for _ in 0..4 {
|
||||
n = match self.eat_byte()? {
|
||||
c @ b'0' ... b'9' => n * 16_u16 + ((c as u16) - (b'0' as u16)),
|
||||
b'a' | b'A' => n * 16_u16 + 10_u16,
|
||||
b'b' | b'B' => n * 16_u16 + 11_u16,
|
||||
b'c' | b'C' => n * 16_u16 + 12_u16,
|
||||
b'd' | b'D' => n * 16_u16 + 13_u16,
|
||||
b'e' | b'E' => n * 16_u16 + 14_u16,
|
||||
b'f' | b'F' => n * 16_u16 + 15_u16,
|
||||
_ => {
|
||||
return self.err(ParseError::InvalidEscape);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
fn parse_str_escape(&mut self, store: &mut Vec<u8>) -> Result<()> {
|
||||
use std::iter::repeat;
|
||||
|
||||
match self.eat_byte()? {
|
||||
b'"' => store.push(b'"'),
|
||||
b'\\' => store.push(b'\\'),
|
||||
b'b' => store.push(b'\x08'),
|
||||
b'f' => store.push(b'\x0c'),
|
||||
b'n' => store.push(b'\n'),
|
||||
b'r' => store.push(b'\r'),
|
||||
b't' => store.push(b'\t'),
|
||||
b'u' => {
|
||||
let c: char = match self.decode_hex_escape()? {
|
||||
0xDC00 ... 0xDFFF => {
|
||||
return self.err(ParseError::InvalidEscape);
|
||||
}
|
||||
|
||||
n1 @ 0xD800 ... 0xDBFF => {
|
||||
if self.eat_byte()? != b'\\' {
|
||||
return self.err(ParseError::InvalidEscape);
|
||||
}
|
||||
|
||||
if self.eat_byte()? != b'u' {
|
||||
return self.err(ParseError::InvalidEscape);
|
||||
}
|
||||
|
||||
let n2 = self.decode_hex_escape()?;
|
||||
|
||||
if n2 < 0xDC00 || n2 > 0xDFFF {
|
||||
return self.err(ParseError::InvalidEscape);
|
||||
}
|
||||
|
||||
let n = (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000;
|
||||
|
||||
match ::std::char::from_u32(n as u32) {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
return self.err(ParseError::InvalidEscape);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n => {
|
||||
match ::std::char::from_u32(n as u32) {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
return self.err(ParseError::InvalidEscape);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let char_start = store.len();
|
||||
store.extend(repeat(0).take(c.len_utf8()));
|
||||
c.encode_utf8(&mut store[char_start..]);
|
||||
}
|
||||
_ => {
|
||||
return self.err(ParseError::InvalidEscape);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn skip_comment(&mut self) -> bool {
|
||||
if self.consume("//") {
|
||||
let bytes = self.bytes.iter().take_while(|&&b| b != b'\n').count();
|
||||
|
||||
let _ = self.advance(bytes);
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Num: Sized {
|
||||
fn from_str(src: &str, radix: u32) -> StdResult<Self, ()>;
|
||||
}
|
||||
|
||||
macro_rules! impl_num {
|
||||
($ty:ident) => {
|
||||
impl Num for $ty {
|
||||
fn from_str(src: &str, radix: u32) -> StdResult<Self, ()> {
|
||||
$ty::from_str_radix(src, radix).map_err(|_| ())
|
||||
}
|
||||
}
|
||||
};
|
||||
($($tys:ident)*) => {
|
||||
$( impl_num!($tys); )*
|
||||
};
|
||||
}
|
||||
|
||||
impl_num!(u8 u16 u32 u64 i8 i16 i32 i64);
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,710 @@
|
|||
use std::error::Error as StdError;
|
||||
use std::fmt::{Display, Formatter, Write, Result as FmtResult};
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use serde::ser::{self, Serialize};
|
||||
|
||||
#[deprecated(since="0.1.4", note="please use `to_string_pretty` with `PrettyConfig::default()` instead")]
|
||||
pub mod pretty;
|
||||
mod value;
|
||||
|
||||
/// Serializes `value` and returns it as string.
|
||||
///
|
||||
/// This function does not generate any newlines or nice formatting;
|
||||
/// if you want that, you can use `pretty::to_string` instead.
|
||||
pub fn to_string<T>(value: &T) -> Result<String>
|
||||
where T: Serialize
|
||||
{
|
||||
let mut s = Serializer {
|
||||
output: String::new(),
|
||||
pretty: None,
|
||||
struct_names: false,
|
||||
};
|
||||
value.serialize(&mut s)?;
|
||||
Ok(s.output)
|
||||
}
|
||||
|
||||
/// Serializes `value` in the recommended RON layout in a pretty way.
|
||||
pub fn to_string_pretty<T>(value: &T, config: PrettyConfig) -> Result<String>
|
||||
where T: Serialize
|
||||
{
|
||||
let mut s = Serializer {
|
||||
output: String::new(),
|
||||
pretty: Some((config, Pretty { indent: 0, sequence_index: Vec::new() })),
|
||||
struct_names: false,
|
||||
};
|
||||
value.serialize(&mut s)?;
|
||||
Ok(s.output)
|
||||
}
|
||||
|
||||
/// Serialization result.
|
||||
pub type Result<T> = StdResult<T, Error>;
|
||||
|
||||
/// Serialization error.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
/// A custom error emitted by a serialized value.
|
||||
Message(String),
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
||||
match *self {
|
||||
Error::Message(ref e) => write!(f, "Custom message: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ser::Error for Error {
|
||||
fn custom<T: Display>(msg: T) -> Self {
|
||||
Error::Message(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
Error::Message(ref e) => e,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pretty serializer state
|
||||
struct Pretty {
|
||||
indent: usize,
|
||||
sequence_index: Vec<usize>,
|
||||
}
|
||||
|
||||
/// Pretty serializer configuration
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct PrettyConfig {
|
||||
/// New line string
|
||||
pub new_line: String,
|
||||
/// Indentation string
|
||||
pub indentor: String,
|
||||
/// Separate tuple members with indentation
|
||||
pub separate_tuple_members: bool,
|
||||
/// Enumerate array items in comments
|
||||
pub enumerate_arrays: bool,
|
||||
}
|
||||
|
||||
impl Default for PrettyConfig {
|
||||
fn default() -> Self {
|
||||
PrettyConfig {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
new_line: "\n".to_string(),
|
||||
#[cfg(target_os = "windows")]
|
||||
new_line: "\r\n".to_string(),
|
||||
indentor: " ".to_string(),
|
||||
separate_tuple_members: false,
|
||||
enumerate_arrays: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The RON serializer.
|
||||
///
|
||||
/// You can just use `to_string` for deserializing a value.
|
||||
/// If you want it pretty-printed, take a look at the `pretty` module.
|
||||
pub struct Serializer {
|
||||
output: String,
|
||||
pretty: Option<(PrettyConfig, Pretty)>,
|
||||
struct_names: bool,
|
||||
}
|
||||
|
||||
impl Serializer {
|
||||
/// Creates a new `Serializer`.
|
||||
///
|
||||
/// Most of the time you can just use `to_string` or `to_string_pretty`.
|
||||
pub fn new(config: Option<PrettyConfig>, struct_names: bool) -> Self {
|
||||
Serializer {
|
||||
output: String::new(),
|
||||
pretty: config.map(|conf| (conf, Pretty { indent: 0, sequence_index: Vec::new() })),
|
||||
struct_names,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes `self` and returns the built `String`.
|
||||
pub fn into_output_string(self) -> String {
|
||||
self.output
|
||||
}
|
||||
|
||||
fn separate_tuple_members(&self) -> bool {
|
||||
self.pretty.as_ref()
|
||||
.map(|&(ref config, _)| config.separate_tuple_members)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn start_indent(&mut self) {
|
||||
if let Some((ref config, ref mut pretty)) = self.pretty {
|
||||
pretty.indent += 1;
|
||||
self.output += &config.new_line;
|
||||
}
|
||||
}
|
||||
|
||||
fn indent(&mut self) {
|
||||
if let Some((ref config, ref pretty)) = self.pretty {
|
||||
self.output.extend((0..pretty.indent).map(|_| config.indentor.as_str()));
|
||||
}
|
||||
}
|
||||
|
||||
fn end_indent(&mut self) {
|
||||
if let Some((ref config, ref mut pretty)) = self.pretty {
|
||||
pretty.indent -= 1;
|
||||
self.output.extend((0..pretty.indent).map(|_| config.indentor.as_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::Serializer for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Self;
|
||||
type SerializeTuple = Self;
|
||||
type SerializeTupleStruct = Self;
|
||||
type SerializeTupleVariant = Self;
|
||||
type SerializeMap = Self;
|
||||
type SerializeStruct = Self;
|
||||
type SerializeStructVariant = Self;
|
||||
|
||||
fn serialize_bool(self, v: bool) -> Result<()> {
|
||||
self.output += if v { "true" } else { "false" };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_i8(self, v: i8) -> Result<()> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_i16(self, v: i16) -> Result<()> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_i32(self, v: i32) -> Result<()> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_i64(self, v: i64) -> Result<()> {
|
||||
// TODO optimize
|
||||
self.output += &v.to_string();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_u8(self, v: u8) -> Result<()> {
|
||||
self.serialize_u64(v as u64)
|
||||
}
|
||||
|
||||
fn serialize_u16(self, v: u16) -> Result<()> {
|
||||
self.serialize_u64(v as u64)
|
||||
}
|
||||
|
||||
fn serialize_u32(self, v: u32) -> Result<()> {
|
||||
self.serialize_u64(v as u64)
|
||||
}
|
||||
|
||||
fn serialize_u64(self, v: u64) -> Result<()> {
|
||||
self.output += &v.to_string();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_f32(self, v: f32) -> Result<()> {
|
||||
self.serialize_f64(v as f64)
|
||||
}
|
||||
|
||||
fn serialize_f64(self, v: f64) -> Result<()> {
|
||||
self.output += &v.to_string();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_char(self, v: char) -> Result<()> {
|
||||
self.output += "'";
|
||||
if v == '\\' || v == '\'' {
|
||||
self.output.push('\\');
|
||||
}
|
||||
self.output.push(v);
|
||||
self.output += "'";
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_str(self, v: &str) -> Result<()> {
|
||||
self.output += "\"";
|
||||
for char in v.chars() {
|
||||
if char == '\\' || char == '"' {
|
||||
self.output.push('\\');
|
||||
}
|
||||
self.output.push(char);
|
||||
}
|
||||
self.output += "\"";
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_bytes(self, v: &[u8]) -> Result<()> {
|
||||
use serde::ser::SerializeSeq;
|
||||
//TODO: shorter version? e.g. base64 encoding in a single line
|
||||
let mut seq = self.serialize_seq(Some(v.len()))?;
|
||||
for byte in v {
|
||||
seq.serialize_element(byte)?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
|
||||
fn serialize_none(self) -> Result<()> {
|
||||
self.output += "None";
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_some<T>(self, value: &T) -> Result<()>
|
||||
where T: ?Sized + Serialize
|
||||
{
|
||||
self.output += "Some(";
|
||||
value.serialize(&mut *self)?;
|
||||
self.output += ")";
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_unit(self) -> Result<()> {
|
||||
self.output += "()";
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_unit_struct(self, name: &'static str) -> Result<()> {
|
||||
if self.struct_names {
|
||||
self.output += name;
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
self.serialize_unit()
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
_: &'static str,
|
||||
_: u32,
|
||||
variant: &'static str
|
||||
) -> Result<()> {
|
||||
self.output += variant;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<()>
|
||||
where T: ?Sized + Serialize
|
||||
{
|
||||
if self.struct_names {
|
||||
self.output += name;
|
||||
}
|
||||
|
||||
self.output += "(";
|
||||
value.serialize(&mut *self)?;
|
||||
self.output += ")";
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T>(
|
||||
self,
|
||||
_: &'static str,
|
||||
_: u32,
|
||||
variant: &'static str,
|
||||
value: &T
|
||||
) -> Result<()>
|
||||
where T: ?Sized + Serialize
|
||||
{
|
||||
self.output += variant;
|
||||
self.output += "(";
|
||||
|
||||
value.serialize(&mut *self)?;
|
||||
|
||||
self.output += ")";
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq> {
|
||||
self.output += "[";
|
||||
|
||||
self.start_indent();
|
||||
|
||||
if let Some((_, ref mut pretty)) = self.pretty {
|
||||
pretty.sequence_index.push(0);
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn serialize_tuple(self, _: usize) -> Result<Self::SerializeTuple> {
|
||||
self.output += "(";
|
||||
|
||||
if self.separate_tuple_members() {
|
||||
self.start_indent();
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
name: &'static str,
|
||||
len: usize
|
||||
) -> Result<Self::SerializeTupleStruct> {
|
||||
if self.struct_names {
|
||||
self.output += name;
|
||||
}
|
||||
|
||||
self.serialize_tuple(len)
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_: &'static str,
|
||||
_: u32,
|
||||
variant: &'static str,
|
||||
_: usize
|
||||
) -> Result<Self::SerializeTupleVariant> {
|
||||
self.output += variant;
|
||||
self.output += "(";
|
||||
|
||||
if self.separate_tuple_members() {
|
||||
self.start_indent();
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
|
||||
self.output += "{";
|
||||
|
||||
self.start_indent();
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn serialize_struct(
|
||||
self,
|
||||
name: &'static str,
|
||||
_: usize
|
||||
) -> Result<Self::SerializeStruct> {
|
||||
if self.struct_names {
|
||||
self.output += name;
|
||||
}
|
||||
self.output += "(";
|
||||
|
||||
self.start_indent();
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
_: &'static str,
|
||||
_: u32,
|
||||
variant: &'static str,
|
||||
_: usize
|
||||
) -> Result<Self::SerializeStructVariant> {
|
||||
self.output += variant;
|
||||
self.output += "(";
|
||||
|
||||
self.start_indent();
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeSeq for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||
where T: ?Sized + Serialize
|
||||
{
|
||||
self.indent();
|
||||
|
||||
value.serialize(&mut **self)?;
|
||||
self.output += ",";
|
||||
|
||||
if let Some((ref config, ref mut pretty)) = self.pretty {
|
||||
if config.enumerate_arrays {
|
||||
assert!(config.new_line.contains('\n'));
|
||||
let index = pretty.sequence_index.last_mut().unwrap();
|
||||
//TODO: when /**/ comments are supported, prepend the index
|
||||
// to an element instead of appending it.
|
||||
write!(self.output, "// [{}]", index).unwrap();
|
||||
*index += 1;
|
||||
}
|
||||
self.output += &config.new_line;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
self.end_indent();
|
||||
|
||||
if let Some((_, ref mut pretty)) = self.pretty {
|
||||
pretty.sequence_index.pop();
|
||||
}
|
||||
|
||||
self.output += "]";
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeTuple for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||
where T: ?Sized + Serialize
|
||||
{
|
||||
if self.separate_tuple_members() {
|
||||
self.indent();
|
||||
}
|
||||
|
||||
value.serialize(&mut **self)?;
|
||||
self.output += ",";
|
||||
|
||||
if let Some((ref config, _)) = self.pretty {
|
||||
self.output += if self.separate_tuple_members() { &config.new_line } else { " " };
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
if self.pretty.is_some() {
|
||||
if self.separate_tuple_members() {
|
||||
self.end_indent();
|
||||
} else {
|
||||
self.output.pop();
|
||||
self.output.pop();
|
||||
}
|
||||
}
|
||||
|
||||
self.output += ")";
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Same thing but for tuple structs.
|
||||
impl<'a> ser::SerializeTupleStruct for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||
where T: ?Sized + Serialize
|
||||
{
|
||||
ser::SerializeTuple::serialize_element(self, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
ser::SerializeTuple::end(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeTupleVariant for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||
where T: ?Sized + Serialize
|
||||
{
|
||||
ser::SerializeTuple::serialize_element(self, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
ser::SerializeTuple::end(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeMap for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
|
||||
where T: ?Sized + Serialize
|
||||
{
|
||||
self.indent();
|
||||
|
||||
key.serialize(&mut **self)
|
||||
}
|
||||
|
||||
fn serialize_value<T>(&mut self, value: &T) -> Result<()>
|
||||
where T: ?Sized + Serialize
|
||||
{
|
||||
self.output += ":";
|
||||
|
||||
if self.pretty.is_some() {
|
||||
self.output += " ";
|
||||
}
|
||||
|
||||
value.serialize(&mut **self)?;
|
||||
self.output += ",";
|
||||
|
||||
if let Some((ref config, _)) = self.pretty {
|
||||
self.output += &config.new_line;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
self.end_indent();
|
||||
|
||||
self.output += "}";
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeStruct for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||
where T: ?Sized + Serialize
|
||||
{
|
||||
self.indent();
|
||||
|
||||
self.output += key;
|
||||
self.output += ":";
|
||||
|
||||
if self.pretty.is_some() {
|
||||
self.output += " ";
|
||||
}
|
||||
|
||||
value.serialize(&mut **self)?;
|
||||
self.output += ",";
|
||||
|
||||
if let Some((ref config, _)) = self.pretty {
|
||||
self.output += &config.new_line;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
self.end_indent();
|
||||
|
||||
self.output += ")";
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeStructVariant for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||
where T: ?Sized + Serialize
|
||||
{
|
||||
ser::SerializeStruct::serialize_field(self, key, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
ser::SerializeStruct::end(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct EmptyStruct1;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct EmptyStruct2 {}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MyStruct { x: f32, y: f32 }
|
||||
|
||||
#[derive(Serialize)]
|
||||
enum MyEnum {
|
||||
A,
|
||||
B(bool),
|
||||
C(bool, f32),
|
||||
D { a: i32, b: i32 }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_struct() {
|
||||
assert_eq!(to_string(&EmptyStruct1).unwrap(), "()");
|
||||
assert_eq!(to_string(&EmptyStruct2 {}).unwrap(), "()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct() {
|
||||
let my_struct = MyStruct { x: 4.0, y: 7.0 };
|
||||
|
||||
assert_eq!(to_string(&my_struct).unwrap(), "(x:4,y:7,)");
|
||||
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct NewType(i32);
|
||||
|
||||
assert_eq!(to_string(&NewType(42)).unwrap(), "(42)");
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct TupleStruct(f32, f32);
|
||||
|
||||
assert_eq!(to_string(&TupleStruct(2.0, 5.0)).unwrap(), "(2,5,)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_option() {
|
||||
assert_eq!(to_string(&Some(1u8)).unwrap(), "Some(1)");
|
||||
assert_eq!(to_string(&None::<u8>).unwrap(), "None");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enum() {
|
||||
assert_eq!(to_string(&MyEnum::A).unwrap(), "A");
|
||||
assert_eq!(to_string(&MyEnum::B(true)).unwrap(), "B(true)");
|
||||
assert_eq!(to_string(&MyEnum::C(true, 3.5)).unwrap(), "C(true,3.5,)");
|
||||
assert_eq!(to_string(&MyEnum::D { a: 2, b: 3 }).unwrap(), "D(a:2,b:3,)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_array() {
|
||||
let empty: [i32; 0] = [];
|
||||
assert_eq!(to_string(&empty).unwrap(), "()");
|
||||
let empty_ref: &[i32] = ∅
|
||||
assert_eq!(to_string(&empty_ref).unwrap(), "[]");
|
||||
|
||||
assert_eq!(to_string(&[2, 3, 4i32]).unwrap(), "(2,3,4,)");
|
||||
assert_eq!(to_string(&(&[2, 3, 4i32] as &[i32])).unwrap(), "[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);
|
||||
|
||||
let s = to_string(&map).unwrap();
|
||||
s.starts_with("{");
|
||||
s.contains("(true,false,):4");
|
||||
s.contains("(false,false,):123");
|
||||
s.ends_with("}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string() {
|
||||
assert_eq!(to_string(&"Some string").unwrap(), "\"Some string\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_char() {
|
||||
assert_eq!(to_string(&'c').unwrap(), "'c'");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_escape() {
|
||||
assert_eq!(to_string(&r#""Quoted""#).unwrap(), r#""\"Quoted\"""#);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
//! Provides default pretty serialization with `to_string`.
|
||||
|
||||
use super::{Result, to_string_pretty};
|
||||
|
||||
use serde::ser::Serialize;
|
||||
use std::default::Default;
|
||||
|
||||
/// Serializes `value` in the recommended RON layout with
|
||||
/// default pretty configuration.
|
||||
pub fn to_string<T>(value: &T) -> Result<String>
|
||||
where T: Serialize
|
||||
{
|
||||
to_string_pretty(value, Default::default())
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
use serde::ser::{Serialize, Serializer};
|
||||
|
||||
use value::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(ref n) => serializer.serialize_f64(n.get()),
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,273 @@
|
|||
//! Value module.
|
||||
|
||||
use std::cmp::{Eq, Ordering};
|
||||
use std::collections::BTreeMap;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use serde::de::{DeserializeSeed, Deserializer, Error as SerdeErr, MapAccess, SeqAccess, Visitor};
|
||||
|
||||
use de::{Error as RonError, Result};
|
||||
|
||||
/// A wrapper for `f64` which guarantees that the inner value
|
||||
/// is finite and thus implements `Eq`, `Hash` and `Ord`.
|
||||
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
|
||||
pub struct Number(f64);
|
||||
|
||||
impl Number {
|
||||
/// Panics if `v` is not a real number
|
||||
/// (infinity, NaN, ..).
|
||||
pub fn new(v: f64) -> Self {
|
||||
if !v.is_finite() {
|
||||
panic!("Tried to create Number with a NaN / infinity");
|
||||
}
|
||||
|
||||
Number(v)
|
||||
}
|
||||
|
||||
/// Returns the wrapped float.
|
||||
pub fn get(&self) -> f64 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Number {}
|
||||
|
||||
impl Hash for Number {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u64(self.0 as u64);
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Number {
|
||||
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(BTreeMap<Value, Value>),
|
||||
Number(Number),
|
||||
Option(Option<Box<Value>>),
|
||||
String(String),
|
||||
Seq(Vec<Value>),
|
||||
Unit,
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
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(Map {
|
||||
keys: m.keys().cloned().rev().collect(),
|
||||
values: m.values().cloned().rev().collect(),
|
||||
}),
|
||||
Value::Number(n) => visitor.visit_f64(n.get()),
|
||||
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(n) => visitor.visit_i64(n.get() as i64),
|
||||
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(n) => visitor.visit_u64(n.get() as u64),
|
||||
v => Err(RonError::custom(format!("Expected a number, got {:?}", v))),
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
struct Map {
|
||||
keys: Vec<Value>,
|
||||
values: Vec<Value>,
|
||||
}
|
||||
|
||||
impl<'de> MapAccess<'de> for Map {
|
||||
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 std::fmt::Debug;
|
||||
use serde::Deserialize;
|
||||
use super::*;
|
||||
|
||||
fn assert_same<'de, T>(s: &'de str)
|
||||
where
|
||||
T: Debug + Deserialize<'de> + PartialEq,
|
||||
{
|
||||
use 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::<()>("()");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
extern crate ron;
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
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),
|
||||
|
||||
ignored_field: \"Totally ignored, not causing a panic. Hopefully.\",
|
||||
)";
|
||||
|
||||
#[test]
|
||||
fn deserialize_big_struct() {
|
||||
ron::de::from_str::<ImGuiStyleSave>(CONFIG).unwrap();
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
extern crate ron;
|
||||
|
||||
#[test]
|
||||
fn test_hex() {
|
||||
assert_eq!(ron::de::from_str("0x507"), Ok(0x507));
|
||||
assert_eq!(ron::de::from_str("0x1A5"), Ok(0x1A5));
|
||||
assert_eq!(ron::de::from_str("0x53C537"), Ok(0x53C537));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bin() {
|
||||
assert_eq!(ron::de::from_str("0b101"), Ok(0b101));
|
||||
assert_eq!(ron::de::from_str("0b001"), Ok(0b001));
|
||||
assert_eq!(ron::de::from_str("0b100100"), Ok(0b100100));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_oct() {
|
||||
assert_eq!(ron::de::from_str("0o1461"), Ok(0o1461));
|
||||
assert_eq!(ron::de::from_str("0o051"), Ok(0o051));
|
||||
assert_eq!(ron::de::from_str("0o150700"), Ok(0o150700));
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
extern crate ron;
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
|
||||
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>,
|
||||
}
|
||||
|
||||
#[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 {
|
||||
enumerate_arrays: true,
|
||||
.. Default::default()
|
||||
};
|
||||
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);
|
||||
}
|
|
@ -1113,6 +1113,14 @@ name = "regex-syntax"
|
|||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "ron"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "runloop"
|
||||
version = "0.1.0"
|
||||
|
@ -1180,6 +1188,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
name = "serde"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde_derive 1.0.27 (git+https://github.com/gankro/serde?branch=deserialize_from_enums4)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
|
@ -1546,6 +1557,8 @@ dependencies = [
|
|||
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plane-split 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1755,6 +1768,7 @@ dependencies = [
|
|||
"checksum redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ab105df655884ede59d45b7070c8a65002d921461ee813a024558ca16030eea0"
|
||||
"checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b"
|
||||
"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db"
|
||||
"checksum ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "da06feaa07f69125ab9ddc769b11de29090122170b402547f64b86fe16ebc399"
|
||||
"checksum runloop 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d79b4b604167921892e84afbbaad9d5ad74e091bf6c511d9dbfb0593f09fabd"
|
||||
"checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7"
|
||||
"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
|
||||
|
|
|
@ -1100,6 +1100,14 @@ name = "regex-syntax"
|
|||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "ron"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "runloop"
|
||||
version = "0.1.0"
|
||||
|
@ -1167,6 +1175,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
name = "serde"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde_derive 1.0.27 (git+https://github.com/gankro/serde?branch=deserialize_from_enums4)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
|
@ -1557,6 +1568,8 @@ dependencies = [
|
|||
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plane-split 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1757,6 +1770,7 @@ dependencies = [
|
|||
"checksum redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ab105df655884ede59d45b7070c8a65002d921461ee813a024558ca16030eea0"
|
||||
"checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b"
|
||||
"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db"
|
||||
"checksum ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "da06feaa07f69125ab9ddc769b11de29090122170b402547f64b86fe16ebc399"
|
||||
"checksum runloop 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d79b4b604167921892e84afbbaad9d5ad74e091bf6c511d9dbfb0593f09fabd"
|
||||
"checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7"
|
||||
"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
|
||||
|
|
Загрузка…
Ссылка в новой задаче