зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1336540 part 2 - Vendor toml. r=froydnj
MozReview-Commit-ID: LmZgzyxTLwc --HG-- extra : rebase_source : 54498689cdcf28c58b6ee1922e057ffc8a50da45
This commit is contained in:
Родитель
029693f013
Коммит
013d14243b
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1,2 @@
|
|||
target
|
||||
Cargo.lock
|
|
@ -0,0 +1,31 @@
|
|||
language: rust
|
||||
rust:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
sudo: false
|
||||
before_script:
|
||||
- pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH
|
||||
script:
|
||||
- cargo build --verbose
|
||||
- cargo build --verbose --no-default-features
|
||||
- cargo build --verbose --features serde --no-default-features
|
||||
- cargo test --verbose --features serde
|
||||
- cargo test --verbose --manifest-path serde-tests/Cargo.toml
|
||||
- rustdoc --test README.md -L target
|
||||
- cargo doc --no-deps
|
||||
after_success:
|
||||
- travis-cargo --only nightly doc-upload
|
||||
- travis-cargo coveralls --no-sudo
|
||||
env:
|
||||
global:
|
||||
secure: LZMkQQJT5LqLQQ8JyakjvHNqqMPy8lm/SyC+H5cKUVI/xk7xRuti4eKY937N8uSmbff2m9ZYlG6cNwIOfk/nWn8YsqxA8Wg/xugubWzqGuqu+NQ4IZVa7INT2Fiqyk5SPCh8B5fo2x7OBJ24SCkWb2p8bEWAuW8XdZZOdmi3H2I=
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libcurl4-openssl-dev
|
||||
- libelf-dev
|
||||
- libdw-dev
|
|
@ -0,0 +1,26 @@
|
|||
[package]
|
||||
|
||||
name = "toml"
|
||||
version = "0.2.1"
|
||||
authors = ["Alex Crichton <alex@alexcrichton.com>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
readme = "README.md"
|
||||
keywords = ["encoding"]
|
||||
repository = "https://github.com/alexcrichton/toml-rs"
|
||||
homepage = "https://github.com/alexcrichton/toml-rs"
|
||||
documentation = "http://alexcrichton.com/toml-rs"
|
||||
description = """
|
||||
A native Rust encoder and decoder of TOML-formatted files and streams. Provides
|
||||
implementations of the standard Encodable/Decodable traits for TOML data to
|
||||
facilitate deserializing and serializing Rust structures.
|
||||
"""
|
||||
|
||||
[dependencies]
|
||||
rustc-serialize = { optional = true, version = "0.3.0" }
|
||||
serde = { optional = true, version = "0.8" }
|
||||
|
||||
[features]
|
||||
default = ["rustc-serialize"]
|
||||
|
||||
[dev-dependencies]
|
||||
rustc-serialize = "0.3"
|
|
@ -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) 2014 Alex Crichton
|
||||
|
||||
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,26 @@
|
|||
# toml-rs
|
||||
|
||||
[![Build Status](https://travis-ci.org/alexcrichton/toml-rs.svg?branch=master)](https://travis-ci.org/alexcrichton/toml-rs)
|
||||
[![Coverage Status](https://coveralls.io/repos/alexcrichton/toml-rs/badge.svg?branch=master&service=github)](https://coveralls.io/github/alexcrichton/toml-rs?branch=master)
|
||||
|
||||
[Documentation](http://alexcrichton.com/toml-rs)
|
||||
|
||||
A [TOML][toml] decoder and encoder for Rust. This library is currently compliant with
|
||||
the v0.4.0 version of TOML. This library will also likely continue to stay up to
|
||||
date with the TOML specification as changes happen.
|
||||
|
||||
[toml]: https://github.com/toml-lang/toml
|
||||
|
||||
```toml
|
||||
# Cargo.toml
|
||||
[dependencies]
|
||||
toml = "0.2"
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
`toml-rs` is primarily distributed under the terms of both the MIT license and
|
||||
the Apache License (Version 2.0), with portions covered by various BSD-like
|
||||
licenses.
|
||||
|
||||
See LICENSE-APACHE, and LICENSE-MIT for details.
|
|
@ -0,0 +1,57 @@
|
|||
#![deny(warnings)]
|
||||
|
||||
extern crate toml;
|
||||
extern crate rustc_serialize;
|
||||
|
||||
use std::fs::File;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
|
||||
use toml::Value;
|
||||
use rustc_serialize::json::Json;
|
||||
|
||||
fn main() {
|
||||
let mut args = env::args();
|
||||
let mut input = String::new();
|
||||
let filename = if args.len() > 1 {
|
||||
let name = args.nth(1).unwrap();
|
||||
File::open(&name).and_then(|mut f| {
|
||||
f.read_to_string(&mut input)
|
||||
}).unwrap();
|
||||
name
|
||||
} else {
|
||||
io::stdin().read_to_string(&mut input).unwrap();
|
||||
"<stdin>".to_string()
|
||||
};
|
||||
|
||||
let mut parser = toml::Parser::new(&input);
|
||||
let toml = match parser.parse() {
|
||||
Some(toml) => toml,
|
||||
None => {
|
||||
for err in &parser.errors {
|
||||
let (loline, locol) = parser.to_linecol(err.lo);
|
||||
let (hiline, hicol) = parser.to_linecol(err.hi);
|
||||
println!("{}:{}:{}-{}:{} error: {}",
|
||||
filename, loline, locol, hiline, hicol, err.desc);
|
||||
}
|
||||
return
|
||||
}
|
||||
};
|
||||
let json = convert(Value::Table(toml));
|
||||
println!("{}", json.pretty());
|
||||
}
|
||||
|
||||
fn convert(toml: Value) -> Json {
|
||||
match toml {
|
||||
Value::String(s) => Json::String(s),
|
||||
Value::Integer(i) => Json::I64(i),
|
||||
Value::Float(f) => Json::F64(f),
|
||||
Value::Boolean(b) => Json::Boolean(b),
|
||||
Value::Array(arr) => Json::Array(arr.into_iter().map(convert).collect()),
|
||||
Value::Table(table) => Json::Object(table.into_iter().map(|(k, v)| {
|
||||
(k, convert(v))
|
||||
}).collect()),
|
||||
Value::Datetime(dt) => Json::String(dt),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
use std::collections::{btree_map, BTreeMap};
|
||||
use std::iter::Peekable;
|
||||
|
||||
use Value;
|
||||
use self::DecodeErrorKind::*;
|
||||
|
||||
#[cfg(feature = "rustc-serialize")] mod rustc_serialize;
|
||||
#[cfg(feature = "serde")] mod serde;
|
||||
|
||||
/// A structure to transform TOML values into Rust values.
|
||||
///
|
||||
/// This decoder implements the serialization `Decoder` interface, allowing
|
||||
/// `Decodable` types to be generated by this decoder. The input is any
|
||||
/// arbitrary TOML value.
|
||||
pub struct Decoder {
|
||||
/// The TOML value left over after decoding. This can be used to inspect
|
||||
/// whether fields were decoded or not.
|
||||
pub toml: Option<Value>,
|
||||
cur_field: Option<String>,
|
||||
|
||||
// These aren't used if serde is in use
|
||||
#[cfg_attr(feature = "serde", allow(dead_code))]
|
||||
cur_map: Peekable<btree_map::IntoIter<String, Value>>,
|
||||
#[cfg_attr(feature = "serde", allow(dead_code))]
|
||||
leftover_map: ::Table,
|
||||
}
|
||||
|
||||
/// Description for errors which can occur while decoding a type.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct DecodeError {
|
||||
/// Field that this error applies to.
|
||||
pub field: Option<String>,
|
||||
/// The type of error which occurred while decoding,
|
||||
pub kind: DecodeErrorKind,
|
||||
}
|
||||
|
||||
/// Enumeration of possible errors which can occur while decoding a structure.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum DecodeErrorKind {
|
||||
/// An error flagged by the application, e.g. value out of range
|
||||
ApplicationError(String),
|
||||
/// A field was expected, but none was found.
|
||||
ExpectedField(/* type */ Option<&'static str>),
|
||||
/// A field was found, but it was not an expected one.
|
||||
UnknownField,
|
||||
/// A field was found, but it had the wrong type.
|
||||
ExpectedType(/* expected */ &'static str, /* found */ &'static str),
|
||||
/// The nth map key was expected, but none was found.
|
||||
ExpectedMapKey(usize),
|
||||
/// The nth map element was expected, but none was found.
|
||||
ExpectedMapElement(usize),
|
||||
/// An enum decoding was requested, but no variants were supplied
|
||||
NoEnumVariants,
|
||||
/// The unit type was being decoded, but a non-zero length string was found
|
||||
NilTooLong,
|
||||
/// There was an error with the syntactical structure of the TOML.
|
||||
SyntaxError,
|
||||
/// A custom error was generated when decoding.
|
||||
CustomError(String),
|
||||
/// The end of the TOML input was reached too soon
|
||||
EndOfStream,
|
||||
/// Produced by serde ...
|
||||
InvalidType(&'static str),
|
||||
}
|
||||
|
||||
/// Decodes a TOML value into a decodable type.
|
||||
///
|
||||
/// This function will consume the given TOML value and attempt to decode it
|
||||
/// into the type specified. If decoding fails, `None` will be returned. If a
|
||||
/// finer-grained error is desired, then it is recommended to use `Decodable`
|
||||
/// directly.
|
||||
#[cfg(feature = "rustc-serialize")]
|
||||
pub fn decode<T: ::rustc_serialize::Decodable>(toml: Value) -> Option<T> {
|
||||
::rustc_serialize::Decodable::decode(&mut Decoder::new(toml)).ok()
|
||||
}
|
||||
|
||||
/// Decodes a TOML value into a decodable type.
|
||||
///
|
||||
/// This function will consume the given TOML value and attempt to decode it
|
||||
/// into the type specified. If decoding fails, `None` will be returned. If a
|
||||
/// finer-grained error is desired, then it is recommended to use `Decodable`
|
||||
/// directly.
|
||||
#[cfg(all(not(feature = "rustc-serialize"), feature = "serde"))]
|
||||
pub fn decode<T: ::serde::Deserialize>(toml: Value) -> Option<T> {
|
||||
::serde::Deserialize::deserialize(&mut Decoder::new(toml)).ok()
|
||||
}
|
||||
|
||||
/// Decodes a string into a toml-encoded value.
|
||||
///
|
||||
/// This function will parse the given string into a TOML value, and then parse
|
||||
/// the TOML value into the desired type. If any error occurs, `None` is
|
||||
/// returned.
|
||||
///
|
||||
/// If more fine-grained errors are desired, these steps should be driven
|
||||
/// manually.
|
||||
#[cfg(feature = "rustc-serialize")]
|
||||
pub fn decode_str<T: ::rustc_serialize::Decodable>(s: &str) -> Option<T> {
|
||||
::Parser::new(s).parse().and_then(|t| decode(Value::Table(t)))
|
||||
}
|
||||
|
||||
/// Decodes a string into a toml-encoded value.
|
||||
///
|
||||
/// This function will parse the given string into a TOML value, and then parse
|
||||
/// the TOML value into the desired type. If any error occurs, `None` is
|
||||
/// returned.
|
||||
///
|
||||
/// If more fine-grained errors are desired, these steps should be driven
|
||||
/// manually.
|
||||
#[cfg(all(not(feature = "rustc-serialize"), feature = "serde"))]
|
||||
pub fn decode_str<T: ::serde::Deserialize>(s: &str) -> Option<T> {
|
||||
::Parser::new(s).parse().and_then(|t| decode(Value::Table(t)))
|
||||
}
|
||||
|
||||
impl Decoder {
|
||||
/// Creates a new decoder, consuming the TOML value to decode.
|
||||
///
|
||||
/// This decoder can be passed to the `Decodable` methods or driven
|
||||
/// manually.
|
||||
pub fn new(toml: Value) -> Decoder {
|
||||
Decoder::new_empty(Some(toml), None)
|
||||
}
|
||||
|
||||
fn sub_decoder(&self, toml: Option<Value>, field: &str) -> Decoder {
|
||||
let cur_field = if field.is_empty() {
|
||||
self.cur_field.clone()
|
||||
} else {
|
||||
match self.cur_field {
|
||||
None => Some(field.to_string()),
|
||||
Some(ref s) => Some(format!("{}.{}", s, field))
|
||||
}
|
||||
};
|
||||
Decoder::new_empty(toml, cur_field)
|
||||
}
|
||||
|
||||
fn new_empty(toml: Option<Value>, cur_field: Option<String>) -> Decoder {
|
||||
Decoder {
|
||||
toml: toml,
|
||||
cur_field: cur_field,
|
||||
leftover_map: BTreeMap::new(),
|
||||
cur_map: BTreeMap::new().into_iter().peekable(),
|
||||
}
|
||||
}
|
||||
|
||||
fn err(&self, kind: DecodeErrorKind) -> DecodeError {
|
||||
DecodeError {
|
||||
field: self.cur_field.clone(),
|
||||
kind: kind,
|
||||
}
|
||||
}
|
||||
|
||||
fn mismatch(&self, expected: &'static str,
|
||||
found: &Option<Value>) -> DecodeError{
|
||||
match *found {
|
||||
Some(ref val) => self.err(ExpectedType(expected, val.type_str())),
|
||||
None => self.err(ExpectedField(Some(expected))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DecodeError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
try!(match self.kind {
|
||||
ApplicationError(ref err) => {
|
||||
write!(f, "{}", err)
|
||||
}
|
||||
ExpectedField(expected_type) => {
|
||||
match expected_type {
|
||||
Some("table") => write!(f, "expected a section"),
|
||||
Some(e) => write!(f, "expected a value of type `{}`", e),
|
||||
None => write!(f, "expected a value"),
|
||||
}
|
||||
}
|
||||
UnknownField => write!(f, "unknown field"),
|
||||
ExpectedType(expected, found) => {
|
||||
fn humanize(s: &str) -> String {
|
||||
if s == "section" {
|
||||
"a section".to_string()
|
||||
} else {
|
||||
format!("a value of type `{}`", s)
|
||||
}
|
||||
}
|
||||
write!(f, "expected {}, but found {}",
|
||||
humanize(expected),
|
||||
humanize(found))
|
||||
}
|
||||
ExpectedMapKey(idx) => {
|
||||
write!(f, "expected at least {} keys", idx + 1)
|
||||
}
|
||||
ExpectedMapElement(idx) => {
|
||||
write!(f, "expected at least {} elements", idx + 1)
|
||||
}
|
||||
NoEnumVariants => {
|
||||
write!(f, "expected an enum variant to decode to")
|
||||
}
|
||||
NilTooLong => {
|
||||
write!(f, "expected 0-length string")
|
||||
}
|
||||
SyntaxError => {
|
||||
write!(f, "syntax error")
|
||||
}
|
||||
EndOfStream => {
|
||||
write!(f, "end of stream")
|
||||
}
|
||||
InvalidType(s) => {
|
||||
write!(f, "invalid type: {}", s)
|
||||
}
|
||||
CustomError(ref s) => {
|
||||
write!(f, "custom error: {}", s)
|
||||
}
|
||||
});
|
||||
match self.field {
|
||||
Some(ref s) => {
|
||||
write!(f, " for the key `{}`", s)
|
||||
}
|
||||
None => Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for DecodeError {
|
||||
fn description(&self) -> &str {
|
||||
match self.kind {
|
||||
ApplicationError(ref s) => &**s,
|
||||
ExpectedField(..) => "expected a field",
|
||||
UnknownField => "found an unknown field",
|
||||
ExpectedType(..) => "expected a type",
|
||||
ExpectedMapKey(..) => "expected a map key",
|
||||
ExpectedMapElement(..) => "expected a map element",
|
||||
NoEnumVariants => "no enum variants to decode to",
|
||||
NilTooLong => "nonzero length string representing nil",
|
||||
SyntaxError => "syntax error",
|
||||
EndOfStream => "end of stream",
|
||||
InvalidType(..) => "invalid type",
|
||||
CustomError(..) => "custom error",
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,371 @@
|
|||
use rustc_serialize;
|
||||
use std::mem;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use super::{Decoder, DecodeError};
|
||||
use super::DecodeErrorKind::*;
|
||||
use Value;
|
||||
|
||||
impl rustc_serialize::Decoder for Decoder {
|
||||
type Error = DecodeError;
|
||||
fn read_nil(&mut self) -> Result<(), DecodeError> {
|
||||
match self.toml {
|
||||
Some(Value::String(ref s)) if s.is_empty() => {}
|
||||
Some(Value::String(..)) => return Err(self.err(NilTooLong)),
|
||||
ref found => return Err(self.mismatch("string", found)),
|
||||
}
|
||||
self.toml.take();
|
||||
Ok(())
|
||||
}
|
||||
fn read_usize(&mut self) -> Result<usize, DecodeError> {
|
||||
self.read_i64().map(|i| i as usize)
|
||||
}
|
||||
fn read_u64(&mut self) -> Result<u64, DecodeError> {
|
||||
self.read_i64().map(|i| i as u64)
|
||||
}
|
||||
fn read_u32(&mut self) -> Result<u32, DecodeError> {
|
||||
self.read_i64().map(|i| i as u32)
|
||||
}
|
||||
fn read_u16(&mut self) -> Result<u16, DecodeError> {
|
||||
self.read_i64().map(|i| i as u16)
|
||||
}
|
||||
fn read_u8(&mut self) -> Result<u8, DecodeError> {
|
||||
self.read_i64().map(|i| i as u8)
|
||||
}
|
||||
fn read_isize(&mut self) -> Result<isize, DecodeError> {
|
||||
self.read_i64().map(|i| i as isize)
|
||||
}
|
||||
fn read_i64(&mut self) -> Result<i64, DecodeError> {
|
||||
match self.toml {
|
||||
Some(Value::Integer(i)) => { self.toml.take(); Ok(i) }
|
||||
ref found => Err(self.mismatch("integer", found)),
|
||||
}
|
||||
}
|
||||
fn read_i32(&mut self) -> Result<i32, DecodeError> {
|
||||
self.read_i64().map(|i| i as i32)
|
||||
}
|
||||
fn read_i16(&mut self) -> Result<i16, DecodeError> {
|
||||
self.read_i64().map(|i| i as i16)
|
||||
}
|
||||
fn read_i8(&mut self) -> Result<i8, DecodeError> {
|
||||
self.read_i64().map(|i| i as i8)
|
||||
}
|
||||
fn read_bool(&mut self) -> Result<bool, DecodeError> {
|
||||
match self.toml {
|
||||
Some(Value::Boolean(b)) => { self.toml.take(); Ok(b) }
|
||||
ref found => Err(self.mismatch("bool", found)),
|
||||
}
|
||||
}
|
||||
fn read_f64(&mut self) -> Result<f64, DecodeError> {
|
||||
match self.toml {
|
||||
Some(Value::Float(f)) => { self.toml.take(); Ok(f) },
|
||||
ref found => Err(self.mismatch("float", found)),
|
||||
}
|
||||
}
|
||||
fn read_f32(&mut self) -> Result<f32, DecodeError> {
|
||||
self.read_f64().map(|f| f as f32)
|
||||
}
|
||||
fn read_char(&mut self) -> Result<char, DecodeError> {
|
||||
let ch = match self.toml {
|
||||
Some(Value::String(ref s)) if s.chars().count() == 1 =>
|
||||
s.chars().next().unwrap(),
|
||||
ref found => return Err(self.mismatch("string", found)),
|
||||
};
|
||||
self.toml.take();
|
||||
Ok(ch)
|
||||
}
|
||||
fn read_str(&mut self) -> Result<String, DecodeError> {
|
||||
match self.toml.take() {
|
||||
Some(Value::String(s)) => Ok(s),
|
||||
found => {
|
||||
let err = Err(self.mismatch("string", &found));
|
||||
self.toml = found;
|
||||
err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compound types:
|
||||
fn read_enum<T, F>(&mut self, _name: &str, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
f(self)
|
||||
}
|
||||
|
||||
fn read_enum_variant<T, F>(&mut self, names: &[&str], mut f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnMut(&mut Decoder, usize) -> Result<T, DecodeError>
|
||||
{
|
||||
// When decoding enums, this crate takes the strategy of trying to
|
||||
// decode the current TOML as all of the possible variants, returning
|
||||
// success on the first one that succeeds.
|
||||
//
|
||||
// Note that fidelity of the errors returned here is a little nebulous,
|
||||
// but we try to return the error that had the relevant field as the
|
||||
// longest field. This way we hopefully match an error against what was
|
||||
// most likely being written down without losing too much info.
|
||||
let mut first_error = None::<DecodeError>;
|
||||
for i in 0..names.len() {
|
||||
let mut d = self.sub_decoder(self.toml.clone(), "");
|
||||
match f(&mut d, i) {
|
||||
Ok(t) => {
|
||||
self.toml = d.toml;
|
||||
return Ok(t)
|
||||
}
|
||||
Err(e) => {
|
||||
if let Some(ref first) = first_error {
|
||||
let my_len = e.field.as_ref().map(|s| s.len());
|
||||
let first_len = first.field.as_ref().map(|s| s.len());
|
||||
if my_len <= first_len {
|
||||
continue
|
||||
}
|
||||
}
|
||||
first_error = Some(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(first_error.unwrap_or_else(|| self.err(NoEnumVariants)))
|
||||
}
|
||||
fn read_enum_variant_arg<T, F>(&mut self, _a_idx: usize, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
f(self)
|
||||
}
|
||||
|
||||
fn read_enum_struct_variant<T, F>(&mut self, _names: &[&str], _f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnMut(&mut Decoder, usize) -> Result<T, DecodeError>
|
||||
{
|
||||
panic!()
|
||||
}
|
||||
fn read_enum_struct_variant_field<T, F>(&mut self,
|
||||
_f_name: &str,
|
||||
_f_idx: usize,
|
||||
_f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn read_struct<T, F>(&mut self, _s_name: &str, _len: usize, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
match self.toml {
|
||||
Some(Value::Table(..)) => {
|
||||
let ret = try!(f(self));
|
||||
match self.toml {
|
||||
Some(Value::Table(ref t)) if t.is_empty() => {}
|
||||
_ => return Ok(ret)
|
||||
}
|
||||
self.toml.take();
|
||||
Ok(ret)
|
||||
}
|
||||
ref found => Err(self.mismatch("table", found)),
|
||||
}
|
||||
}
|
||||
fn read_struct_field<T, F>(&mut self, f_name: &str, _f_idx: usize, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
let field = f_name.to_string();
|
||||
let toml = match self.toml {
|
||||
Some(Value::Table(ref mut table)) => {
|
||||
table.remove(&field)
|
||||
.or_else(|| table.remove(&f_name.replace("_", "-")))
|
||||
},
|
||||
ref found => return Err(self.mismatch("table", found)),
|
||||
};
|
||||
let mut d = self.sub_decoder(toml, f_name);
|
||||
let ret = try!(f(&mut d));
|
||||
if let Some(value) = d.toml {
|
||||
if let Some(Value::Table(ref mut table)) = self.toml {
|
||||
table.insert(field, value);
|
||||
}
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn read_tuple<T, F>(&mut self, tuple_len: usize, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
self.read_seq(move |d, len| {
|
||||
assert!(len == tuple_len,
|
||||
"expected tuple of length `{}`, found tuple \
|
||||
of length `{}`", tuple_len, len);
|
||||
f(d)
|
||||
})
|
||||
}
|
||||
fn read_tuple_arg<T, F>(&mut self, a_idx: usize, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
self.read_seq_elt(a_idx, f)
|
||||
}
|
||||
|
||||
fn read_tuple_struct<T, F>(&mut self, _s_name: &str, _len: usize, _f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
panic!()
|
||||
}
|
||||
fn read_tuple_struct_arg<T, F>(&mut self, _a_idx: usize, _f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
panic!()
|
||||
}
|
||||
|
||||
// Specialized types:
|
||||
fn read_option<T, F>(&mut self, mut f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnMut(&mut Decoder, bool) -> Result<T, DecodeError>
|
||||
{
|
||||
match self.toml {
|
||||
Some(..) => f(self, true),
|
||||
None => f(self, false),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_seq<T, F>(&mut self, f: F) -> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder, usize) -> Result<T, DecodeError>
|
||||
{
|
||||
let len = match self.toml {
|
||||
Some(Value::Array(ref arr)) => arr.len(),
|
||||
None => 0,
|
||||
ref found => return Err(self.mismatch("array", found)),
|
||||
};
|
||||
let ret = try!(f(self, len));
|
||||
match self.toml {
|
||||
Some(Value::Array(ref mut arr)) => {
|
||||
arr.retain(|slot| slot.as_integer() != Some(0));
|
||||
if !arr.is_empty() { return Ok(ret) }
|
||||
}
|
||||
_ => return Ok(ret)
|
||||
}
|
||||
self.toml.take();
|
||||
Ok(ret)
|
||||
}
|
||||
fn read_seq_elt<T, F>(&mut self, idx: usize, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
let toml = match self.toml {
|
||||
Some(Value::Array(ref mut arr)) => {
|
||||
mem::replace(&mut arr[idx], Value::Integer(0))
|
||||
}
|
||||
ref found => return Err(self.mismatch("array", found)),
|
||||
};
|
||||
let mut d = self.sub_decoder(Some(toml), "");
|
||||
let ret = try!(f(&mut d));
|
||||
if let Some(toml) = d.toml {
|
||||
if let Some(Value::Array(ref mut arr)) = self.toml {
|
||||
arr[idx] = toml;
|
||||
}
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn read_map<T, F>(&mut self, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder, usize) -> Result<T, DecodeError>
|
||||
{
|
||||
let map = match self.toml.take() {
|
||||
Some(Value::Table(table)) => table,
|
||||
found => {
|
||||
self.toml = found;
|
||||
return Err(self.mismatch("table", &self.toml))
|
||||
}
|
||||
};
|
||||
let amt = map.len();
|
||||
let prev_iter = mem::replace(&mut self.cur_map,
|
||||
map.into_iter().peekable());
|
||||
let prev_map = mem::replace(&mut self.leftover_map, BTreeMap::new());
|
||||
let ret = try!(f(self, amt));
|
||||
let leftover = mem::replace(&mut self.leftover_map, prev_map);
|
||||
self.cur_map = prev_iter;
|
||||
if !leftover.is_empty() {
|
||||
self.toml = Some(Value::Table(leftover));
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
fn read_map_elt_key<T, F>(&mut self, idx: usize, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
let key = match self.cur_map.peek().map(|p| p.0.clone()) {
|
||||
Some(k) => k,
|
||||
None => return Err(self.err(ExpectedMapKey(idx))),
|
||||
};
|
||||
let val = Value::String(key.clone());
|
||||
f(&mut self.sub_decoder(Some(val), &key))
|
||||
}
|
||||
fn read_map_elt_val<T, F>(&mut self, idx: usize, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
match self.cur_map.next() {
|
||||
Some((key, value)) => {
|
||||
let mut d = self.sub_decoder(Some(value), &key);
|
||||
let ret = f(&mut d);
|
||||
if let Some(toml) = d.toml.take() {
|
||||
self.leftover_map.insert(key, toml);
|
||||
}
|
||||
ret
|
||||
}
|
||||
None => Err(self.err(ExpectedMapElement(idx))),
|
||||
}
|
||||
}
|
||||
|
||||
fn error(&mut self, err: &str) -> DecodeError {
|
||||
DecodeError {
|
||||
field: self.cur_field.clone(),
|
||||
kind: ApplicationError(err.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rustc_serialize::Decodable;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use {Parser, Decoder, Value};
|
||||
|
||||
#[test]
|
||||
fn bad_enum_chooses_longest_error() {
|
||||
#[derive(RustcDecodable)]
|
||||
#[allow(dead_code)]
|
||||
struct Foo {
|
||||
wut: HashMap<String, Bar>,
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable)]
|
||||
enum Bar {
|
||||
Simple(String),
|
||||
Detailed(Baz),
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable, Debug)]
|
||||
struct Baz {
|
||||
features: Vec<String>,
|
||||
}
|
||||
|
||||
let s = r#"
|
||||
[wut]
|
||||
a = { features = "" }
|
||||
"#;
|
||||
let v = Parser::new(s).parse().unwrap();
|
||||
let mut d = Decoder::new(Value::Table(v));
|
||||
let err = match Foo::decode(&mut d) {
|
||||
Ok(_) => panic!("expected error"),
|
||||
Err(e) => e,
|
||||
};
|
||||
assert_eq!(err.field.as_ref().unwrap(), "wut.a.features");
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,773 @@
|
|||
use serde::de;
|
||||
use Value;
|
||||
use super::{Decoder, DecodeError, DecodeErrorKind};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
macro_rules! forward_to_deserialize {
|
||||
($(
|
||||
$name:ident ( $( $arg:ident : $ty:ty ),* );
|
||||
)*) => {
|
||||
$(
|
||||
forward_to_deserialize!{
|
||||
func: $name ( $( $arg: $ty ),* );
|
||||
}
|
||||
)*
|
||||
};
|
||||
|
||||
(func: deserialize_enum ( $( $arg:ident : $ty:ty ),* );) => {
|
||||
fn deserialize_enum<V>(
|
||||
&mut self,
|
||||
$(_: $ty,)*
|
||||
_visitor: V,
|
||||
) -> ::std::result::Result<V::Value, Self::Error>
|
||||
where V: ::serde::de::EnumVisitor
|
||||
{
|
||||
Err(::serde::de::Error::invalid_type(::serde::de::Type::Enum))
|
||||
}
|
||||
};
|
||||
|
||||
(func: $name:ident ( $( $arg:ident : $ty:ty ),* );) => {
|
||||
#[inline]
|
||||
fn $name<V>(
|
||||
&mut self,
|
||||
$(_: $ty,)*
|
||||
visitor: V,
|
||||
) -> ::std::result::Result<V::Value, Self::Error>
|
||||
where V: ::serde::de::Visitor
|
||||
{
|
||||
self.deserialize(visitor)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl de::Deserializer for Decoder {
|
||||
type Error = DecodeError;
|
||||
|
||||
fn deserialize<V>(&mut self, mut visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor
|
||||
{
|
||||
match self.toml.take() {
|
||||
Some(Value::String(s)) => visitor.visit_string(s),
|
||||
Some(Value::Integer(i)) => visitor.visit_i64(i),
|
||||
Some(Value::Float(f)) => visitor.visit_f64(f),
|
||||
Some(Value::Boolean(b)) => visitor.visit_bool(b),
|
||||
Some(Value::Datetime(s)) => visitor.visit_string(s),
|
||||
Some(Value::Array(a)) => {
|
||||
let len = a.len();
|
||||
let iter = a.into_iter();
|
||||
visitor.visit_seq(SeqDeserializer::new(iter, len, &mut self.toml))
|
||||
}
|
||||
Some(Value::Table(t)) => {
|
||||
visitor.visit_map(MapVisitor {
|
||||
iter: t.into_iter(),
|
||||
de: self,
|
||||
key: None,
|
||||
value: None,
|
||||
})
|
||||
}
|
||||
None => Err(self.err(DecodeErrorKind::EndOfStream)),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_bool<V>(&mut self, mut visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor
|
||||
{
|
||||
match self.toml.take() {
|
||||
Some(Value::Boolean(b)) => visitor.visit_bool(b),
|
||||
ref found => Err(self.mismatch("bool", found)),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_i8<V>(&mut self, visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor
|
||||
{
|
||||
self.deserialize_i64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i16<V>(&mut self, visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor
|
||||
{
|
||||
self.deserialize_i64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i32<V>(&mut self, visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor
|
||||
{
|
||||
self.deserialize_i64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i64<V>(&mut self, mut visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor
|
||||
{
|
||||
match self.toml.take() {
|
||||
Some(Value::Integer(f)) => visitor.visit_i64(f),
|
||||
ref found => Err(self.mismatch("integer", found)),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_isize<V>(&mut self, visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor
|
||||
{
|
||||
self.deserialize_i64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u8<V>(&mut self, visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor
|
||||
{
|
||||
self.deserialize_i64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u16<V>(&mut self, visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor
|
||||
{
|
||||
self.deserialize_i64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u32<V>(&mut self, visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor
|
||||
{
|
||||
self.deserialize_i64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u64<V>(&mut self, visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor
|
||||
{
|
||||
self.deserialize_i64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_usize<V>(&mut self, visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor
|
||||
{
|
||||
self.deserialize_i64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_f32<V>(&mut self, visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor
|
||||
{
|
||||
self.deserialize_f64(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_f64<V>(&mut self, mut visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor
|
||||
{
|
||||
match self.toml.take() {
|
||||
Some(Value::Float(f)) => visitor.visit_f64(f),
|
||||
ref found => Err(self.mismatch("float", found)),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_str<V>(&mut self, mut visitor: V)
|
||||
-> Result<V::Value, Self::Error>
|
||||
where V: de::Visitor,
|
||||
{
|
||||
match self.toml.take() {
|
||||
Some(Value::String(s)) => visitor.visit_string(s),
|
||||
ref found => Err(self.mismatch("string", found)),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_string<V>(&mut self, visitor: V)
|
||||
-> Result<V::Value, Self::Error>
|
||||
where V: de::Visitor,
|
||||
{
|
||||
self.deserialize_str(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_char<V>(&mut self, mut visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor
|
||||
{
|
||||
match self.toml.take() {
|
||||
Some(Value::String(ref s)) if s.chars().count() == 1 => {
|
||||
visitor.visit_char(s.chars().next().unwrap())
|
||||
}
|
||||
ref found => return Err(self.mismatch("string", found)),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_option<V>(&mut self, mut visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor
|
||||
{
|
||||
if self.toml.is_none() {
|
||||
visitor.visit_none()
|
||||
} else {
|
||||
visitor.visit_some(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_seq<V>(&mut self, mut visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor,
|
||||
{
|
||||
if self.toml.is_none() {
|
||||
let iter = None::<i32>.into_iter();
|
||||
visitor.visit_seq(de::value::SeqDeserializer::new(iter, 0))
|
||||
} else {
|
||||
self.deserialize(visitor)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_map<V>(&mut self, mut visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor,
|
||||
{
|
||||
match self.toml.take() {
|
||||
Some(Value::Table(t)) => {
|
||||
visitor.visit_map(MapVisitor {
|
||||
iter: t.into_iter(),
|
||||
de: self,
|
||||
key: None,
|
||||
value: None,
|
||||
})
|
||||
}
|
||||
ref found => Err(self.mismatch("table", found)),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_enum<V>(&mut self,
|
||||
_enum: &str,
|
||||
variants: &[&str],
|
||||
mut visitor: V) -> Result<V::Value, DecodeError>
|
||||
where V: de::EnumVisitor,
|
||||
{
|
||||
// When decoding enums, this crate takes the strategy of trying to
|
||||
// decode the current TOML as all of the possible variants, returning
|
||||
// success on the first one that succeeds.
|
||||
//
|
||||
// Note that fidelity of the errors returned here is a little nebulous,
|
||||
// but we try to return the error that had the relevant field as the
|
||||
// longest field. This way we hopefully match an error against what was
|
||||
// most likely being written down without losing too much info.
|
||||
let mut first_error = None::<DecodeError>;
|
||||
|
||||
for variant in 0..variants.len() {
|
||||
let mut de = VariantVisitor {
|
||||
de: self.sub_decoder(self.toml.clone(), ""),
|
||||
variant: variant,
|
||||
};
|
||||
|
||||
match visitor.visit(&mut de) {
|
||||
Ok(value) => {
|
||||
self.toml = de.de.toml;
|
||||
return Ok(value);
|
||||
}
|
||||
Err(e) => {
|
||||
if let Some(ref first) = first_error {
|
||||
let my_len = e.field.as_ref().map(|s| s.len());
|
||||
let first_len = first.field.as_ref().map(|s| s.len());
|
||||
if my_len <= first_len {
|
||||
continue
|
||||
}
|
||||
}
|
||||
first_error = Some(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(first_error.unwrap_or_else(|| self.err(DecodeErrorKind::NoEnumVariants)))
|
||||
}
|
||||
|
||||
// When #[derive(Deserialize)] encounters an unknown struct field it will
|
||||
// call this method (somehow), and we want to preserve all unknown struct
|
||||
// fields to return them upwards (to warn about unused keys), so we override
|
||||
// that here to not tamper with our own internal state.
|
||||
fn deserialize_ignored_any<V>(&mut self, visitor: V)
|
||||
-> Result<V::Value, Self::Error>
|
||||
where V: de::Visitor
|
||||
{
|
||||
use serde::de::value::ValueDeserializer;
|
||||
let mut d = <() as ValueDeserializer<Self::Error>>::into_deserializer(());
|
||||
d.deserialize(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_bytes<V>(&mut self, visitor: V)
|
||||
-> Result<V::Value, Self::Error>
|
||||
where V: de::Visitor
|
||||
{
|
||||
self.deserialize_seq(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_seq_fixed_size<V>(&mut self, _len: usize, visitor: V)
|
||||
-> Result<V::Value, Self::Error>
|
||||
where V: de::Visitor
|
||||
{
|
||||
self.deserialize_seq(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_newtype_struct<V>(&mut self, _name: &'static str, visitor: V)
|
||||
-> Result<V::Value, Self::Error>
|
||||
where V: de::Visitor
|
||||
{
|
||||
self.deserialize_seq(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_tuple_struct<V>(&mut self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
visitor: V)
|
||||
-> Result<V::Value, Self::Error>
|
||||
where V: de::Visitor
|
||||
{
|
||||
self.deserialize_seq(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_struct<V>(&mut self,
|
||||
_name: &'static str,
|
||||
_fields: &'static [&'static str],
|
||||
visitor: V)
|
||||
-> Result<V::Value, Self::Error>
|
||||
where V: de::Visitor
|
||||
{
|
||||
self.deserialize_map(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_tuple<V>(&mut self,
|
||||
_len: usize,
|
||||
visitor: V)
|
||||
-> Result<V::Value, Self::Error>
|
||||
where V: de::Visitor
|
||||
{
|
||||
self.deserialize_seq(visitor)
|
||||
}
|
||||
|
||||
forward_to_deserialize!{
|
||||
deserialize_unit();
|
||||
deserialize_unit_struct(name: &'static str);
|
||||
deserialize_struct_field();
|
||||
}
|
||||
}
|
||||
|
||||
struct VariantVisitor {
|
||||
de: Decoder,
|
||||
variant: usize,
|
||||
}
|
||||
|
||||
impl de::VariantVisitor for VariantVisitor {
|
||||
type Error = DecodeError;
|
||||
|
||||
fn visit_variant<V>(&mut self) -> Result<V, DecodeError>
|
||||
where V: de::Deserialize
|
||||
{
|
||||
use serde::de::value::ValueDeserializer;
|
||||
|
||||
let mut de = self.variant.into_deserializer();
|
||||
|
||||
de::Deserialize::deserialize(&mut de)
|
||||
}
|
||||
|
||||
fn visit_unit(&mut self) -> Result<(), DecodeError> {
|
||||
de::Deserialize::deserialize(&mut self.de)
|
||||
}
|
||||
|
||||
fn visit_newtype<T>(&mut self) -> Result<T, DecodeError>
|
||||
where T: de::Deserialize,
|
||||
{
|
||||
de::Deserialize::deserialize(&mut self.de)
|
||||
}
|
||||
|
||||
fn visit_tuple<V>(&mut self,
|
||||
_len: usize,
|
||||
visitor: V) -> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor,
|
||||
{
|
||||
de::Deserializer::deserialize(&mut self.de, visitor)
|
||||
}
|
||||
|
||||
fn visit_struct<V>(&mut self,
|
||||
_fields: &'static [&'static str],
|
||||
visitor: V) -> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor,
|
||||
{
|
||||
de::Deserializer::deserialize(&mut self.de, visitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct SeqDeserializer<'a, I> {
|
||||
iter: I,
|
||||
len: usize,
|
||||
toml: &'a mut Option<Value>,
|
||||
}
|
||||
|
||||
impl<'a, I> SeqDeserializer<'a, I> where I: Iterator<Item=Value> {
|
||||
fn new(iter: I, len: usize, toml: &'a mut Option<Value>) -> Self {
|
||||
SeqDeserializer {
|
||||
iter: iter,
|
||||
len: len,
|
||||
toml: toml,
|
||||
}
|
||||
}
|
||||
|
||||
fn put_value_back(&mut self, v: Value) {
|
||||
*self.toml = self.toml.take().or(Some(Value::Array(Vec::new())));
|
||||
match self.toml.as_mut().unwrap() {
|
||||
&mut Value::Array(ref mut a) => {
|
||||
a.push(v);
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, I> de::Deserializer for SeqDeserializer<'a, I>
|
||||
where I: Iterator<Item=Value>,
|
||||
{
|
||||
type Error = DecodeError;
|
||||
|
||||
fn deserialize<V>(&mut self, mut visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor,
|
||||
{
|
||||
visitor.visit_seq(self)
|
||||
}
|
||||
|
||||
forward_to_deserialize!{
|
||||
deserialize_bool();
|
||||
deserialize_usize();
|
||||
deserialize_u8();
|
||||
deserialize_u16();
|
||||
deserialize_u32();
|
||||
deserialize_u64();
|
||||
deserialize_isize();
|
||||
deserialize_i8();
|
||||
deserialize_i16();
|
||||
deserialize_i32();
|
||||
deserialize_i64();
|
||||
deserialize_f32();
|
||||
deserialize_f64();
|
||||
deserialize_char();
|
||||
deserialize_str();
|
||||
deserialize_string();
|
||||
deserialize_unit();
|
||||
deserialize_option();
|
||||
deserialize_seq();
|
||||
deserialize_seq_fixed_size(len: usize);
|
||||
deserialize_bytes();
|
||||
deserialize_map();
|
||||
deserialize_unit_struct(name: &'static str);
|
||||
deserialize_newtype_struct(name: &'static str);
|
||||
deserialize_tuple_struct(name: &'static str, len: usize);
|
||||
deserialize_struct(name: &'static str, fields: &'static [&'static str]);
|
||||
deserialize_struct_field();
|
||||
deserialize_tuple(len: usize);
|
||||
deserialize_enum(name: &'static str, variants: &'static [&'static str]);
|
||||
deserialize_ignored_any();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, I> de::SeqVisitor for SeqDeserializer<'a, I>
|
||||
where I: Iterator<Item=Value>
|
||||
{
|
||||
type Error = DecodeError;
|
||||
|
||||
fn visit<V>(&mut self) -> Result<Option<V>, DecodeError>
|
||||
where V: de::Deserialize
|
||||
{
|
||||
match self.iter.next() {
|
||||
Some(value) => {
|
||||
self.len -= 1;
|
||||
let mut de = Decoder::new(value);
|
||||
let v = try!(de::Deserialize::deserialize(&mut de));
|
||||
if let Some(t) = de.toml {
|
||||
self.put_value_back(t);
|
||||
}
|
||||
Ok(Some(v))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn end(&mut self) -> Result<(), DecodeError> {
|
||||
if self.len == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(de::Error::end_of_stream())
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.len, Some(self.len))
|
||||
}
|
||||
}
|
||||
|
||||
impl de::Error for DecodeError {
|
||||
fn custom<T: Into<String>>(msg: T) -> DecodeError {
|
||||
DecodeError {
|
||||
field: None,
|
||||
kind: DecodeErrorKind::CustomError(msg.into()),
|
||||
}
|
||||
}
|
||||
fn end_of_stream() -> DecodeError {
|
||||
DecodeError { field: None, kind: DecodeErrorKind::EndOfStream }
|
||||
}
|
||||
fn missing_field(name: &'static str) -> DecodeError {
|
||||
DecodeError {
|
||||
field: Some(name.to_string()),
|
||||
kind: DecodeErrorKind::ExpectedField(None),
|
||||
}
|
||||
}
|
||||
fn unknown_field(name: &str) -> DecodeError {
|
||||
DecodeError {
|
||||
field: Some(name.to_string()),
|
||||
kind: DecodeErrorKind::UnknownField,
|
||||
}
|
||||
}
|
||||
fn invalid_type(ty: de::Type) -> Self {
|
||||
DecodeError {
|
||||
field: None,
|
||||
kind: DecodeErrorKind::InvalidType(match ty {
|
||||
de::Type::Bool => "bool",
|
||||
de::Type::Usize |
|
||||
de::Type::U8 |
|
||||
de::Type::U16 |
|
||||
de::Type::U32 |
|
||||
de::Type::U64 |
|
||||
de::Type::Isize |
|
||||
de::Type::I8 |
|
||||
de::Type::I16 |
|
||||
de::Type::I32 |
|
||||
de::Type::I64 => "integer",
|
||||
de::Type::F32 |
|
||||
de::Type::F64 => "float",
|
||||
de::Type::Char |
|
||||
de::Type::Str |
|
||||
de::Type::String => "string",
|
||||
de::Type::Seq => "array",
|
||||
de::Type::Struct |
|
||||
de::Type::Map => "table",
|
||||
de::Type::Unit => "Unit",
|
||||
de::Type::Option => "Option",
|
||||
de::Type::UnitStruct => "UnitStruct",
|
||||
de::Type::NewtypeStruct => "NewtypeStruct",
|
||||
de::Type::TupleStruct => "TupleStruct",
|
||||
de::Type::FieldName => "FieldName",
|
||||
de::Type::Tuple => "Tuple",
|
||||
de::Type::Enum => "Enum",
|
||||
de::Type::VariantName => "VariantName",
|
||||
de::Type::StructVariant => "StructVariant",
|
||||
de::Type::TupleVariant => "TupleVariant",
|
||||
de::Type::UnitVariant => "UnitVariant",
|
||||
de::Type::Bytes => "Bytes",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MapVisitor<'a, I> {
|
||||
iter: I,
|
||||
de: &'a mut Decoder,
|
||||
key: Option<String>,
|
||||
value: Option<Value>,
|
||||
}
|
||||
|
||||
impl<'a, I> MapVisitor<'a, I> {
|
||||
fn put_value_back(&mut self, v: Value) {
|
||||
self.de.toml = self.de.toml.take().or_else(|| {
|
||||
Some(Value::Table(BTreeMap::new()))
|
||||
});
|
||||
|
||||
match self.de.toml.as_mut().unwrap() {
|
||||
&mut Value::Table(ref mut t) => {
|
||||
t.insert(self.key.take().unwrap(), v);
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, I> de::MapVisitor for MapVisitor<'a, I>
|
||||
where I: Iterator<Item=(String, Value)>
|
||||
{
|
||||
type Error = DecodeError;
|
||||
|
||||
fn visit_key<K>(&mut self) -> Result<Option<K>, DecodeError>
|
||||
where K: de::Deserialize
|
||||
{
|
||||
while let Some((k, v)) = self.iter.next() {
|
||||
let mut dec = self.de.sub_decoder(Some(Value::String(k.clone())), &k);
|
||||
self.key = Some(k);
|
||||
|
||||
match de::Deserialize::deserialize(&mut dec) {
|
||||
Ok(val) => {
|
||||
self.value = Some(v);
|
||||
return Ok(Some(val))
|
||||
}
|
||||
|
||||
// If this was an unknown field, then we put the toml value
|
||||
// back into the map and keep going.
|
||||
Err(DecodeError {kind: DecodeErrorKind::UnknownField, ..}) => {
|
||||
self.put_value_back(v);
|
||||
}
|
||||
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn visit_value<V>(&mut self) -> Result<V, DecodeError>
|
||||
where V: de::Deserialize
|
||||
{
|
||||
match self.value.take() {
|
||||
Some(t) => {
|
||||
let mut dec = {
|
||||
// Borrowing the key here because Rust doesn't have
|
||||
// non-lexical borrows yet.
|
||||
let key = match self.key {
|
||||
Some(ref key) => &**key,
|
||||
None => ""
|
||||
};
|
||||
|
||||
self.de.sub_decoder(Some(t), key)
|
||||
};
|
||||
let v = try!(de::Deserialize::deserialize(&mut dec));
|
||||
if let Some(t) = dec.toml {
|
||||
self.put_value_back(t);
|
||||
}
|
||||
Ok(v)
|
||||
},
|
||||
None => Err(de::Error::end_of_stream())
|
||||
}
|
||||
}
|
||||
|
||||
fn end(&mut self) -> Result<(), DecodeError> {
|
||||
if let Some(v) = self.value.take() {
|
||||
self.put_value_back(v);
|
||||
}
|
||||
while let Some((k, v)) = self.iter.next() {
|
||||
self.key = Some(k);
|
||||
self.put_value_back(v);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn missing_field<V>(&mut self, field_name: &'static str)
|
||||
-> Result<V, DecodeError> where V: de::Deserialize {
|
||||
// See if the type can deserialize from a unit.
|
||||
match de::Deserialize::deserialize(&mut UnitDeserializer) {
|
||||
Err(DecodeError {
|
||||
kind: DecodeErrorKind::InvalidType(..),
|
||||
field,
|
||||
}) => Err(DecodeError {
|
||||
field: field.or(Some(field_name.to_string())),
|
||||
kind: DecodeErrorKind::ExpectedField(None),
|
||||
}),
|
||||
v => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct UnitDeserializer;
|
||||
|
||||
impl de::Deserializer for UnitDeserializer {
|
||||
type Error = DecodeError;
|
||||
|
||||
fn deserialize<V>(&mut self, mut visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor,
|
||||
{
|
||||
visitor.visit_unit()
|
||||
}
|
||||
|
||||
fn deserialize_option<V>(&mut self, mut visitor: V)
|
||||
-> Result<V::Value, DecodeError>
|
||||
where V: de::Visitor,
|
||||
{
|
||||
visitor.visit_none()
|
||||
}
|
||||
|
||||
forward_to_deserialize!{
|
||||
deserialize_bool();
|
||||
deserialize_usize();
|
||||
deserialize_u8();
|
||||
deserialize_u16();
|
||||
deserialize_u32();
|
||||
deserialize_u64();
|
||||
deserialize_isize();
|
||||
deserialize_i8();
|
||||
deserialize_i16();
|
||||
deserialize_i32();
|
||||
deserialize_i64();
|
||||
deserialize_f32();
|
||||
deserialize_f64();
|
||||
deserialize_char();
|
||||
deserialize_str();
|
||||
deserialize_string();
|
||||
deserialize_unit();
|
||||
deserialize_seq();
|
||||
deserialize_seq_fixed_size(len: usize);
|
||||
deserialize_bytes();
|
||||
deserialize_map();
|
||||
deserialize_unit_struct(name: &'static str);
|
||||
deserialize_newtype_struct(name: &'static str);
|
||||
deserialize_tuple_struct(name: &'static str, len: usize);
|
||||
deserialize_struct(name: &'static str, fields: &'static [&'static str]);
|
||||
deserialize_struct_field();
|
||||
deserialize_tuple(len: usize);
|
||||
deserialize_enum(name: &'static str, variants: &'static [&'static str]);
|
||||
deserialize_ignored_any();
|
||||
}
|
||||
}
|
||||
|
||||
impl de::Deserialize for Value {
|
||||
fn deserialize<D>(deserializer: &mut D) -> Result<Value, D::Error>
|
||||
where D: de::Deserializer
|
||||
{
|
||||
struct ValueVisitor;
|
||||
|
||||
impl de::Visitor for ValueVisitor {
|
||||
type Value = Value;
|
||||
|
||||
fn visit_bool<E>(&mut self, value: bool) -> Result<Value, E> {
|
||||
Ok(Value::Boolean(value))
|
||||
}
|
||||
|
||||
fn visit_i64<E>(&mut self, value: i64) -> Result<Value, E> {
|
||||
Ok(Value::Integer(value))
|
||||
}
|
||||
|
||||
fn visit_f64<E>(&mut self, value: f64) -> Result<Value, E> {
|
||||
Ok(Value::Float(value))
|
||||
}
|
||||
|
||||
fn visit_str<E>(&mut self, value: &str) -> Result<Value, E> {
|
||||
Ok(Value::String(value.into()))
|
||||
}
|
||||
|
||||
fn visit_string<E>(&mut self, value: String) -> Result<Value, E> {
|
||||
Ok(Value::String(value))
|
||||
}
|
||||
|
||||
fn visit_seq<V>(&mut self, visitor: V) -> Result<Value, V::Error>
|
||||
where V: de::SeqVisitor
|
||||
{
|
||||
let values = try!(de::impls::VecVisitor::new().visit_seq(visitor));
|
||||
Ok(Value::Array(values))
|
||||
}
|
||||
|
||||
fn visit_map<V>(&mut self, visitor: V) -> Result<Value, V::Error>
|
||||
where V: de::MapVisitor
|
||||
{
|
||||
let mut v = de::impls::BTreeMapVisitor::new();
|
||||
let values = try!(v.visit_map(visitor));
|
||||
Ok(Value::Table(values))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize(ValueVisitor)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
use std::fmt;
|
||||
|
||||
use Table as TomlTable;
|
||||
use Value::{self, String, Integer, Float, Boolean, Datetime, Array, Table};
|
||||
|
||||
struct Printer<'a, 'b:'a> {
|
||||
output: &'a mut fmt::Formatter<'b>,
|
||||
stack: Vec<&'a str>,
|
||||
}
|
||||
|
||||
struct Key<'a>(&'a [&'a str]);
|
||||
|
||||
impl fmt::Display for Value {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
String(ref s) => write_str(f, s),
|
||||
Integer(i) => write!(f, "{}", i),
|
||||
Float(fp) => {
|
||||
try!(write!(f, "{}", fp));
|
||||
if fp % 1.0 == 0.0 { try!(write!(f, ".0")) }
|
||||
Ok(())
|
||||
}
|
||||
Boolean(b) => write!(f, "{}", b),
|
||||
Datetime(ref s) => write!(f, "{}", s),
|
||||
Table(ref t) => {
|
||||
let mut p = Printer { output: f, stack: Vec::new() };
|
||||
p.print(t)
|
||||
}
|
||||
Array(ref a) => {
|
||||
try!(write!(f, "["));
|
||||
for (i, v) in a.iter().enumerate() {
|
||||
if i != 0 { try!(write!(f, ", ")); }
|
||||
try!(write!(f, "{}", v));
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_str(f: &mut fmt::Formatter, s: &str) -> fmt::Result {
|
||||
try!(write!(f, "\""));
|
||||
for ch in s.chars() {
|
||||
match ch {
|
||||
'\u{8}' => try!(write!(f, "\\b")),
|
||||
'\u{9}' => try!(write!(f, "\\t")),
|
||||
'\u{a}' => try!(write!(f, "\\n")),
|
||||
'\u{c}' => try!(write!(f, "\\f")),
|
||||
'\u{d}' => try!(write!(f, "\\r")),
|
||||
'\u{22}' => try!(write!(f, "\\\"")),
|
||||
'\u{5c}' => try!(write!(f, "\\\\")),
|
||||
ch => try!(write!(f, "{}", ch)),
|
||||
}
|
||||
}
|
||||
write!(f, "\"")
|
||||
}
|
||||
|
||||
impl<'a, 'b> Printer<'a, 'b> {
|
||||
fn print(&mut self, table: &'a TomlTable) -> fmt::Result {
|
||||
let mut space_out_first = false;
|
||||
for (k, v) in table.iter() {
|
||||
match *v {
|
||||
Table(..) => continue,
|
||||
Array(ref a) => {
|
||||
if let Some(&Table(..)) = a.first() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
space_out_first = true;
|
||||
try!(writeln!(self.output, "{} = {}", Key(&[k]), v));
|
||||
}
|
||||
for (i, (k, v)) in table.iter().enumerate() {
|
||||
match *v {
|
||||
Table(ref inner) => {
|
||||
self.stack.push(k);
|
||||
if space_out_first || i != 0 {
|
||||
try!(write!(self.output, "\n"));
|
||||
}
|
||||
try!(writeln!(self.output, "[{}]", Key(&self.stack)));
|
||||
try!(self.print(inner));
|
||||
self.stack.pop();
|
||||
}
|
||||
Array(ref inner) => {
|
||||
match inner.first() {
|
||||
Some(&Table(..)) => {}
|
||||
_ => continue
|
||||
}
|
||||
self.stack.push(k);
|
||||
for (j, inner) in inner.iter().enumerate() {
|
||||
if space_out_first || i != 0 || j != 0 {
|
||||
try!(write!(self.output, "\n"));
|
||||
}
|
||||
try!(writeln!(self.output, "[[{}]]", Key(&self.stack)));
|
||||
match *inner {
|
||||
Table(ref inner) => try!(self.print(inner)),
|
||||
_ => panic!("non-heterogeneous toml array"),
|
||||
}
|
||||
}
|
||||
self.stack.pop();
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for Key<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
for (i, part) in self.0.iter().enumerate() {
|
||||
if i != 0 { try!(write!(f, ".")); }
|
||||
let ok = part.chars().all(|c| {
|
||||
match c {
|
||||
'a' ... 'z' |
|
||||
'A' ... 'Z' |
|
||||
'0' ... '9' |
|
||||
'-' | '_' => true,
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
if ok {
|
||||
try!(write!(f, "{}", part));
|
||||
} else {
|
||||
try!(write_str(f, part));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(warnings)]
|
||||
mod tests {
|
||||
use Value;
|
||||
use Value::{String, Integer, Float, Boolean, Datetime, Array, Table};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
macro_rules! map( ($($k:expr => $v:expr),*) => ({
|
||||
let mut _m = BTreeMap::new();
|
||||
$(_m.insert($k.to_string(), $v);)*
|
||||
_m
|
||||
}) );
|
||||
|
||||
#[test]
|
||||
fn simple_show() {
|
||||
assert_eq!(String("foo".to_string()).to_string(),
|
||||
"\"foo\"");
|
||||
assert_eq!(Integer(10).to_string(),
|
||||
"10");
|
||||
assert_eq!(Float(10.0).to_string(),
|
||||
"10.0");
|
||||
assert_eq!(Float(2.4).to_string(),
|
||||
"2.4");
|
||||
assert_eq!(Boolean(true).to_string(),
|
||||
"true");
|
||||
assert_eq!(Datetime("test".to_string()).to_string(),
|
||||
"test");
|
||||
assert_eq!(Array(vec![]).to_string(),
|
||||
"[]");
|
||||
assert_eq!(Array(vec![Integer(1), Integer(2)]).to_string(),
|
||||
"[1, 2]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table() {
|
||||
assert_eq!(Table(map! { }).to_string(),
|
||||
"");
|
||||
assert_eq!(Table(map! { "test" => Integer(2) }).to_string(),
|
||||
"test = 2\n");
|
||||
assert_eq!(Table(map! {
|
||||
"test" => Integer(2),
|
||||
"test2" => Table(map! {
|
||||
"test" => String("wut".to_string())
|
||||
})
|
||||
}).to_string(),
|
||||
"test = 2\n\
|
||||
\n\
|
||||
[test2]\n\
|
||||
test = \"wut\"\n");
|
||||
assert_eq!(Table(map! {
|
||||
"test" => Integer(2),
|
||||
"test2" => Table(map! {
|
||||
"test" => String("wut".to_string())
|
||||
})
|
||||
}).to_string(),
|
||||
"test = 2\n\
|
||||
\n\
|
||||
[test2]\n\
|
||||
test = \"wut\"\n");
|
||||
assert_eq!(Table(map! {
|
||||
"test" => Integer(2),
|
||||
"test2" => Array(vec![Table(map! {
|
||||
"test" => String("wut".to_string())
|
||||
})])
|
||||
}).to_string(),
|
||||
"test = 2\n\
|
||||
\n\
|
||||
[[test2]]\n\
|
||||
test = \"wut\"\n");
|
||||
assert_eq!(Table(map! {
|
||||
"foo.bar" => Integer(2),
|
||||
"foo\"bar" => Integer(2)
|
||||
}).to_string(),
|
||||
"\"foo\\\"bar\" = 2\n\
|
||||
\"foo.bar\" = 2\n");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
|
||||
use {Value, Table};
|
||||
|
||||
#[cfg(feature = "rustc-serialize")] mod rustc_serialize;
|
||||
#[cfg(feature = "serde")] mod serde;
|
||||
|
||||
/// A structure to transform Rust values into TOML values.
|
||||
///
|
||||
/// This encoder implements the serialization `Encoder` interface, allowing
|
||||
/// `Encodable` rust types to be fed into the encoder. The output of this
|
||||
/// encoder is a TOML `Table` structure. The resulting TOML can be stringified
|
||||
/// if necessary.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// extern crate rustc_serialize;
|
||||
/// extern crate toml;
|
||||
///
|
||||
/// # fn main() {
|
||||
/// use toml::{Encoder, Value};
|
||||
/// use rustc_serialize::Encodable;
|
||||
///
|
||||
/// #[derive(RustcEncodable)]
|
||||
/// struct MyStruct { foo: isize, bar: String }
|
||||
/// let my_struct = MyStruct { foo: 4, bar: "hello!".to_string() };
|
||||
///
|
||||
/// let mut e = Encoder::new();
|
||||
/// my_struct.encode(&mut e).unwrap();
|
||||
///
|
||||
/// assert_eq!(e.toml.get(&"foo".to_string()), Some(&Value::Integer(4)))
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Encoder {
|
||||
/// Output TOML that is emitted. The current version of this encoder forces
|
||||
/// the top-level representation of a structure to be a table.
|
||||
///
|
||||
/// This field can be used to extract the return value after feeding a value
|
||||
/// into this `Encoder`.
|
||||
pub toml: Table,
|
||||
state: State,
|
||||
}
|
||||
|
||||
/// Enumeration of errors which can occur while encoding a rust value into a
|
||||
/// TOML value.
|
||||
#[allow(missing_copy_implementations)]
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Indication that a key was needed when a value was emitted, but no key
|
||||
/// was previously emitted.
|
||||
NeedsKey,
|
||||
/// Indication that a key was emitted, but no value was emitted.
|
||||
NoValue,
|
||||
/// Indicates that a map key was attempted to be emitted at an invalid
|
||||
/// location.
|
||||
InvalidMapKeyLocation,
|
||||
/// Indicates that a type other than a string was attempted to be used as a
|
||||
/// map key type.
|
||||
InvalidMapKeyType,
|
||||
/// A custom error type was generated
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
/// Internal state of the encoder when encoding transitions
|
||||
#[derive(Debug)]
|
||||
pub struct EncoderState {
|
||||
inner: State,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum State {
|
||||
Start,
|
||||
NextKey(String),
|
||||
NextArray(Vec<Value>),
|
||||
NextMapKey,
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
fn default() -> State { State::Start }
|
||||
}
|
||||
|
||||
impl Encoder {
|
||||
/// Constructs a new encoder which will emit to the given output stream.
|
||||
pub fn new() -> Encoder {
|
||||
Encoder { state: State::Start, toml: BTreeMap::new() }
|
||||
}
|
||||
|
||||
fn emit_value(&mut self, v: Value) -> Result<(), Error> {
|
||||
match mem::replace(&mut self.state, State::Start) {
|
||||
State::NextKey(key) => { self.toml.insert(key, v); Ok(()) }
|
||||
State::NextArray(mut vec) => {
|
||||
// TODO: validate types
|
||||
vec.push(v);
|
||||
self.state = State::NextArray(vec);
|
||||
Ok(())
|
||||
}
|
||||
State::NextMapKey => {
|
||||
match v {
|
||||
Value::String(s) => { self.state = State::NextKey(s); Ok(()) }
|
||||
_ => Err(Error::InvalidMapKeyType)
|
||||
}
|
||||
}
|
||||
_ => Err(Error::NeedsKey)
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_none(&mut self) -> Result<(), Error> {
|
||||
match mem::replace(&mut self.state, State::Start) {
|
||||
State::Start => unreachable!(),
|
||||
State::NextKey(_) => Ok(()),
|
||||
State::NextArray(..) => panic!("how to encode None in an array?"),
|
||||
State::NextMapKey => Err(Error::InvalidMapKeyLocation),
|
||||
}
|
||||
}
|
||||
|
||||
fn seq_begin(&mut self) -> Result<State, Error> {
|
||||
Ok(mem::replace(&mut self.state, State::NextArray(Vec::new())))
|
||||
}
|
||||
|
||||
fn seq_end(&mut self, old: State) -> Result<(), Error> {
|
||||
match mem::replace(&mut self.state, old) {
|
||||
State::NextArray(v) => self.emit_value(Value::Array(v)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn table_key<F>(&mut self, f: F) -> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
match mem::replace(&mut self.state, State::NextMapKey) {
|
||||
State::Start => {}
|
||||
_ => return Err(Error::InvalidMapKeyLocation),
|
||||
}
|
||||
try!(f(self));
|
||||
match self.state {
|
||||
State::NextKey(_) => Ok(()),
|
||||
_ => Err(Error::InvalidMapKeyLocation),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes an encodable value into a TOML value.
|
||||
///
|
||||
/// This function expects the type given to represent a TOML table in some form.
|
||||
/// If encoding encounters an error, then this function will fail the task.
|
||||
#[cfg(feature = "rustc-serialize")]
|
||||
pub fn encode<T: ::rustc_serialize::Encodable>(t: &T) -> Value {
|
||||
let mut e = Encoder::new();
|
||||
t.encode(&mut e).unwrap();
|
||||
Value::Table(e.toml)
|
||||
}
|
||||
|
||||
/// Encodes an encodable value into a TOML value.
|
||||
///
|
||||
/// This function expects the type given to represent a TOML table in some form.
|
||||
/// If encoding encounters an error, then this function will fail the task.
|
||||
#[cfg(all(not(feature = "rustc-serialize"), feature = "serde"))]
|
||||
pub fn encode<T: ::serde::Serialize>(t: &T) -> Value {
|
||||
let mut e = Encoder::new();
|
||||
t.serialize(&mut e).unwrap();
|
||||
Value::Table(e.toml)
|
||||
}
|
||||
|
||||
/// Encodes an encodable value into a TOML string.
|
||||
///
|
||||
/// This function expects the type given to represent a TOML table in some form.
|
||||
/// If encoding encounters an error, then this function will fail the task.
|
||||
#[cfg(feature = "rustc-serialize")]
|
||||
pub fn encode_str<T: ::rustc_serialize::Encodable>(t: &T) -> String {
|
||||
encode(t).to_string()
|
||||
}
|
||||
|
||||
/// Encodes an encodable value into a TOML string.
|
||||
///
|
||||
/// This function expects the type given to represent a TOML table in some form.
|
||||
/// If encoding encounters an error, then this function will fail the task.
|
||||
#[cfg(all(not(feature = "rustc-serialize"), feature = "serde"))]
|
||||
pub fn encode_str<T: ::serde::Serialize>(t: &T) -> String {
|
||||
encode(t).to_string()
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::NeedsKey => write!(f, "need a key to encode"),
|
||||
Error::NoValue => write!(f, "no value to emit for a previous key"),
|
||||
Error::InvalidMapKeyLocation => write!(f, "a map cannot be emitted \
|
||||
at this location"),
|
||||
Error::InvalidMapKeyType => write!(f, "only strings can be used as \
|
||||
key types"),
|
||||
Error::Custom(ref s) => write!(f, "custom error: {}", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str { "TOML encoding error" }
|
||||
}
|
|
@ -0,0 +1,748 @@
|
|||
use std::mem;
|
||||
|
||||
use rustc_serialize;
|
||||
use Value;
|
||||
use super::{Encoder, Error, State};
|
||||
use super::Error::*;
|
||||
|
||||
impl Encoder {
|
||||
fn table<F>(&mut self, f: F) -> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
match mem::replace(&mut self.state, State::Start) {
|
||||
State::NextKey(key) => {
|
||||
let mut nested = Encoder::new();
|
||||
try!(f(&mut nested));
|
||||
self.toml.insert(key, Value::Table(nested.toml));
|
||||
Ok(())
|
||||
}
|
||||
State::NextArray(mut arr) => {
|
||||
let mut nested = Encoder::new();
|
||||
try!(f(&mut nested));
|
||||
arr.push(Value::Table(nested.toml));
|
||||
self.state = State::NextArray(arr);
|
||||
Ok(())
|
||||
}
|
||||
State::Start => f(self),
|
||||
State::NextMapKey => Err(Error::InvalidMapKeyLocation),
|
||||
}
|
||||
}
|
||||
|
||||
fn seq<F>(&mut self, f: F) -> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
let old = try!(self.seq_begin());
|
||||
try!(f(self));
|
||||
self.seq_end(old)
|
||||
}
|
||||
}
|
||||
|
||||
impl rustc_serialize::Encoder for Encoder {
|
||||
type Error = Error;
|
||||
|
||||
fn emit_nil(&mut self) -> Result<(), Error> { Ok(()) }
|
||||
fn emit_usize(&mut self, v: usize) -> Result<(), Error> {
|
||||
self.emit_i64(v as i64)
|
||||
}
|
||||
fn emit_u8(&mut self, v: u8) -> Result<(), Error> {
|
||||
self.emit_i64(v as i64)
|
||||
}
|
||||
fn emit_u16(&mut self, v: u16) -> Result<(), Error> {
|
||||
self.emit_i64(v as i64)
|
||||
}
|
||||
fn emit_u32(&mut self, v: u32) -> Result<(), Error> {
|
||||
self.emit_i64(v as i64)
|
||||
}
|
||||
fn emit_u64(&mut self, v: u64) -> Result<(), Error> {
|
||||
self.emit_i64(v as i64)
|
||||
}
|
||||
fn emit_isize(&mut self, v: isize) -> Result<(), Error> {
|
||||
self.emit_i64(v as i64)
|
||||
}
|
||||
fn emit_i8(&mut self, v: i8) -> Result<(), Error> {
|
||||
self.emit_i64(v as i64)
|
||||
}
|
||||
fn emit_i16(&mut self, v: i16) -> Result<(), Error> {
|
||||
self.emit_i64(v as i64)
|
||||
}
|
||||
fn emit_i32(&mut self, v: i32) -> Result<(), Error> {
|
||||
self.emit_i64(v as i64)
|
||||
}
|
||||
fn emit_i64(&mut self, v: i64) -> Result<(), Error> {
|
||||
self.emit_value(Value::Integer(v))
|
||||
}
|
||||
fn emit_bool(&mut self, v: bool) -> Result<(), Error> {
|
||||
self.emit_value(Value::Boolean(v))
|
||||
}
|
||||
fn emit_f32(&mut self, v: f32) -> Result<(), Error> { self.emit_f64(v as f64) }
|
||||
fn emit_f64(&mut self, v: f64) -> Result<(), Error> {
|
||||
self.emit_value(Value::Float(v))
|
||||
}
|
||||
fn emit_char(&mut self, v: char) -> Result<(), Error> {
|
||||
self.emit_str(&v.to_string())
|
||||
}
|
||||
fn emit_str(&mut self, v: &str) -> Result<(), Error> {
|
||||
self.emit_value(Value::String(v.to_string()))
|
||||
}
|
||||
fn emit_enum<F>(&mut self, _name: &str, f: F)
|
||||
-> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
f(self)
|
||||
}
|
||||
fn emit_enum_variant<F>(&mut self, _v_name: &str, _v_id: usize,
|
||||
_len: usize, f: F) -> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
f(self)
|
||||
}
|
||||
fn emit_enum_variant_arg<F>(&mut self, _a_idx: usize, f: F)
|
||||
-> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
f(self)
|
||||
}
|
||||
fn emit_enum_struct_variant<F>(&mut self, _v_name: &str, _v_id: usize,
|
||||
_len: usize,
|
||||
_f: F)
|
||||
-> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
panic!()
|
||||
}
|
||||
fn emit_enum_struct_variant_field<F>(&mut self,
|
||||
_f_name: &str,
|
||||
_f_idx: usize,
|
||||
_f: F)
|
||||
-> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
panic!()
|
||||
}
|
||||
fn emit_struct<F>(&mut self, _name: &str, _len: usize, f: F)
|
||||
-> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
self.table(f)
|
||||
}
|
||||
fn emit_struct_field<F>(&mut self, f_name: &str, _f_idx: usize, f: F)
|
||||
-> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
let old = mem::replace(&mut self.state,
|
||||
State::NextKey(f_name.to_string()));
|
||||
try!(f(self));
|
||||
if self.state != State::Start {
|
||||
return Err(NoValue)
|
||||
}
|
||||
self.state = old;
|
||||
Ok(())
|
||||
}
|
||||
fn emit_tuple<F>(&mut self, len: usize, f: F)
|
||||
-> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
self.emit_seq(len, f)
|
||||
}
|
||||
fn emit_tuple_arg<F>(&mut self, idx: usize, f: F)
|
||||
-> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
self.emit_seq_elt(idx, f)
|
||||
}
|
||||
fn emit_tuple_struct<F>(&mut self, _name: &str, _len: usize, _f: F)
|
||||
-> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
fn emit_tuple_struct_arg<F>(&mut self, _f_idx: usize, _f: F)
|
||||
-> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
fn emit_option<F>(&mut self, f: F)
|
||||
-> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
f(self)
|
||||
}
|
||||
fn emit_option_none(&mut self) -> Result<(), Error> {
|
||||
self.emit_none()
|
||||
}
|
||||
fn emit_option_some<F>(&mut self, f: F) -> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
f(self)
|
||||
}
|
||||
fn emit_seq<F>(&mut self, _len: usize, f: F)
|
||||
-> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
self.seq(f)
|
||||
}
|
||||
fn emit_seq_elt<F>(&mut self, _idx: usize, f: F)
|
||||
-> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
f(self)
|
||||
}
|
||||
fn emit_map<F>(&mut self, len: usize, f: F)
|
||||
-> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
self.emit_struct("foo", len, f)
|
||||
}
|
||||
fn emit_map_elt_key<F>(&mut self, _idx: usize, f: F) -> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
self.table_key(f)
|
||||
}
|
||||
fn emit_map_elt_val<F>(&mut self, _idx: usize, f: F) -> Result<(), Error>
|
||||
where F: FnOnce(&mut Encoder) -> Result<(), Error>
|
||||
{
|
||||
f(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl rustc_serialize::Encodable for Value {
|
||||
fn encode<E>(&self, e: &mut E) -> Result<(), E::Error>
|
||||
where E: rustc_serialize::Encoder
|
||||
{
|
||||
match *self {
|
||||
Value::String(ref s) => e.emit_str(s),
|
||||
Value::Integer(i) => e.emit_i64(i),
|
||||
Value::Float(f) => e.emit_f64(f),
|
||||
Value::Boolean(b) => e.emit_bool(b),
|
||||
Value::Datetime(ref s) => e.emit_str(s),
|
||||
Value::Array(ref a) => {
|
||||
e.emit_seq(a.len(), |e| {
|
||||
for item in a {
|
||||
try!(item.encode(e));
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
Value::Table(ref t) => {
|
||||
e.emit_map(t.len(), |e| {
|
||||
for (i, (key, value)) in t.iter().enumerate() {
|
||||
try!(e.emit_map_elt_key(i, |e| e.emit_str(key)));
|
||||
try!(e.emit_map_elt_val(i, |e| value.encode(e)));
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use rustc_serialize::{self, Encodable, Decodable};
|
||||
|
||||
use {Encoder, Decoder, DecodeError};
|
||||
use Value;
|
||||
use Value::{Table, Integer, Array, Float};
|
||||
|
||||
macro_rules! encode( ($t:expr) => ({
|
||||
let mut e = Encoder::new();
|
||||
$t.encode(&mut e).unwrap();
|
||||
e.toml
|
||||
}) );
|
||||
|
||||
macro_rules! decode( ($t:expr) => ({
|
||||
let mut d = Decoder::new($t);
|
||||
Decodable::decode(&mut d).unwrap()
|
||||
}) );
|
||||
|
||||
macro_rules! map( ($($k:ident, $v:expr),*) => ({
|
||||
let mut _m = BTreeMap::new();
|
||||
$(_m.insert(stringify!($k).to_string(), $v);)*
|
||||
_m
|
||||
}) );
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { a: isize }
|
||||
|
||||
let v = Foo { a: 2 };
|
||||
assert_eq!(encode!(v), map! { a, Integer(2) });
|
||||
assert_eq!(v, decode!(Table(encode!(v))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke_hyphen() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { a_b: isize }
|
||||
|
||||
let v = Foo { a_b: 2 };
|
||||
assert_eq!(encode!(v), map! { a_b, Integer(2) });
|
||||
assert_eq!(v, decode!(Table(encode!(v))));
|
||||
|
||||
let mut m = BTreeMap::new();
|
||||
m.insert("a-b".to_string(), Integer(2));
|
||||
assert_eq!(v, decode!(Table(encode!(v))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { a: isize, b: Bar }
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Bar { a: String }
|
||||
|
||||
let v = Foo { a: 2, b: Bar { a: "test".to_string() } };
|
||||
assert_eq!(encode!(v),
|
||||
map! {
|
||||
a, Integer(2),
|
||||
b, Table(map! {
|
||||
a, Value::String("test".to_string())
|
||||
})
|
||||
});
|
||||
assert_eq!(v, decode!(Table(encode!(v))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn application_decode_error() {
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct Range10(usize);
|
||||
impl Decodable for Range10 {
|
||||
fn decode<D: rustc_serialize::Decoder>(d: &mut D) -> Result<Range10, D::Error> {
|
||||
let x: usize = try!(Decodable::decode(d));
|
||||
if x > 10 {
|
||||
Err(d.error("Value out of range!"))
|
||||
} else {
|
||||
Ok(Range10(x))
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut d_good = Decoder::new(Integer(5));
|
||||
let mut d_bad1 = Decoder::new(Value::String("not an isize".to_string()));
|
||||
let mut d_bad2 = Decoder::new(Integer(11));
|
||||
|
||||
assert_eq!(Ok(Range10(5)), Decodable::decode(&mut d_good));
|
||||
|
||||
let err1: Result<Range10, _> = Decodable::decode(&mut d_bad1);
|
||||
assert!(err1.is_err());
|
||||
let err2: Result<Range10, _> = Decodable::decode(&mut d_bad2);
|
||||
assert!(err2.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { a: Vec<isize> }
|
||||
|
||||
let v = Foo { a: vec![1, 2, 3, 4] };
|
||||
assert_eq!(encode!(v),
|
||||
map! {
|
||||
a, Array(vec![
|
||||
Integer(1),
|
||||
Integer(2),
|
||||
Integer(3),
|
||||
Integer(4)
|
||||
])
|
||||
});
|
||||
assert_eq!(v, decode!(Table(encode!(v))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tuple() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { a: (isize, isize, isize, isize) }
|
||||
|
||||
let v = Foo { a: (1, 2, 3, 4) };
|
||||
assert_eq!(encode!(v),
|
||||
map! {
|
||||
a, Array(vec![
|
||||
Integer(1),
|
||||
Integer(2),
|
||||
Integer(3),
|
||||
Integer(4)
|
||||
])
|
||||
});
|
||||
assert_eq!(v, decode!(Table(encode!(v))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inner_structs_with_options() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo {
|
||||
a: Option<Box<Foo>>,
|
||||
b: Bar,
|
||||
}
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Bar {
|
||||
a: String,
|
||||
b: f64,
|
||||
}
|
||||
|
||||
let v = Foo {
|
||||
a: Some(Box::new(Foo {
|
||||
a: None,
|
||||
b: Bar { a: "foo".to_string(), b: 4.5 },
|
||||
})),
|
||||
b: Bar { a: "bar".to_string(), b: 1.0 },
|
||||
};
|
||||
assert_eq!(encode!(v),
|
||||
map! {
|
||||
a, Table(map! {
|
||||
b, Table(map! {
|
||||
a, Value::String("foo".to_string()),
|
||||
b, Float(4.5)
|
||||
})
|
||||
}),
|
||||
b, Table(map! {
|
||||
a, Value::String("bar".to_string()),
|
||||
b, Float(1.0)
|
||||
})
|
||||
});
|
||||
assert_eq!(v, decode!(Table(encode!(v))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hashmap() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo {
|
||||
map: BTreeMap<String, isize>,
|
||||
set: HashSet<char>,
|
||||
}
|
||||
|
||||
let v = Foo {
|
||||
map: {
|
||||
let mut m = BTreeMap::new();
|
||||
m.insert("foo".to_string(), 10);
|
||||
m.insert("bar".to_string(), 4);
|
||||
m
|
||||
},
|
||||
set: {
|
||||
let mut s = HashSet::new();
|
||||
s.insert('a');
|
||||
s
|
||||
},
|
||||
};
|
||||
assert_eq!(encode!(v),
|
||||
map! {
|
||||
map, Table(map! {
|
||||
foo, Integer(10),
|
||||
bar, Integer(4)
|
||||
}),
|
||||
set, Array(vec![Value::String("a".to_string())])
|
||||
}
|
||||
);
|
||||
assert_eq!(v, decode!(Table(encode!(v))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tuple_struct() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo(isize, String, f64);
|
||||
|
||||
let v = Foo(1, "foo".to_string(), 4.5);
|
||||
assert_eq!(
|
||||
encode!(v),
|
||||
map! {
|
||||
_field0, Integer(1),
|
||||
_field1, Value::String("foo".to_string()),
|
||||
_field2, Float(4.5)
|
||||
}
|
||||
);
|
||||
assert_eq!(v, decode!(Table(encode!(v))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_array() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { a: Vec<Bar>, }
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Bar { a: isize }
|
||||
|
||||
let v = Foo { a: vec![Bar { a: 1 }, Bar { a: 2 }] };
|
||||
assert_eq!(
|
||||
encode!(v),
|
||||
map! {
|
||||
a, Array(vec![
|
||||
Table(map!{ a, Integer(1) }),
|
||||
Table(map!{ a, Integer(2) }),
|
||||
])
|
||||
}
|
||||
);
|
||||
assert_eq!(v, decode!(Table(encode!(v))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_errors() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { bar: isize }
|
||||
|
||||
let mut d = Decoder::new(Table(map! {
|
||||
bar, Float(1.0)
|
||||
}));
|
||||
let a: Result<Foo, DecodeError> = Decodable::decode(&mut d);
|
||||
match a {
|
||||
Ok(..) => panic!("should not have decoded"),
|
||||
Err(e) => {
|
||||
assert_eq!(e.to_string(),
|
||||
"expected a value of type `integer`, but \
|
||||
found a value of type `float` for the key `bar`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_errors() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { bar: isize }
|
||||
|
||||
let mut d = Decoder::new(Table(map! {
|
||||
}));
|
||||
let a: Result<Foo, DecodeError> = Decodable::decode(&mut d);
|
||||
match a {
|
||||
Ok(..) => panic!("should not have decoded"),
|
||||
Err(e) => {
|
||||
assert_eq!(e.to_string(),
|
||||
"expected a value of type `integer` for the key `bar`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_enum() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { a: E }
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
enum E {
|
||||
Bar(isize),
|
||||
Baz(f64),
|
||||
Last(Foo2),
|
||||
}
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo2 {
|
||||
test: String,
|
||||
}
|
||||
|
||||
let v = Foo { a: E::Bar(10) };
|
||||
assert_eq!(
|
||||
encode!(v),
|
||||
map! { a, Integer(10) }
|
||||
);
|
||||
assert_eq!(v, decode!(Table(encode!(v))));
|
||||
|
||||
let v = Foo { a: E::Baz(10.2) };
|
||||
assert_eq!(
|
||||
encode!(v),
|
||||
map! { a, Float(10.2) }
|
||||
);
|
||||
assert_eq!(v, decode!(Table(encode!(v))));
|
||||
|
||||
let v = Foo { a: E::Last(Foo2 { test: "test".to_string() }) };
|
||||
assert_eq!(
|
||||
encode!(v),
|
||||
map! { a, Table(map! { test, Value::String("test".to_string()) }) }
|
||||
);
|
||||
assert_eq!(v, decode!(Table(encode!(v))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unused_fields() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { a: isize }
|
||||
|
||||
let v = Foo { a: 2 };
|
||||
let mut d = Decoder::new(Table(map! {
|
||||
a, Integer(2),
|
||||
b, Integer(5)
|
||||
}));
|
||||
assert_eq!(v, Decodable::decode(&mut d).unwrap());
|
||||
|
||||
assert_eq!(d.toml, Some(Table(map! {
|
||||
b, Integer(5)
|
||||
})));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unused_fields2() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { a: Bar }
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Bar { a: isize }
|
||||
|
||||
let v = Foo { a: Bar { a: 2 } };
|
||||
let mut d = Decoder::new(Table(map! {
|
||||
a, Table(map! {
|
||||
a, Integer(2),
|
||||
b, Integer(5)
|
||||
})
|
||||
}));
|
||||
assert_eq!(v, Decodable::decode(&mut d).unwrap());
|
||||
|
||||
assert_eq!(d.toml, Some(Table(map! {
|
||||
a, Table(map! {
|
||||
b, Integer(5)
|
||||
})
|
||||
})));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unused_fields3() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { a: Bar }
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Bar { a: isize }
|
||||
|
||||
let v = Foo { a: Bar { a: 2 } };
|
||||
let mut d = Decoder::new(Table(map! {
|
||||
a, Table(map! {
|
||||
a, Integer(2)
|
||||
})
|
||||
}));
|
||||
assert_eq!(v, Decodable::decode(&mut d).unwrap());
|
||||
|
||||
assert_eq!(d.toml, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unused_fields4() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { a: BTreeMap<String, String> }
|
||||
|
||||
let v = Foo { a: map! { a, "foo".to_string() } };
|
||||
let mut d = Decoder::new(Table(map! {
|
||||
a, Table(map! {
|
||||
a, Value::String("foo".to_string())
|
||||
})
|
||||
}));
|
||||
assert_eq!(v, Decodable::decode(&mut d).unwrap());
|
||||
|
||||
assert_eq!(d.toml, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unused_fields5() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { a: Vec<String> }
|
||||
|
||||
let v = Foo { a: vec!["a".to_string()] };
|
||||
let mut d = Decoder::new(Table(map! {
|
||||
a, Array(vec![Value::String("a".to_string())])
|
||||
}));
|
||||
assert_eq!(v, Decodable::decode(&mut d).unwrap());
|
||||
|
||||
assert_eq!(d.toml, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unused_fields6() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { a: Option<Vec<String>> }
|
||||
|
||||
let v = Foo { a: Some(vec![]) };
|
||||
let mut d = Decoder::new(Table(map! {
|
||||
a, Array(vec![])
|
||||
}));
|
||||
assert_eq!(v, Decodable::decode(&mut d).unwrap());
|
||||
|
||||
assert_eq!(d.toml, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unused_fields7() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { a: Vec<Bar> }
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Bar { a: isize }
|
||||
|
||||
let v = Foo { a: vec![Bar { a: 1 }] };
|
||||
let mut d = Decoder::new(Table(map! {
|
||||
a, Array(vec![Table(map! {
|
||||
a, Integer(1),
|
||||
b, Integer(2)
|
||||
})])
|
||||
}));
|
||||
assert_eq!(v, Decodable::decode(&mut d).unwrap());
|
||||
|
||||
assert_eq!(d.toml, Some(Table(map! {
|
||||
a, Array(vec![Table(map! {
|
||||
b, Integer(2)
|
||||
})])
|
||||
})));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unused_fields8() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { a: BTreeMap<String, Bar> }
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Bar { a: isize }
|
||||
|
||||
let v = Foo { a: map! { a, Bar { a: 2 } } };
|
||||
let mut d = Decoder::new(Table(map! {
|
||||
a, Table(map! {
|
||||
a, Table(map! {
|
||||
a, Integer(2),
|
||||
b, Integer(2)
|
||||
})
|
||||
})
|
||||
}));
|
||||
assert_eq!(v, Decodable::decode(&mut d).unwrap());
|
||||
|
||||
assert_eq!(d.toml, Some(Table(map! {
|
||||
a, Table(map! {
|
||||
a, Table(map! {
|
||||
b, Integer(2)
|
||||
})
|
||||
})
|
||||
})));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_arrays() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { a: Vec<Bar> }
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Bar;
|
||||
|
||||
let v = Foo { a: vec![] };
|
||||
let mut d = Decoder::new(Table(map! {}));
|
||||
assert_eq!(v, Decodable::decode(&mut d).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_arrays2() {
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Foo { a: Option<Vec<Bar>> }
|
||||
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
|
||||
struct Bar;
|
||||
|
||||
let v = Foo { a: None };
|
||||
let mut d = Decoder::new(Table(map! {}));
|
||||
assert_eq!(v, Decodable::decode(&mut d).unwrap());
|
||||
|
||||
let v = Foo { a: Some(vec![]) };
|
||||
let mut d = Decoder::new(Table(map! {
|
||||
a, Array(vec![])
|
||||
}));
|
||||
assert_eq!(v, Decodable::decode(&mut d).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
let toml = r#"
|
||||
[test]
|
||||
foo = "bar"
|
||||
|
||||
[[values]]
|
||||
foo = "baz"
|
||||
|
||||
[[values]]
|
||||
foo = "qux"
|
||||
"#;
|
||||
|
||||
let value: Value = toml.parse().unwrap();
|
||||
let val2 = ::encode_str(&value).parse().unwrap();
|
||||
assert_eq!(value, val2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,339 @@
|
|||
use std::mem;
|
||||
|
||||
use serde::ser;
|
||||
use Value;
|
||||
use super::{Encoder, Error, EncoderState, State};
|
||||
|
||||
impl Encoder {
|
||||
fn table_begin(&mut self) -> Result<Self, Error> {
|
||||
match self.state {
|
||||
State::NextMapKey => Err(Error::InvalidMapKeyLocation),
|
||||
_ => Ok(mem::replace(self, Encoder::new()))
|
||||
}
|
||||
}
|
||||
|
||||
fn table_end(&mut self, mut state: Self) -> Result<(), Error> {
|
||||
match state.state {
|
||||
State::NextKey(key) => {
|
||||
mem::swap(&mut self.toml, &mut state.toml);
|
||||
self.toml.insert(key, Value::Table(state.toml));
|
||||
},
|
||||
State::NextArray(mut arr) => {
|
||||
mem::swap(&mut self.toml, &mut state.toml);
|
||||
arr.push(Value::Table(state.toml));
|
||||
self.state = State::NextArray(arr);
|
||||
},
|
||||
State::Start => {},
|
||||
State::NextMapKey => unreachable!(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ser::Serializer for Encoder {
|
||||
type Error = Error;
|
||||
type MapState = Self;
|
||||
type StructState = Self;
|
||||
type StructVariantState = Self;
|
||||
type SeqState = EncoderState;
|
||||
type TupleState = EncoderState;
|
||||
type TupleStructState = EncoderState;
|
||||
type TupleVariantState = EncoderState;
|
||||
|
||||
fn serialize_bool(&mut self, v: bool) -> Result<(), Error> {
|
||||
self.emit_value(Value::Boolean(v))
|
||||
}
|
||||
|
||||
fn serialize_i64(&mut self, v: i64) -> Result<(), Error> {
|
||||
self.emit_value(Value::Integer(v))
|
||||
}
|
||||
|
||||
// TODO: checked casts
|
||||
|
||||
fn serialize_u64(&mut self, v: u64) -> Result<(), Error> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_isize(&mut self, v: isize) -> Result<(), Error> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_usize(&mut self, v: usize) -> Result<(), Error> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_i8(&mut self, v: i8) -> Result<(), Error> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_u8(&mut self, v: u8) -> Result<(), Error> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_i16(&mut self, v: i16) -> Result<(), Error> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_u16(&mut self, v: u16) -> Result<(), Error> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_i32(&mut self, v: i32) -> Result<(), Error> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_u32(&mut self, v: u32) -> Result<(), Error> {
|
||||
self.serialize_i64(v as i64)
|
||||
}
|
||||
|
||||
fn serialize_f32(&mut self, v: f32) -> Result<(), Error> {
|
||||
self.serialize_f64(v as f64)
|
||||
}
|
||||
|
||||
fn serialize_f64(&mut self, v: f64) -> Result<(), Error> {
|
||||
self.emit_value(Value::Float(v))
|
||||
}
|
||||
|
||||
fn serialize_str(&mut self, value: &str) -> Result<(), Error> {
|
||||
self.emit_value(Value::String(value.to_string()))
|
||||
}
|
||||
|
||||
fn serialize_unit_struct(&mut self, _name: &'static str) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_unit(&mut self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_none(&mut self) -> Result<(), Error> {
|
||||
self.emit_none()
|
||||
}
|
||||
|
||||
fn serialize_char(&mut self, c: char) -> Result<(), Error> {
|
||||
self.serialize_str(&c.to_string())
|
||||
}
|
||||
|
||||
fn serialize_some<V>(&mut self, value: V) -> Result<(), Error>
|
||||
where V: ser::Serialize
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_bytes(&mut self, v: &[u8]) -> Result<(), Error> {
|
||||
let mut state = try!(self.serialize_seq(Some(v.len())));
|
||||
for c in v {
|
||||
try!(self.serialize_seq_elt(&mut state, c));
|
||||
}
|
||||
self.serialize_seq_end(state)
|
||||
}
|
||||
|
||||
fn serialize_seq_fixed_size(&mut self, len: usize)
|
||||
-> Result<EncoderState, Error> {
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
fn serialize_seq(&mut self, _len: Option<usize>)
|
||||
-> Result<EncoderState, Error> {
|
||||
self.seq_begin().map(|s| EncoderState { inner: s })
|
||||
}
|
||||
|
||||
fn serialize_seq_elt<T>(&mut self,
|
||||
_state: &mut EncoderState,
|
||||
value: T) -> Result<(), Error>
|
||||
where T: ser::Serialize
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_seq_end(&mut self, state: EncoderState) -> Result<(), Error> {
|
||||
self.seq_end(state.inner)
|
||||
}
|
||||
|
||||
fn serialize_tuple(&mut self, len: usize)
|
||||
-> Result<EncoderState, Error> {
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
fn serialize_tuple_elt<T>(&mut self,
|
||||
state: &mut EncoderState,
|
||||
value: T) -> Result<(), Error>
|
||||
where T: ser::Serialize
|
||||
{
|
||||
self.serialize_seq_elt(state, value)
|
||||
}
|
||||
|
||||
fn serialize_tuple_end(&mut self, state: EncoderState) -> Result<(), Error> {
|
||||
self.serialize_seq_end(state)
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(&mut self,
|
||||
_name: &'static str,
|
||||
len: usize) -> Result<EncoderState, Error> {
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct_elt<T>(&mut self,
|
||||
state: &mut EncoderState,
|
||||
value: T) -> Result<(), Error>
|
||||
where T: ser::Serialize
|
||||
{
|
||||
self.serialize_seq_elt(state, value)
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct_end(&mut self, state: EncoderState)
|
||||
-> Result<(), Error> {
|
||||
self.serialize_seq_end(state)
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(&mut self,
|
||||
_name: &'static str,
|
||||
_id: usize,
|
||||
_variant: &'static str,
|
||||
len: usize) -> Result<EncoderState, Error> {
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant_elt<T>(&mut self,
|
||||
state: &mut EncoderState,
|
||||
value: T) -> Result<(), Error>
|
||||
where T: ser::Serialize
|
||||
{
|
||||
self.serialize_seq_elt(state, value)
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant_end(&mut self, state: EncoderState)
|
||||
-> Result<(), Error> {
|
||||
self.serialize_seq_end(state)
|
||||
}
|
||||
|
||||
fn serialize_map(&mut self, _len: Option<usize>) -> Result<Self, Error> {
|
||||
self.table_begin()
|
||||
}
|
||||
|
||||
fn serialize_map_key<K>(&mut self,
|
||||
_state: &mut Encoder,
|
||||
key: K) -> Result<(), Error>
|
||||
where K: ser::Serialize
|
||||
{
|
||||
self.table_key(|me| key.serialize(me))
|
||||
}
|
||||
|
||||
fn serialize_map_value<V>(&mut self,
|
||||
_state: &mut Encoder,
|
||||
value: V) -> Result<(), Error>
|
||||
where V: ser::Serialize
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_map_end(&mut self, state: Self) -> Result<(), Error> {
|
||||
self.table_end(state)
|
||||
}
|
||||
|
||||
fn serialize_struct(&mut self,
|
||||
_name: &'static str,
|
||||
len: usize) -> Result<Self, Error> {
|
||||
self.serialize_map(Some(len))
|
||||
}
|
||||
|
||||
fn serialize_struct_elt<V>(&mut self,
|
||||
state: &mut Encoder,
|
||||
key: &'static str,
|
||||
value: V) -> Result<(), Error>
|
||||
where V: ser::Serialize
|
||||
{
|
||||
try!(self.serialize_map_key(state, key));
|
||||
self.serialize_map_value(state, value)
|
||||
}
|
||||
|
||||
fn serialize_struct_end(&mut self, state: Self) -> Result<(), Error> {
|
||||
self.serialize_map_end(state)
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(&mut self,
|
||||
_name: &'static str,
|
||||
_id: usize,
|
||||
_variant: &'static str,
|
||||
len: usize) -> Result<Self, Error> {
|
||||
self.serialize_map(Some(len))
|
||||
}
|
||||
|
||||
fn serialize_struct_variant_elt<V>(&mut self,
|
||||
state: &mut Encoder,
|
||||
key: &'static str,
|
||||
value: V) -> Result<(), Error>
|
||||
where V: ser::Serialize
|
||||
{
|
||||
try!(self.serialize_map_key(state, key));
|
||||
self.serialize_map_value(state, value)
|
||||
}
|
||||
|
||||
fn serialize_struct_variant_end(&mut self, state: Self) -> Result<(), Error> {
|
||||
self.serialize_map_end(state)
|
||||
}
|
||||
|
||||
fn serialize_newtype_struct<T>(&mut self,
|
||||
_name: &'static str,
|
||||
value: T) -> Result<(), Self::Error>
|
||||
where T: ser::Serialize,
|
||||
{
|
||||
// Don't serialize the newtype struct in a tuple.
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T>(&mut self,
|
||||
_name: &'static str,
|
||||
_variant_index: usize,
|
||||
_variant: &'static str,
|
||||
value: T) -> Result<(), Self::Error>
|
||||
where T: ser::Serialize,
|
||||
{
|
||||
// Don't serialize the newtype struct variant in a tuple.
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_unit_variant(&mut self,
|
||||
_name: &'static str,
|
||||
_variant_index: usize,
|
||||
_variant: &'static str,
|
||||
) -> Result<(), Self::Error>
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ser::Serialize for Value {
|
||||
fn serialize<E>(&self, e: &mut E) -> Result<(), E::Error>
|
||||
where E: ser::Serializer
|
||||
{
|
||||
match *self {
|
||||
Value::String(ref s) => e.serialize_str(s),
|
||||
Value::Integer(i) => e.serialize_i64(i),
|
||||
Value::Float(f) => e.serialize_f64(f),
|
||||
Value::Boolean(b) => e.serialize_bool(b),
|
||||
Value::Datetime(ref s) => e.serialize_str(s),
|
||||
Value::Array(ref a) => {
|
||||
let mut state = try!(e.serialize_seq(Some(a.len())));
|
||||
for el in a.iter() {
|
||||
try!(e.serialize_seq_elt(&mut state, el));
|
||||
}
|
||||
e.serialize_seq_end(state)
|
||||
}
|
||||
Value::Table(ref t) => {
|
||||
let mut state = try!(e.serialize_map(Some(t.len())));
|
||||
for (k, v) in t.iter() {
|
||||
try!(e.serialize_map_key(&mut state, k));
|
||||
try!(e.serialize_map_value(&mut state, v));
|
||||
}
|
||||
e.serialize_map_end(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ser::Error for Error {
|
||||
fn custom<T: Into<String>>(msg: T) -> Error {
|
||||
Error::Custom(msg.into())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,492 @@
|
|||
//! A TOML-parsing library
|
||||
//!
|
||||
//! This library is an implementation in Rust of a parser for TOML configuration
|
||||
//! files [1]. It is focused around high quality errors including specific spans
|
||||
//! and detailed error messages when things go wrong.
|
||||
//!
|
||||
//! This implementation currently passes the language agnostic [test suite][2].
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! let toml = r#"
|
||||
//! [test]
|
||||
//! foo = "bar"
|
||||
//! "#;
|
||||
//!
|
||||
//! let value = toml::Parser::new(toml).parse().unwrap();
|
||||
//! println!("{:?}", value);
|
||||
//! ```
|
||||
//!
|
||||
//! # Conversions
|
||||
//!
|
||||
//! This library also supports using the standard `Encodable` and `Decodable`
|
||||
//! traits with TOML values. This library provides the following conversion
|
||||
//! capabilities:
|
||||
//!
|
||||
//! * `String` => `toml::Value` - via `Parser`
|
||||
//! * `toml::Value` => `String` - via `Display`
|
||||
//! * `toml::Value` => rust object - via `Decoder`
|
||||
//! * rust object => `toml::Value` - via `Encoder`
|
||||
//!
|
||||
//! Convenience functions for performing multiple conversions at a time are also
|
||||
//! provided.
|
||||
//!
|
||||
//! [1]: https://github.com/mojombo/toml
|
||||
//! [2]: https://github.com/BurntSushi/toml-test
|
||||
|
||||
#![doc(html_root_url = "http://alexcrichton.com/toml-rs")]
|
||||
#![deny(missing_docs)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
#[cfg(feature = "rustc-serialize")] extern crate rustc_serialize;
|
||||
#[cfg(feature = "serde")] extern crate serde;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub use parser::{Parser, ParserError};
|
||||
|
||||
#[cfg(any(feature = "rustc-serialize", feature = "serde"))]
|
||||
pub use self::encoder::{Encoder, Error, EncoderState, encode, encode_str};
|
||||
#[cfg(any(feature = "rustc-serialize", feature = "serde"))]
|
||||
pub use self::decoder::{Decoder, DecodeError, DecodeErrorKind, decode, decode_str};
|
||||
|
||||
mod parser;
|
||||
mod display;
|
||||
#[cfg(any(feature = "rustc-serialize", feature = "serde"))]
|
||||
mod encoder;
|
||||
#[cfg(any(feature = "rustc-serialize", feature = "serde"))]
|
||||
mod decoder;
|
||||
|
||||
/// Representation of a TOML value.
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Value {
|
||||
String(String),
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
Boolean(bool),
|
||||
Datetime(String),
|
||||
Array(Array),
|
||||
Table(Table),
|
||||
}
|
||||
|
||||
/// Type representing a TOML array, payload of the `Value::Array` variant
|
||||
pub type Array = Vec<Value>;
|
||||
|
||||
/// Type representing a TOML table, payload of the `Value::Table` variant
|
||||
pub type Table = BTreeMap<String, Value>;
|
||||
|
||||
impl Value {
|
||||
/// Tests whether this and another value have the same type.
|
||||
pub fn same_type(&self, other: &Value) -> bool {
|
||||
match (self, other) {
|
||||
(&Value::String(..), &Value::String(..)) |
|
||||
(&Value::Integer(..), &Value::Integer(..)) |
|
||||
(&Value::Float(..), &Value::Float(..)) |
|
||||
(&Value::Boolean(..), &Value::Boolean(..)) |
|
||||
(&Value::Datetime(..), &Value::Datetime(..)) |
|
||||
(&Value::Array(..), &Value::Array(..)) |
|
||||
(&Value::Table(..), &Value::Table(..)) => true,
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a human-readable representation of the type of this value.
|
||||
pub fn type_str(&self) -> &'static str {
|
||||
match *self {
|
||||
Value::String(..) => "string",
|
||||
Value::Integer(..) => "integer",
|
||||
Value::Float(..) => "float",
|
||||
Value::Boolean(..) => "boolean",
|
||||
Value::Datetime(..) => "datetime",
|
||||
Value::Array(..) => "array",
|
||||
Value::Table(..) => "table",
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts the string of this value if it is a string.
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
match *self { Value::String(ref s) => Some(&**s), _ => None }
|
||||
}
|
||||
|
||||
/// Extracts the integer value if it is an integer.
|
||||
pub fn as_integer(&self) -> Option<i64> {
|
||||
match *self { Value::Integer(i) => Some(i), _ => None }
|
||||
}
|
||||
|
||||
/// Extracts the float value if it is a float.
|
||||
pub fn as_float(&self) -> Option<f64> {
|
||||
match *self { Value::Float(f) => Some(f), _ => None }
|
||||
}
|
||||
|
||||
/// Extracts the boolean value if it is a boolean.
|
||||
pub fn as_bool(&self) -> Option<bool> {
|
||||
match *self { Value::Boolean(b) => Some(b), _ => None }
|
||||
}
|
||||
|
||||
/// Extracts the datetime value if it is a datetime.
|
||||
///
|
||||
/// Note that a parsed TOML value will only contain ISO 8601 dates. An
|
||||
/// example date is:
|
||||
///
|
||||
/// ```notrust
|
||||
/// 1979-05-27T07:32:00Z
|
||||
/// ```
|
||||
pub fn as_datetime(&self) -> Option<&str> {
|
||||
match *self { Value::Datetime(ref s) => Some(&**s), _ => None }
|
||||
}
|
||||
|
||||
/// Extracts the array value if it is an array.
|
||||
pub fn as_slice(&self) -> Option<&[Value]> {
|
||||
match *self { Value::Array(ref s) => Some(&**s), _ => None }
|
||||
}
|
||||
|
||||
/// Extracts the table value if it is a table.
|
||||
pub fn as_table(&self) -> Option<&Table> {
|
||||
match *self { Value::Table(ref s) => Some(s), _ => None }
|
||||
}
|
||||
|
||||
/// Lookups for value at specified path.
|
||||
///
|
||||
/// Uses '.' as a path separator.
|
||||
///
|
||||
/// Note: arrays have zero-based indexes.
|
||||
///
|
||||
/// Note: empty path returns self.
|
||||
///
|
||||
/// ```
|
||||
/// # #![allow(unstable)]
|
||||
/// let toml = r#"
|
||||
/// [test]
|
||||
/// foo = "bar"
|
||||
///
|
||||
/// [[values]]
|
||||
/// foo = "baz"
|
||||
///
|
||||
/// [[values]]
|
||||
/// foo = "qux"
|
||||
/// "#;
|
||||
/// let value: toml::Value = toml.parse().unwrap();
|
||||
///
|
||||
/// let foo = value.lookup("test.foo").unwrap();
|
||||
/// assert_eq!(foo.as_str().unwrap(), "bar");
|
||||
///
|
||||
/// let foo = value.lookup("values.1.foo").unwrap();
|
||||
/// assert_eq!(foo.as_str().unwrap(), "qux");
|
||||
///
|
||||
/// let no_bar = value.lookup("test.bar");
|
||||
/// assert_eq!(no_bar.is_none(), true);
|
||||
/// ```
|
||||
pub fn lookup(&self, path: &str) -> Option<&Value> {
|
||||
let ref path = match Parser::new(path).lookup() {
|
||||
Some(path) => path,
|
||||
None => return None,
|
||||
};
|
||||
let mut cur_value = self;
|
||||
if path.is_empty() {
|
||||
return Some(cur_value)
|
||||
}
|
||||
|
||||
for key in path {
|
||||
match *cur_value {
|
||||
Value::Table(ref hm) => {
|
||||
match hm.get(key) {
|
||||
Some(v) => cur_value = v,
|
||||
None => return None
|
||||
}
|
||||
},
|
||||
Value::Array(ref v) => {
|
||||
match key.parse::<usize>().ok() {
|
||||
Some(idx) if idx < v.len() => cur_value = &v[idx],
|
||||
_ => return None
|
||||
}
|
||||
},
|
||||
_ => return None
|
||||
}
|
||||
};
|
||||
|
||||
Some(cur_value)
|
||||
|
||||
}
|
||||
/// Lookups for mutable value at specified path.
|
||||
///
|
||||
/// Uses '.' as a path separator.
|
||||
///
|
||||
/// Note: arrays have zero-based indexes.
|
||||
///
|
||||
/// Note: empty path returns self.
|
||||
///
|
||||
/// ```
|
||||
/// # #![allow(unstable)]
|
||||
/// let toml = r#"
|
||||
/// [test]
|
||||
/// foo = "bar"
|
||||
///
|
||||
/// [[values]]
|
||||
/// foo = "baz"
|
||||
///
|
||||
/// [[values]]
|
||||
/// foo = "qux"
|
||||
/// "#;
|
||||
/// let mut value: toml::Value = toml.parse().unwrap();
|
||||
/// {
|
||||
/// let string = value.lookup_mut("test.foo").unwrap();
|
||||
/// assert_eq!(string, &mut toml::Value::String(String::from("bar")));
|
||||
/// *string = toml::Value::String(String::from("foo"));
|
||||
/// }
|
||||
/// let result = value.lookup_mut("test.foo").unwrap();
|
||||
/// assert_eq!(result.as_str().unwrap(), "foo");
|
||||
/// ```
|
||||
pub fn lookup_mut(&mut self, path: &str) -> Option<&mut Value> {
|
||||
let ref path = match Parser::new(path).lookup() {
|
||||
Some(path) => path,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let mut cur = self;
|
||||
if path.is_empty() {
|
||||
return Some(cur)
|
||||
}
|
||||
|
||||
for key in path {
|
||||
let tmp = cur;
|
||||
match *tmp {
|
||||
Value::Table(ref mut hm) => {
|
||||
match hm.get_mut(key) {
|
||||
Some(v) => cur = v,
|
||||
None => return None
|
||||
}
|
||||
}
|
||||
Value::Array(ref mut v) => {
|
||||
match key.parse::<usize>().ok() {
|
||||
Some(idx) if idx < v.len() => cur = &mut v[idx],
|
||||
_ => return None
|
||||
}
|
||||
}
|
||||
_ => return None
|
||||
}
|
||||
}
|
||||
Some(cur)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Value {
|
||||
type Err = Vec<ParserError>;
|
||||
fn from_str(s: &str) -> Result<Value, Vec<ParserError>> {
|
||||
let mut p = Parser::new(s);
|
||||
match p.parse().map(Value::Table) {
|
||||
Some(n) => Ok(n),
|
||||
None => Err(p.errors),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Value;
|
||||
|
||||
#[test]
|
||||
fn lookup_mut_change() {
|
||||
let toml = r#"
|
||||
[test]
|
||||
foo = "bar"
|
||||
|
||||
[[values]]
|
||||
foo = "baz"
|
||||
|
||||
[[values]]
|
||||
foo = "qux"
|
||||
"#;
|
||||
|
||||
let mut value: Value = toml.parse().unwrap();
|
||||
{
|
||||
let foo = value.lookup_mut("values.0.foo").unwrap();
|
||||
*foo = Value::String(String::from("bar"));
|
||||
}
|
||||
let foo = value.lookup("values.0.foo").unwrap();
|
||||
assert_eq!(foo.as_str().unwrap(), "bar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup_mut_valid() {
|
||||
let toml = r#"
|
||||
[test]
|
||||
foo = "bar"
|
||||
|
||||
[[values]]
|
||||
foo = "baz"
|
||||
|
||||
[[values]]
|
||||
foo = "qux"
|
||||
"#;
|
||||
|
||||
let mut value: Value = toml.parse().unwrap();
|
||||
|
||||
{
|
||||
let test_foo = value.lookup_mut("test.foo").unwrap();
|
||||
assert_eq!(test_foo.as_str().unwrap(), "bar");
|
||||
}
|
||||
|
||||
{
|
||||
let foo1 = value.lookup_mut("values.1.foo").unwrap();
|
||||
assert_eq!(foo1.as_str().unwrap(), "qux");
|
||||
}
|
||||
|
||||
assert!(value.lookup_mut("test.bar").is_none());
|
||||
assert!(value.lookup_mut("test.foo.bar").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup_mut_invalid_index() {
|
||||
let toml = r#"
|
||||
[[values]]
|
||||
foo = "baz"
|
||||
"#;
|
||||
|
||||
let mut value: Value = toml.parse().unwrap();
|
||||
|
||||
{
|
||||
let foo = value.lookup_mut("test.foo");
|
||||
assert!(foo.is_none());
|
||||
}
|
||||
|
||||
{
|
||||
let foo = value.lookup_mut("values.100.foo");
|
||||
assert!(foo.is_none());
|
||||
}
|
||||
|
||||
{
|
||||
let foo = value.lookup_mut("values.str.foo");
|
||||
assert!(foo.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup_mut_self() {
|
||||
let mut value: Value = r#"foo = "bar""#.parse().unwrap();
|
||||
|
||||
{
|
||||
let foo = value.lookup_mut("foo").unwrap();
|
||||
assert_eq!(foo.as_str().unwrap(), "bar");
|
||||
}
|
||||
|
||||
let foo = value.lookup_mut("").unwrap();
|
||||
assert!(foo.as_table().is_some());
|
||||
|
||||
let baz = foo.lookup_mut("foo").unwrap();
|
||||
assert_eq!(baz.as_str().unwrap(), "bar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup_valid() {
|
||||
let toml = r#"
|
||||
[test]
|
||||
foo = "bar"
|
||||
|
||||
[[values]]
|
||||
foo = "baz"
|
||||
|
||||
[[values]]
|
||||
foo = "qux"
|
||||
"#;
|
||||
|
||||
let value: Value = toml.parse().unwrap();
|
||||
|
||||
let test_foo = value.lookup("test.foo").unwrap();
|
||||
assert_eq!(test_foo.as_str().unwrap(), "bar");
|
||||
|
||||
let foo1 = value.lookup("values.1.foo").unwrap();
|
||||
assert_eq!(foo1.as_str().unwrap(), "qux");
|
||||
|
||||
assert!(value.lookup("test.bar").is_none());
|
||||
assert!(value.lookup("test.foo.bar").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup_invalid_index() {
|
||||
let toml = r#"
|
||||
[[values]]
|
||||
foo = "baz"
|
||||
"#;
|
||||
|
||||
let value: Value = toml.parse().unwrap();
|
||||
|
||||
let foo = value.lookup("test.foo");
|
||||
assert!(foo.is_none());
|
||||
|
||||
let foo = value.lookup("values.100.foo");
|
||||
assert!(foo.is_none());
|
||||
|
||||
let foo = value.lookup("values.str.foo");
|
||||
assert!(foo.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup_self() {
|
||||
let value: Value = r#"foo = "bar""#.parse().unwrap();
|
||||
|
||||
let foo = value.lookup("foo").unwrap();
|
||||
assert_eq!(foo.as_str().unwrap(), "bar");
|
||||
|
||||
let foo = value.lookup("").unwrap();
|
||||
assert!(foo.as_table().is_some());
|
||||
|
||||
let baz = foo.lookup("foo").unwrap();
|
||||
assert_eq!(baz.as_str().unwrap(), "bar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup_advanced() {
|
||||
let value: Value = "[table]\n\"value\" = 0".parse().unwrap();
|
||||
let looked = value.lookup("table.\"value\"").unwrap();
|
||||
assert_eq!(*looked, Value::Integer(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup_advanced_table() {
|
||||
let value: Value = "[table.\"name.other\"]\nvalue = \"my value\"".parse().unwrap();
|
||||
let looked = value.lookup(r#"table."name.other".value"#).unwrap();
|
||||
assert_eq!(*looked, Value::String(String::from("my value")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup_mut_advanced() {
|
||||
let mut value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap();
|
||||
let looked = value.lookup_mut("table.\"value\".1").unwrap();
|
||||
assert_eq!(*looked, Value::Integer(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_dot() {
|
||||
let value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap();
|
||||
assert_eq!(None, value.lookup("."));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_dot() {
|
||||
let value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap();
|
||||
assert_eq!(None, value.lookup("0."));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dot_inside() {
|
||||
let value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap();
|
||||
assert_eq!(None, value.lookup("table.\"value.0\""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_with_quotes() {
|
||||
let value: Value = "[table.\"element\"]\n\"value\" = [0, 1, 2]".parse().unwrap();
|
||||
assert_eq!(None, value.lookup("\"table.element\".\"value\".0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_with_quotes_2() {
|
||||
let value: Value = "[table.\"element\"]\n\"value\" = [0, 1, 2]".parse().unwrap();
|
||||
assert_eq!(Value::Integer(0), *value.lookup("table.\"element\".\"value\".0").unwrap());
|
||||
}
|
||||
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1 @@
|
|||
Tests are from https://github.com/BurntSushi/toml-test
|
|
@ -0,0 +1,52 @@
|
|||
extern crate rustc_serialize;
|
||||
extern crate toml;
|
||||
use toml::encode_str;
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)]
|
||||
struct User {
|
||||
pub name: String,
|
||||
pub surname: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)]
|
||||
struct Users {
|
||||
pub user: Vec<User>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)]
|
||||
struct TwoUsers {
|
||||
pub user0: User,
|
||||
pub user1: User,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_unnecessary_newlines_array() {
|
||||
assert!(!encode_str(&Users {
|
||||
user: vec![
|
||||
User {
|
||||
name: "John".to_string(),
|
||||
surname: "Doe".to_string(),
|
||||
},
|
||||
User {
|
||||
name: "Jane".to_string(),
|
||||
surname: "Dough".to_string(),
|
||||
},
|
||||
],
|
||||
})
|
||||
.starts_with("\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_unnecessary_newlines_table() {
|
||||
assert!(!encode_str(&TwoUsers {
|
||||
user0: User {
|
||||
name: "John".to_string(),
|
||||
surname: "Doe".to_string(),
|
||||
},
|
||||
user1: User {
|
||||
name: "Jane".to_string(),
|
||||
surname: "Dough".to_string(),
|
||||
},
|
||||
})
|
||||
.starts_with("\n"));
|
||||
}
|
15
third_party/rust/toml/tests/invalid-encoder/array-mixed-types-ints-and-floats.json
поставляемый
Normal file
15
third_party/rust/toml/tests/invalid-encoder/array-mixed-types-ints-and-floats.json
поставляемый
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"ints-and-floats": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{
|
||||
"type": "integer",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"type": "float",
|
||||
"value": "1.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
extern crate toml;
|
||||
|
||||
use toml::{Parser};
|
||||
|
||||
fn run(toml: &str) {
|
||||
let mut p = Parser::new(toml);
|
||||
let table = p.parse();
|
||||
assert!(table.is_none());
|
||||
assert!(p.errors.len() > 0);
|
||||
|
||||
// test Parser::to_linecol with the generated error offsets
|
||||
for error in &p.errors {
|
||||
p.to_linecol(error.lo);
|
||||
p.to_linecol(error.hi);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! test( ($name:ident, $toml:expr) => (
|
||||
#[test]
|
||||
fn $name() { run($toml); }
|
||||
) );
|
||||
|
||||
test!(array_mixed_types_arrays_and_ints,
|
||||
include_str!("invalid/array-mixed-types-arrays-and-ints.toml"));
|
||||
test!(array_mixed_types_ints_and_floats,
|
||||
include_str!("invalid/array-mixed-types-ints-and-floats.toml"));
|
||||
test!(array_mixed_types_strings_and_ints,
|
||||
include_str!("invalid/array-mixed-types-strings-and-ints.toml"));
|
||||
test!(datetime_malformed_no_leads,
|
||||
include_str!("invalid/datetime-malformed-no-leads.toml"));
|
||||
test!(datetime_malformed_no_secs,
|
||||
include_str!("invalid/datetime-malformed-no-secs.toml"));
|
||||
test!(datetime_malformed_no_t,
|
||||
include_str!("invalid/datetime-malformed-no-t.toml"));
|
||||
test!(datetime_malformed_no_z,
|
||||
include_str!("invalid/datetime-malformed-no-z.toml"));
|
||||
test!(datetime_malformed_with_milli,
|
||||
include_str!("invalid/datetime-malformed-with-milli.toml"));
|
||||
test!(duplicate_keys,
|
||||
include_str!("invalid/duplicate-keys.toml"));
|
||||
test!(duplicate_key_table,
|
||||
include_str!("invalid/duplicate-key-table.toml"));
|
||||
test!(duplicate_tables,
|
||||
include_str!("invalid/duplicate-tables.toml"));
|
||||
test!(empty_implicit_table,
|
||||
include_str!("invalid/empty-implicit-table.toml"));
|
||||
test!(empty_table,
|
||||
include_str!("invalid/empty-table.toml"));
|
||||
test!(float_no_leading_zero,
|
||||
include_str!("invalid/float-no-leading-zero.toml"));
|
||||
test!(float_no_trailing_digits,
|
||||
include_str!("invalid/float-no-trailing-digits.toml"));
|
||||
test!(key_after_array,
|
||||
include_str!("invalid/key-after-array.toml"));
|
||||
test!(key_after_table,
|
||||
include_str!("invalid/key-after-table.toml"));
|
||||
test!(key_empty,
|
||||
include_str!("invalid/key-empty.toml"));
|
||||
test!(key_hash,
|
||||
include_str!("invalid/key-hash.toml"));
|
||||
test!(key_newline,
|
||||
include_str!("invalid/key-newline.toml"));
|
||||
test!(key_open_bracket,
|
||||
include_str!("invalid/key-open-bracket.toml"));
|
||||
test!(key_single_open_bracket,
|
||||
include_str!("invalid/key-single-open-bracket.toml"));
|
||||
test!(key_space,
|
||||
include_str!("invalid/key-space.toml"));
|
||||
test!(key_start_bracket,
|
||||
include_str!("invalid/key-start-bracket.toml"));
|
||||
test!(key_two_equals,
|
||||
include_str!("invalid/key-two-equals.toml"));
|
||||
test!(string_bad_byte_escape,
|
||||
include_str!("invalid/string-bad-byte-escape.toml"));
|
||||
test!(string_bad_escape,
|
||||
include_str!("invalid/string-bad-escape.toml"));
|
||||
test!(string_byte_escapes,
|
||||
include_str!("invalid/string-byte-escapes.toml"));
|
||||
test!(string_no_close,
|
||||
include_str!("invalid/string-no-close.toml"));
|
||||
test!(table_array_implicit,
|
||||
include_str!("invalid/table-array-implicit.toml"));
|
||||
test!(table_array_malformed_bracket,
|
||||
include_str!("invalid/table-array-malformed-bracket.toml"));
|
||||
test!(table_array_malformed_empty,
|
||||
include_str!("invalid/table-array-malformed-empty.toml"));
|
||||
test!(table_empty,
|
||||
include_str!("invalid/table-empty.toml"));
|
||||
test!(table_nested_brackets_close,
|
||||
include_str!("invalid/table-nested-brackets-close.toml"));
|
||||
test!(table_nested_brackets_open,
|
||||
include_str!("invalid/table-nested-brackets-open.toml"));
|
||||
test!(table_whitespace,
|
||||
include_str!("invalid/table-whitespace.toml"));
|
||||
test!(table_with_pound,
|
||||
include_str!("invalid/table-with-pound.toml"));
|
||||
test!(text_after_array_entries,
|
||||
include_str!("invalid/text-after-array-entries.toml"));
|
||||
test!(text_after_integer,
|
||||
include_str!("invalid/text-after-integer.toml"));
|
||||
test!(text_after_string,
|
||||
include_str!("invalid/text-after-string.toml"));
|
||||
test!(text_after_table,
|
||||
include_str!("invalid/text-after-table.toml"));
|
||||
test!(text_before_array_separator,
|
||||
include_str!("invalid/text-before-array-separator.toml"));
|
||||
test!(text_in_array,
|
||||
include_str!("invalid/text-in-array.toml"));
|
1
third_party/rust/toml/tests/invalid/array-mixed-types-arrays-and-ints.toml
поставляемый
Normal file
1
third_party/rust/toml/tests/invalid/array-mixed-types-arrays-and-ints.toml
поставляемый
Normal file
|
@ -0,0 +1 @@
|
|||
arrays-and-ints = [1, ["Arrays are not integers."]]
|
1
third_party/rust/toml/tests/invalid/array-mixed-types-ints-and-floats.toml
поставляемый
Normal file
1
third_party/rust/toml/tests/invalid/array-mixed-types-ints-and-floats.toml
поставляемый
Normal file
|
@ -0,0 +1 @@
|
|||
ints-and-floats = [1, 1.1]
|
1
third_party/rust/toml/tests/invalid/array-mixed-types-strings-and-ints.toml
поставляемый
Normal file
1
third_party/rust/toml/tests/invalid/array-mixed-types-strings-and-ints.toml
поставляемый
Normal file
|
@ -0,0 +1 @@
|
|||
strings-and-ints = ["hi", 42]
|
|
@ -0,0 +1 @@
|
|||
no-leads = 1987-7-05T17:45:00Z
|
|
@ -0,0 +1 @@
|
|||
no-secs = 1987-07-05T17:45Z
|
|
@ -0,0 +1 @@
|
|||
no-t = 1987-07-0517:45:00Z
|
|
@ -0,0 +1 @@
|
|||
no-z = 1987-07-05T17:45:00
|
|
@ -0,0 +1 @@
|
|||
with-milli = 1987-07-5T17:45:00.12Z
|
|
@ -0,0 +1,5 @@
|
|||
[fruit]
|
||||
type = "apple"
|
||||
|
||||
[fruit.type]
|
||||
apple = "yes"
|
|
@ -0,0 +1,2 @@
|
|||
dupe = false
|
||||
dupe = true
|
|
@ -0,0 +1,2 @@
|
|||
[a]
|
||||
[a]
|
|
@ -0,0 +1 @@
|
|||
[naughty..naughty]
|
|
@ -0,0 +1 @@
|
|||
[]
|
|
@ -0,0 +1,2 @@
|
|||
answer = .12345
|
||||
neganswer = -.12345
|
|
@ -0,0 +1,2 @@
|
|||
answer = 1.
|
||||
neganswer = -1.
|
|
@ -0,0 +1 @@
|
|||
[[agencies]] owner = "S Cjelli"
|
|
@ -0,0 +1 @@
|
|||
[history] guard = "sleeping"
|
|
@ -0,0 +1 @@
|
|||
= 1
|
|
@ -0,0 +1 @@
|
|||
a# = 1
|
|
@ -0,0 +1,2 @@
|
|||
a
|
||||
= 1
|
|
@ -0,0 +1 @@
|
|||
[abc = 1
|
|
@ -0,0 +1 @@
|
|||
[
|
|
@ -0,0 +1 @@
|
|||
a b = 1
|
|
@ -0,0 +1,3 @@
|
|||
[a]
|
||||
[xyz = 5
|
||||
[b]
|
|
@ -0,0 +1 @@
|
|||
key= = 1
|
|
@ -0,0 +1 @@
|
|||
naughty = "\xAg"
|
|
@ -0,0 +1 @@
|
|||
invalid-escape = "This string has a bad \a escape character."
|
|
@ -0,0 +1 @@
|
|||
answer = "\x33"
|
|
@ -0,0 +1 @@
|
|||
no-ending-quote = "One time, at band camp
|
|
@ -0,0 +1,14 @@
|
|||
# This test is a bit tricky. It should fail because the first use of
|
||||
# `[[albums.songs]]` without first declaring `albums` implies that `albums`
|
||||
# must be a table. The alternative would be quite weird. Namely, it wouldn't
|
||||
# comply with the TOML spec: "Each double-bracketed sub-table will belong to
|
||||
# the most *recently* defined table element *above* it."
|
||||
#
|
||||
# This is in contrast to the *valid* test, table-array-implicit where
|
||||
# `[[albums.songs]]` works by itself, so long as `[[albums]]` isn't declared
|
||||
# later. (Although, `[albums]` could be.)
|
||||
[[albums.songs]]
|
||||
name = "Glory Days"
|
||||
|
||||
[[albums]]
|
||||
name = "Born in the USA"
|
|
@ -0,0 +1,2 @@
|
|||
[[albums]
|
||||
name = "Born to Run"
|
|
@ -0,0 +1,2 @@
|
|||
[[]]
|
||||
name = "Born to Run"
|
|
@ -0,0 +1 @@
|
|||
[]
|
|
@ -0,0 +1,2 @@
|
|||
[a]b]
|
||||
zyx = 42
|
|
@ -0,0 +1,2 @@
|
|||
[a[b]
|
||||
zyx = 42
|
|
@ -0,0 +1 @@
|
|||
[invalid key]
|
|
@ -0,0 +1,2 @@
|
|||
[key#group]
|
||||
answer = 42
|
|
@ -0,0 +1,4 @@
|
|||
array = [
|
||||
"Is there life after an array separator?", No
|
||||
"Entry"
|
||||
]
|
|
@ -0,0 +1 @@
|
|||
answer = 42 the ultimate answer?
|
|
@ -0,0 +1 @@
|
|||
string = "Is there life after strings?" No.
|
|
@ -0,0 +1 @@
|
|||
[error] this shouldn't be here
|
|
@ -0,0 +1,4 @@
|
|||
array = [
|
||||
"Is there life before an array separator?" No,
|
||||
"Entry"
|
||||
]
|
|
@ -0,0 +1,5 @@
|
|||
array = [
|
||||
"Entry 1",
|
||||
I don't belong,
|
||||
"Entry 2",
|
||||
]
|
|
@ -0,0 +1,195 @@
|
|||
extern crate rustc_serialize;
|
||||
extern crate toml;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use rustc_serialize::json::Json;
|
||||
|
||||
use toml::{Parser, Value};
|
||||
use toml::Value::{Table, Integer, Float, Boolean, Datetime, Array};
|
||||
|
||||
fn to_json(toml: Value) -> Json {
|
||||
fn doit(s: &str, json: Json) -> Json {
|
||||
let mut map = BTreeMap::new();
|
||||
map.insert(format!("{}", "type"), Json::String(format!("{}", s)));
|
||||
map.insert(format!("{}", "value"), json);
|
||||
Json::Object(map)
|
||||
}
|
||||
match toml {
|
||||
Value::String(s) => doit("string", Json::String(s)),
|
||||
Integer(i) => doit("integer", Json::String(format!("{}", i))),
|
||||
Float(f) => doit("float", Json::String({
|
||||
let s = format!("{:.15}", f);
|
||||
let s = format!("{}", s.trim_right_matches('0'));
|
||||
if s.ends_with(".") {format!("{}0", s)} else {s}
|
||||
})),
|
||||
Boolean(b) => doit("bool", Json::String(format!("{}", b))),
|
||||
Datetime(s) => doit("datetime", Json::String(s)),
|
||||
Array(arr) => {
|
||||
let is_table = match arr.first() {
|
||||
Some(&Table(..)) => true,
|
||||
_ => false,
|
||||
};
|
||||
let json = Json::Array(arr.into_iter().map(to_json).collect());
|
||||
if is_table {json} else {doit("array", json)}
|
||||
}
|
||||
Table(table) => Json::Object(table.into_iter().map(|(k, v)| {
|
||||
(k, to_json(v))
|
||||
}).collect()),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(toml: &str, json: &str) {
|
||||
let mut p = Parser::new(toml);
|
||||
let table = p.parse();
|
||||
assert!(p.errors.len() == 0, "had_errors: {:?}",
|
||||
p.errors.iter().map(|e| {
|
||||
(e.desc.clone(), &toml[e.lo - 5..e.hi + 5])
|
||||
}).collect::<Vec<(String, &str)>>());
|
||||
assert!(table.is_some());
|
||||
let toml = Table(table.unwrap());
|
||||
let toml_string = format!("{}", toml);
|
||||
|
||||
let json = Json::from_str(json).unwrap();
|
||||
let toml_json = to_json(toml.clone());
|
||||
assert!(json == toml_json,
|
||||
"expected\n{}\ngot\n{}\n",
|
||||
json.pretty(),
|
||||
toml_json.pretty());
|
||||
|
||||
let table2 = Parser::new(&toml_string).parse().unwrap();
|
||||
// floats are a little lossy
|
||||
if table2.values().any(|v| v.as_float().is_some()) { return }
|
||||
assert_eq!(toml, Table(table2));
|
||||
}
|
||||
|
||||
macro_rules! test( ($name:ident, $toml:expr, $json:expr) => (
|
||||
#[test]
|
||||
fn $name() { run($toml, $json); }
|
||||
) );
|
||||
|
||||
test!(array_empty,
|
||||
include_str!("valid/array-empty.toml"),
|
||||
include_str!("valid/array-empty.json"));
|
||||
test!(array_nospaces,
|
||||
include_str!("valid/array-nospaces.toml"),
|
||||
include_str!("valid/array-nospaces.json"));
|
||||
test!(arrays_hetergeneous,
|
||||
include_str!("valid/arrays-hetergeneous.toml"),
|
||||
include_str!("valid/arrays-hetergeneous.json"));
|
||||
test!(arrays,
|
||||
include_str!("valid/arrays.toml"),
|
||||
include_str!("valid/arrays.json"));
|
||||
test!(arrays_nested,
|
||||
include_str!("valid/arrays-nested.toml"),
|
||||
include_str!("valid/arrays-nested.json"));
|
||||
test!(empty,
|
||||
include_str!("valid/empty.toml"),
|
||||
include_str!("valid/empty.json"));
|
||||
test!(bool,
|
||||
include_str!("valid/bool.toml"),
|
||||
include_str!("valid/bool.json"));
|
||||
test!(datetime,
|
||||
include_str!("valid/datetime.toml"),
|
||||
include_str!("valid/datetime.json"));
|
||||
test!(example,
|
||||
include_str!("valid/example.toml"),
|
||||
include_str!("valid/example.json"));
|
||||
test!(float,
|
||||
include_str!("valid/float.toml"),
|
||||
include_str!("valid/float.json"));
|
||||
test!(implicit_and_explicit_after,
|
||||
include_str!("valid/implicit-and-explicit-after.toml"),
|
||||
include_str!("valid/implicit-and-explicit-after.json"));
|
||||
test!(implicit_and_explicit_before,
|
||||
include_str!("valid/implicit-and-explicit-before.toml"),
|
||||
include_str!("valid/implicit-and-explicit-before.json"));
|
||||
test!(implicit_groups,
|
||||
include_str!("valid/implicit-groups.toml"),
|
||||
include_str!("valid/implicit-groups.json"));
|
||||
test!(integer,
|
||||
include_str!("valid/integer.toml"),
|
||||
include_str!("valid/integer.json"));
|
||||
test!(key_equals_nospace,
|
||||
include_str!("valid/key-equals-nospace.toml"),
|
||||
include_str!("valid/key-equals-nospace.json"));
|
||||
test!(key_space,
|
||||
include_str!("valid/key-space.toml"),
|
||||
include_str!("valid/key-space.json"));
|
||||
test!(key_special_chars,
|
||||
include_str!("valid/key-special-chars.toml"),
|
||||
include_str!("valid/key-special-chars.json"));
|
||||
test!(key_with_pound,
|
||||
include_str!("valid/key-with-pound.toml"),
|
||||
include_str!("valid/key-with-pound.json"));
|
||||
test!(long_float,
|
||||
include_str!("valid/long-float.toml"),
|
||||
include_str!("valid/long-float.json"));
|
||||
test!(long_integer,
|
||||
include_str!("valid/long-integer.toml"),
|
||||
include_str!("valid/long-integer.json"));
|
||||
test!(multiline_string,
|
||||
include_str!("valid/multiline-string.toml"),
|
||||
include_str!("valid/multiline-string.json"));
|
||||
test!(raw_multiline_string,
|
||||
include_str!("valid/raw-multiline-string.toml"),
|
||||
include_str!("valid/raw-multiline-string.json"));
|
||||
test!(raw_string,
|
||||
include_str!("valid/raw-string.toml"),
|
||||
include_str!("valid/raw-string.json"));
|
||||
test!(string_empty,
|
||||
include_str!("valid/string-empty.toml"),
|
||||
include_str!("valid/string-empty.json"));
|
||||
test!(string_escapes,
|
||||
include_str!("valid/string-escapes.toml"),
|
||||
include_str!("valid/string-escapes.json"));
|
||||
test!(string_simple,
|
||||
include_str!("valid/string-simple.toml"),
|
||||
include_str!("valid/string-simple.json"));
|
||||
test!(string_with_pound,
|
||||
include_str!("valid/string-with-pound.toml"),
|
||||
include_str!("valid/string-with-pound.json"));
|
||||
test!(table_array_implicit,
|
||||
include_str!("valid/table-array-implicit.toml"),
|
||||
include_str!("valid/table-array-implicit.json"));
|
||||
test!(table_array_many,
|
||||
include_str!("valid/table-array-many.toml"),
|
||||
include_str!("valid/table-array-many.json"));
|
||||
test!(table_array_nest,
|
||||
include_str!("valid/table-array-nest.toml"),
|
||||
include_str!("valid/table-array-nest.json"));
|
||||
test!(table_array_one,
|
||||
include_str!("valid/table-array-one.toml"),
|
||||
include_str!("valid/table-array-one.json"));
|
||||
test!(table_empty,
|
||||
include_str!("valid/table-empty.toml"),
|
||||
include_str!("valid/table-empty.json"));
|
||||
test!(table_sub_empty,
|
||||
include_str!("valid/table-sub-empty.toml"),
|
||||
include_str!("valid/table-sub-empty.json"));
|
||||
test!(table_whitespace,
|
||||
include_str!("valid/table-whitespace.toml"),
|
||||
include_str!("valid/table-whitespace.json"));
|
||||
test!(table_with_pound,
|
||||
include_str!("valid/table-with-pound.toml"),
|
||||
include_str!("valid/table-with-pound.json"));
|
||||
test!(unicode_escape,
|
||||
include_str!("valid/unicode-escape.toml"),
|
||||
include_str!("valid/unicode-escape.json"));
|
||||
test!(unicode_literal,
|
||||
include_str!("valid/unicode-literal.toml"),
|
||||
include_str!("valid/unicode-literal.json"));
|
||||
test!(hard_example,
|
||||
include_str!("valid/hard_example.toml"),
|
||||
include_str!("valid/hard_example.json"));
|
||||
test!(example2,
|
||||
include_str!("valid/example2.toml"),
|
||||
include_str!("valid/example2.json"));
|
||||
test!(example3,
|
||||
include_str!("valid/example-v0.3.0.toml"),
|
||||
include_str!("valid/example-v0.3.0.json"));
|
||||
test!(example4,
|
||||
include_str!("valid/example-v0.4.0.toml"),
|
||||
include_str!("valid/example-v0.4.0.json"));
|
||||
test!(example_bom,
|
||||
include_str!("valid/example-bom.toml"),
|
||||
include_str!("valid/example.json"));
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"thevoid": { "type": "array", "value": [
|
||||
{"type": "array", "value": [
|
||||
{"type": "array", "value": [
|
||||
{"type": "array", "value": [
|
||||
{"type": "array", "value": []}
|
||||
]}
|
||||
]}
|
||||
]}
|
||||
]}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
thevoid = [[[[[]]]]]
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"ints": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "integer", "value": "1"},
|
||||
{"type": "integer", "value": "2"},
|
||||
{"type": "integer", "value": "3"}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
ints = [1,2,3]
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"mixed": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "array", "value": [
|
||||
{"type": "integer", "value": "1"},
|
||||
{"type": "integer", "value": "2"}
|
||||
]},
|
||||
{"type": "array", "value": [
|
||||
{"type": "string", "value": "a"},
|
||||
{"type": "string", "value": "b"}
|
||||
]},
|
||||
{"type": "array", "value": [
|
||||
{"type": "float", "value": "1.1"},
|
||||
{"type": "float", "value": "2.1"}
|
||||
]}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
mixed = [[1, 2], ["a", "b"], [1.1, 2.1]]
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"nest": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "array", "value": [
|
||||
{"type": "string", "value": "a"}
|
||||
]},
|
||||
{"type": "array", "value": [
|
||||
{"type": "string", "value": "b"}
|
||||
]}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
nest = [["a"], ["b"]]
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"ints": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "integer", "value": "1"},
|
||||
{"type": "integer", "value": "2"},
|
||||
{"type": "integer", "value": "3"}
|
||||
]
|
||||
},
|
||||
"floats": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "float", "value": "1.1"},
|
||||
{"type": "float", "value": "2.1"},
|
||||
{"type": "float", "value": "3.1"}
|
||||
]
|
||||
},
|
||||
"strings": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "string", "value": "a"},
|
||||
{"type": "string", "value": "b"},
|
||||
{"type": "string", "value": "c"}
|
||||
]
|
||||
},
|
||||
"dates": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "datetime", "value": "1987-07-05T17:45:00Z"},
|
||||
{"type": "datetime", "value": "1979-05-27T07:32:00Z"},
|
||||
{"type": "datetime", "value": "2006-06-01T11:00:00Z"}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
ints = [1, 2, 3]
|
||||
floats = [1.1, 2.1, 3.1]
|
||||
strings = ["a", "b", "c"]
|
||||
dates = [
|
||||
1987-07-05T17:45:00Z,
|
||||
1979-05-27T07:32:00Z,
|
||||
2006-06-01T11:00:00Z,
|
||||
]
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"f": {"type": "bool", "value": "false"},
|
||||
"t": {"type": "bool", "value": "true"}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
t = true
|
||||
f = false
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"group": {
|
||||
"answer": {"type": "integer", "value": "42"},
|
||||
"more": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "integer", "value": "42"},
|
||||
{"type": "integer", "value": "42"}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
# Top comment.
|
||||
# Top comment.
|
||||
# Top comment.
|
||||
|
||||
# [no-extraneous-groups-please]
|
||||
|
||||
[group] # Comment
|
||||
answer = 42 # Comment
|
||||
# no-extraneous-keys-please = 999
|
||||
# Inbetween comment.
|
||||
more = [ # Comment
|
||||
# What about multiple # comments?
|
||||
# Can you handle it?
|
||||
#
|
||||
# Evil.
|
||||
# Evil.
|
||||
42, 42, # Comments within arrays are fun.
|
||||
# What about multiple # comments?
|
||||
# Can you handle it?
|
||||
#
|
||||
# Evil.
|
||||
# Evil.
|
||||
# ] Did I fool you?
|
||||
] # Hopefully not.
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"bestdayever": {"type": "datetime", "value": "1987-07-05T17:45:00Z"}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
bestdayever = 1987-07-05T17:45:00Z
|
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -0,0 +1,5 @@
|
|||
best-day-ever = 1987-07-05T17:45:00Z
|
||||
|
||||
[numtheory]
|
||||
boring = false
|
||||
perfection = [6, 28, 496]
|
|
@ -0,0 +1 @@
|
|||
{"Array":{"key1":{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"},{"type":"integer","value":"3"}]},"key2":{"type":"array","value":[{"type":"string","value":"red"},{"type":"string","value":"yellow"},{"type":"string","value":"green"}]},"key3":{"type":"array","value":[{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"}]},{"type":"array","value":[{"type":"integer","value":"3"},{"type":"integer","value":"4"},{"type":"integer","value":"5"}]}]},"key4":{"type":"array","value":[{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"}]},{"type":"array","value":[{"type":"string","value":"a"},{"type":"string","value":"b"},{"type":"string","value":"c"}]}]},"key5":{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"},{"type":"integer","value":"3"}]},"key6":{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"}]}},"Booleans":{"False":{"type":"bool","value":"false"},"True":{"type":"bool","value":"true"}},"Datetime":{"key1":{"type":"datetime","value":"1979-05-27T07:32:00Z"}},"Float":{"both":{},"exponent":{},"fractional":{"key1":{"type":"float","value":"1.0"},"key2":{"type":"float","value":"3.1415"},"key3":{"type":"float","value":"-0.01"}}},"Integer":{"key1":{"type":"integer","value":"99"},"key2":{"type":"integer","value":"42"},"key3":{"type":"integer","value":"0"},"key4":{"type":"integer","value":"-17"}},"String":{"Literal":{"Multiline":{"lines":{"type":"string","value":"The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n"},"regex2":{"type":"string","value":"I [dw]on't need \\d{2} apples"}},"quoted":{"type":"string","value":"Tom \"Dubs\" Preston-Werner"},"regex":{"type":"string","value":"\u003c\\i\\c*\\s*\u003e"},"winpath":{"type":"string","value":"C:\\Users\\nodejs\\templates"},"winpath2":{"type":"string","value":"\\\\ServerX\\admin$\\system32\\"}},"Multiline":{"key1":{"type":"string","value":"One\nTwo"},"key2":{"type":"string","value":"One\nTwo"},"key3":{"type":"string","value":"One\nTwo"}},"Multilined":{"Singleline":{"key1":{"type":"string","value":"The quick brown fox jumps over the lazy dog."},"key2":{"type":"string","value":"The quick brown fox jumps over the lazy dog."},"key3":{"type":"string","value":"The quick brown fox jumps over the lazy dog."}}},"basic":{"type":"string","value":"I'm a string. \"You can quote me\". Name\u0009José\nLocation\u0009SF."}},"Table":{"key":{"type":"string","value":"value"}},"dog":{"tater":{"type":{"type":"string","value":"pug"}}},"fruit":[{"name":{"type":"string","value":"apple"},"physical":{"color":{"type":"string","value":"red"},"shape":{"type":"string","value":"round"}},"variety":[{"name":{"type":"string","value":"red delicious"}},{"name":{"type":"string","value":"granny smith"}}]},{"name":{"type":"string","value":"banana"},"variety":[{"name":{"type":"string","value":"plantain"}}]}],"products":[{"name":{"type":"string","value":"Hammer"},"sku":{"type":"integer","value":"738594937"}},{},{"color":{"type":"string","value":"gray"},"name":{"type":"string","value":"Nail"},"sku":{"type":"integer","value":"284758393"}}],"x":{"y":{"z":{"w":{}}}}}
|
|
@ -0,0 +1,182 @@
|
|||
# Comment
|
||||
# I am a comment. Hear me roar. Roar.
|
||||
|
||||
# Table
|
||||
# Tables (also known as hash tables or dictionaries) are collections of key/value pairs.
|
||||
# They appear in square brackets on a line by themselves.
|
||||
|
||||
[Table]
|
||||
|
||||
key = "value" # Yeah, you can do this.
|
||||
|
||||
# Nested tables are denoted by table names with dots in them. Name your tables whatever crap you please, just don't use #, ., [ or ].
|
||||
|
||||
[dog.tater]
|
||||
type = "pug"
|
||||
|
||||
# You don't need to specify all the super-tables if you don't want to. TOML knows how to do it for you.
|
||||
|
||||
# [x] you
|
||||
# [x.y] don't
|
||||
# [x.y.z] need these
|
||||
[x.y.z.w] # for this to work
|
||||
|
||||
# String
|
||||
# There are four ways to express strings: basic, multi-line basic, literal, and multi-line literal.
|
||||
# All strings must contain only valid UTF-8 characters.
|
||||
|
||||
[String]
|
||||
basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."
|
||||
|
||||
[String.Multiline]
|
||||
|
||||
# The following strings are byte-for-byte equivalent:
|
||||
key1 = "One\nTwo"
|
||||
key2 = """One\nTwo"""
|
||||
key3 = """
|
||||
One
|
||||
Two"""
|
||||
|
||||
[String.Multilined.Singleline]
|
||||
|
||||
# The following strings are byte-for-byte equivalent:
|
||||
key1 = "The quick brown fox jumps over the lazy dog."
|
||||
|
||||
key2 = """
|
||||
The quick brown \
|
||||
|
||||
|
||||
fox jumps over \
|
||||
the lazy dog."""
|
||||
|
||||
key3 = """\
|
||||
The quick brown \
|
||||
fox jumps over \
|
||||
the lazy dog.\
|
||||
"""
|
||||
|
||||
[String.Literal]
|
||||
|
||||
# What you see is what you get.
|
||||
winpath = 'C:\Users\nodejs\templates'
|
||||
winpath2 = '\\ServerX\admin$\system32\'
|
||||
quoted = 'Tom "Dubs" Preston-Werner'
|
||||
regex = '<\i\c*\s*>'
|
||||
|
||||
|
||||
[String.Literal.Multiline]
|
||||
|
||||
regex2 = '''I [dw]on't need \d{2} apples'''
|
||||
lines = '''
|
||||
The first newline is
|
||||
trimmed in raw strings.
|
||||
All other whitespace
|
||||
is preserved.
|
||||
'''
|
||||
|
||||
# Integer
|
||||
# Integers are whole numbers. Positive numbers may be prefixed with a plus sign.
|
||||
# Negative numbers are prefixed with a minus sign.
|
||||
|
||||
[Integer]
|
||||
key1 = +99
|
||||
key2 = 42
|
||||
key3 = 0
|
||||
key4 = -17
|
||||
|
||||
# Float
|
||||
# A float consists of an integer part (which may be prefixed with a plus or minus sign)
|
||||
# followed by a fractional part and/or an exponent part.
|
||||
|
||||
[Float.fractional]
|
||||
|
||||
# fractional
|
||||
key1 = +1.0
|
||||
key2 = 3.1415
|
||||
key3 = -0.01
|
||||
|
||||
[Float.exponent]
|
||||
|
||||
# exponent
|
||||
#key1 = 5e+22
|
||||
#key2 = 1e6
|
||||
#key3 = -2E-2
|
||||
|
||||
[Float.both]
|
||||
|
||||
# both
|
||||
#key = 6.626e-34
|
||||
|
||||
# Boolean
|
||||
# Booleans are just the tokens you're used to. Always lowercase.
|
||||
|
||||
[Booleans]
|
||||
True = true
|
||||
False = false
|
||||
|
||||
# Datetime
|
||||
# Datetimes are RFC 3339 dates.
|
||||
|
||||
[Datetime]
|
||||
key1 = 1979-05-27T07:32:00Z
|
||||
#key2 = 1979-05-27T00:32:00-07:00
|
||||
#key3 = 1979-05-27T00:32:00.999999-07:00
|
||||
|
||||
# Array
|
||||
# Arrays are square brackets with other primitives inside. Whitespace is ignored. Elements are separated by commas. Data types may not be mixed.
|
||||
|
||||
[Array]
|
||||
key1 = [ 1, 2, 3 ]
|
||||
key2 = [ "red", "yellow", "green" ]
|
||||
key3 = [ [ 1, 2 ], [3, 4, 5] ]
|
||||
key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok
|
||||
|
||||
#Arrays can also be multiline. So in addition to ignoring whitespace, arrays also ignore newlines between the brackets.
|
||||
# Terminating commas are ok before the closing bracket.
|
||||
|
||||
key5 = [
|
||||
1, 2, 3
|
||||
]
|
||||
key6 = [
|
||||
1,
|
||||
2, # this is ok
|
||||
]
|
||||
|
||||
# Array of Tables
|
||||
# These can be expressed by using a table name in double brackets.
|
||||
# Each table with the same double bracketed name will be an element in the array.
|
||||
# The tables are inserted in the order encountered.
|
||||
|
||||
[[products]]
|
||||
name = "Hammer"
|
||||
sku = 738594937
|
||||
|
||||
[[products]]
|
||||
|
||||
[[products]]
|
||||
name = "Nail"
|
||||
sku = 284758393
|
||||
color = "gray"
|
||||
|
||||
|
||||
# You can create nested arrays of tables as well.
|
||||
|
||||
[[fruit]]
|
||||
name = "apple"
|
||||
|
||||
[fruit.physical]
|
||||
color = "red"
|
||||
shape = "round"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "red delicious"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "granny smith"
|
||||
|
||||
[[fruit]]
|
||||
name = "banana"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "plantain"
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"array":{"key1":{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"},{"type":"integer","value":"3"}]},"key2":{"type":"array","value":[{"type":"string","value":"red"},{"type":"string","value":"yellow"},{"type":"string","value":"green"}]},"key3":{"type":"array","value":[{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"}]},{"type":"array","value":[{"type":"integer","value":"3"},{"type":"integer","value":"4"},{"type":"integer","value":"5"}]}]},"key4":{"type":"array","value":[{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"}]},{"type":"array","value":[{"type":"string","value":"a"},{"type":"string","value":"b"},{"type":"string","value":"c"}]}]},"key5":{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"},{"type":"integer","value":"3"}]},"key6":{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"}]}},"boolean":{"False":{"type":"bool","value":"false"},"True":{"type":"bool","value":"true"}},"datetime":{},"float":{"both":{},"exponent":{},"fractional":{"key1":{"type":"float","value":"1.0"},"key2":{"type":"float","value":"3.1415"},"key3":{"type":"float","value":"-0.01"}},"underscores":{}},"fruit":[{"name":{"type":"string","value":"apple"},"physical":{"color":{"type":"string","value":"red"},"shape":{"type":"string","value":"round"}},"variety":[{"name":{"type":"string","value":"red delicious"}},{"name":{"type":"string","value":"granny smith"}}]},{"name":{"type":"string","value":"banana"},"variety":[{"name":{"type":"string","value":"plantain"}}]}],"integer":{"key1":{"type":"integer","value":"99"},"key2":{"type":"integer","value":"42"},"key3":{"type":"integer","value":"0"},"key4":{"type":"integer","value":"-17"},"underscores":{"key1":{"type":"integer","value":"1000"},"key2":{"type":"integer","value":"5349221"},"key3":{"type":"integer","value":"12345"}}},"products":[{"name":{"type":"string","value":"Hammer"},"sku":{"type":"integer","value":"738594937"}},{},{"color":{"type":"string","value":"gray"},"name":{"type":"string","value":"Nail"},"sku":{"type":"integer","value":"284758393"}}],"string":{"basic":{"basic":{"type":"string","value":"I'm a string. \"You can quote me\". Name\u0009José\nLocation\u0009SF."}},"literal":{"multiline":{"lines":{"type":"string","value":"The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n"},"regex2":{"type":"string","value":"I [dw]on't need \\d{2} apples"}},"quoted":{"type":"string","value":"Tom \"Dubs\" Preston-Werner"},"regex":{"type":"string","value":"\u003c\\i\\c*\\s*\u003e"},"winpath":{"type":"string","value":"C:\\Users\\nodejs\\templates"},"winpath2":{"type":"string","value":"\\\\ServerX\\admin$\\system32\\"}},"multiline":{"continued":{"key1":{"type":"string","value":"The quick brown fox jumps over the lazy dog."},"key2":{"type":"string","value":"The quick brown fox jumps over the lazy dog."},"key3":{"type":"string","value":"The quick brown fox jumps over the lazy dog."}},"key1":{"type":"string","value":"One\nTwo"},"key2":{"type":"string","value":"One\nTwo"},"key3":{"type":"string","value":"One\nTwo"}}},"table":{"inline":{"name":{"first":{"type":"string","value":"Tom"},"last":{"type":"string","value":"Preston-Werner"}},"point":{"x":{"type":"integer","value":"1"},"y":{"type":"integer","value":"2"}}},"key":{"type":"string","value":"value"},"subtable":{"key":{"type":"string","value":"another value"}}},"x":{"y":{"z":{"w":{}}}}}
|
|
@ -0,0 +1,235 @@
|
|||
################################################################################
|
||||
## Comment
|
||||
|
||||
# Speak your mind with the hash symbol. They go from the symbol to the end of
|
||||
# the line.
|
||||
|
||||
|
||||
################################################################################
|
||||
## Table
|
||||
|
||||
# Tables (also known as hash tables or dictionaries) are collections of
|
||||
# key/value pairs. They appear in square brackets on a line by themselves.
|
||||
|
||||
[table]
|
||||
|
||||
key = "value" # Yeah, you can do this.
|
||||
|
||||
# Nested tables are denoted by table names with dots in them. Name your tables
|
||||
# whatever crap you please, just don't use #, ., [ or ].
|
||||
|
||||
[table.subtable]
|
||||
|
||||
key = "another value"
|
||||
|
||||
# You don't need to specify all the super-tables if you don't want to. TOML
|
||||
# knows how to do it for you.
|
||||
|
||||
# [x] you
|
||||
# [x.y] don't
|
||||
# [x.y.z] need these
|
||||
[x.y.z.w] # for this to work
|
||||
|
||||
|
||||
################################################################################
|
||||
## Inline Table
|
||||
|
||||
# Inline tables provide a more compact syntax for expressing tables. They are
|
||||
# especially useful for grouped data that can otherwise quickly become verbose.
|
||||
# Inline tables are enclosed in curly braces `{` and `}`. No newlines are
|
||||
# allowed between the curly braces unless they are valid within a value.
|
||||
|
||||
[table.inline]
|
||||
|
||||
name = { first = "Tom", last = "Preston-Werner" }
|
||||
point = { x = 1, y = 2 }
|
||||
|
||||
|
||||
################################################################################
|
||||
## String
|
||||
|
||||
# There are four ways to express strings: basic, multi-line basic, literal, and
|
||||
# multi-line literal. All strings must contain only valid UTF-8 characters.
|
||||
|
||||
[string.basic]
|
||||
|
||||
basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."
|
||||
|
||||
[string.multiline]
|
||||
|
||||
# The following strings are byte-for-byte equivalent:
|
||||
key1 = "One\nTwo"
|
||||
key2 = """One\nTwo"""
|
||||
key3 = """
|
||||
One
|
||||
Two"""
|
||||
|
||||
[string.multiline.continued]
|
||||
|
||||
# The following strings are byte-for-byte equivalent:
|
||||
key1 = "The quick brown fox jumps over the lazy dog."
|
||||
|
||||
key2 = """
|
||||
The quick brown \
|
||||
|
||||
|
||||
fox jumps over \
|
||||
the lazy dog."""
|
||||
|
||||
key3 = """\
|
||||
The quick brown \
|
||||
fox jumps over \
|
||||
the lazy dog.\
|
||||
"""
|
||||
|
||||
[string.literal]
|
||||
|
||||
# What you see is what you get.
|
||||
winpath = 'C:\Users\nodejs\templates'
|
||||
winpath2 = '\\ServerX\admin$\system32\'
|
||||
quoted = 'Tom "Dubs" Preston-Werner'
|
||||
regex = '<\i\c*\s*>'
|
||||
|
||||
|
||||
[string.literal.multiline]
|
||||
|
||||
regex2 = '''I [dw]on't need \d{2} apples'''
|
||||
lines = '''
|
||||
The first newline is
|
||||
trimmed in raw strings.
|
||||
All other whitespace
|
||||
is preserved.
|
||||
'''
|
||||
|
||||
|
||||
################################################################################
|
||||
## Integer
|
||||
|
||||
# Integers are whole numbers. Positive numbers may be prefixed with a plus sign.
|
||||
# Negative numbers are prefixed with a minus sign.
|
||||
|
||||
[integer]
|
||||
|
||||
key1 = +99
|
||||
key2 = 42
|
||||
key3 = 0
|
||||
key4 = -17
|
||||
|
||||
[integer.underscores]
|
||||
|
||||
# For large numbers, you may use underscores to enhance readability. Each
|
||||
# underscore must be surrounded by at least one digit.
|
||||
key1 = 1_000
|
||||
key2 = 5_349_221
|
||||
key3 = 1_2_3_4_5 # valid but inadvisable
|
||||
|
||||
|
||||
################################################################################
|
||||
## Float
|
||||
|
||||
# A float consists of an integer part (which may be prefixed with a plus or
|
||||
# minus sign) followed by a fractional part and/or an exponent part.
|
||||
|
||||
[float.fractional]
|
||||
|
||||
key1 = +1.0
|
||||
key2 = 3.1415
|
||||
key3 = -0.01
|
||||
|
||||
[float.exponent]
|
||||
|
||||
[float.both]
|
||||
|
||||
[float.underscores]
|
||||
|
||||
|
||||
################################################################################
|
||||
## Boolean
|
||||
|
||||
# Booleans are just the tokens you're used to. Always lowercase.
|
||||
|
||||
[boolean]
|
||||
|
||||
True = true
|
||||
False = false
|
||||
|
||||
|
||||
################################################################################
|
||||
## Datetime
|
||||
|
||||
# Datetimes are RFC 3339 dates.
|
||||
|
||||
[datetime]
|
||||
|
||||
#key1 = 1979-05-27T07:32:00Z
|
||||
#key2 = 1979-05-27T00:32:00-07:00
|
||||
#key3 = 1979-05-27T00:32:00.999999-07:00
|
||||
|
||||
|
||||
################################################################################
|
||||
## Array
|
||||
|
||||
# Arrays are square brackets with other primitives inside. Whitespace is
|
||||
# ignored. Elements are separated by commas. Data types may not be mixed.
|
||||
|
||||
[array]
|
||||
|
||||
key1 = [ 1, 2, 3 ]
|
||||
key2 = [ "red", "yellow", "green" ]
|
||||
key3 = [ [ 1, 2 ], [3, 4, 5] ]
|
||||
key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok
|
||||
|
||||
# Arrays can also be multiline. So in addition to ignoring whitespace, arrays
|
||||
# also ignore newlines between the brackets. Terminating commas are ok before
|
||||
# the closing bracket.
|
||||
|
||||
key5 = [
|
||||
1, 2, 3
|
||||
]
|
||||
key6 = [
|
||||
1,
|
||||
2, # this is ok
|
||||
]
|
||||
|
||||
|
||||
################################################################################
|
||||
## Array of Tables
|
||||
|
||||
# These can be expressed by using a table name in double brackets. Each table
|
||||
# with the same double bracketed name will be an element in the array. The
|
||||
# tables are inserted in the order encountered.
|
||||
|
||||
[[products]]
|
||||
|
||||
name = "Hammer"
|
||||
sku = 738594937
|
||||
|
||||
[[products]]
|
||||
|
||||
[[products]]
|
||||
|
||||
name = "Nail"
|
||||
sku = 284758393
|
||||
color = "gray"
|
||||
|
||||
|
||||
# You can create nested arrays of tables as well.
|
||||
|
||||
[[fruit]]
|
||||
name = "apple"
|
||||
|
||||
[fruit.physical]
|
||||
color = "red"
|
||||
shape = "round"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "red delicious"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "granny smith"
|
||||
|
||||
[[fruit]]
|
||||
name = "banana"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "plantain"
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"best-day-ever": {"type": "datetime", "value": "1987-07-05T17:45:00Z"},
|
||||
"numtheory": {
|
||||
"boring": {"type": "bool", "value": "false"},
|
||||
"perfection": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "integer", "value": "6"},
|
||||
{"type": "integer", "value": "28"},
|
||||
{"type": "integer", "value": "496"}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
best-day-ever = 1987-07-05T17:45:00Z
|
||||
|
||||
[numtheory]
|
||||
boring = false
|
||||
perfection = [6, 28, 496]
|
|
@ -0,0 +1 @@
|
|||
{"clients":{"data":{"type":"array","value":[{"type":"array","value":[{"type":"string","value":"gamma"},{"type":"string","value":"delta"}]},{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"}]}]},"hosts":{"type":"array","value":[{"type":"string","value":"alpha"},{"type":"string","value":"omega"}]}},"database":{"connection_max":{"type":"integer","value":"5000"},"enabled":{"type":"bool","value":"true"},"ports":{"type":"array","value":[{"type":"integer","value":"8001"},{"type":"integer","value":"8001"},{"type":"integer","value":"8002"}]},"server":{"type":"string","value":"192.168.1.1"}},"owner":{"bio":{"type":"string","value":"GitHub Cofounder \u0026 CEO\nLikes tater tots and beer."},"dob":{"type":"datetime","value":"1979-05-27T07:32:00Z"},"name":{"type":"string","value":"Tom Preston-Werner"},"organization":{"type":"string","value":"GitHub"}},"products":[{"name":{"type":"string","value":"Hammer"},"sku":{"type":"integer","value":"738594937"}},{"color":{"type":"string","value":"gray"},"name":{"type":"string","value":"Nail"},"sku":{"type":"integer","value":"284758393"}}],"servers":{"alpha":{"dc":{"type":"string","value":"eqdc10"},"ip":{"type":"string","value":"10.0.0.1"}},"beta":{"country":{"type":"string","value":"中国"},"dc":{"type":"string","value":"eqdc10"},"ip":{"type":"string","value":"10.0.0.2"}}},"title":{"type":"string","value":"TOML Example"}}
|
|
@ -0,0 +1,47 @@
|
|||
# This is a TOML document. Boom.
|
||||
|
||||
title = "TOML Example"
|
||||
|
||||
[owner]
|
||||
name = "Tom Preston-Werner"
|
||||
organization = "GitHub"
|
||||
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
|
||||
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
|
||||
|
||||
[database]
|
||||
server = "192.168.1.1"
|
||||
ports = [ 8001, 8001, 8002 ]
|
||||
connection_max = 5000
|
||||
enabled = true
|
||||
|
||||
[servers]
|
||||
|
||||
# You can indent as you please. Tabs or spaces. TOML don't care.
|
||||
[servers.alpha]
|
||||
ip = "10.0.0.1"
|
||||
dc = "eqdc10"
|
||||
|
||||
[servers.beta]
|
||||
ip = "10.0.0.2"
|
||||
dc = "eqdc10"
|
||||
country = "中国" # This should be parsed as UTF-8
|
||||
|
||||
[clients]
|
||||
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
|
||||
|
||||
# Line breaks are OK when inside arrays
|
||||
hosts = [
|
||||
"alpha",
|
||||
"omega"
|
||||
]
|
||||
|
||||
# Products
|
||||
|
||||
[[products]]
|
||||
name = "Hammer"
|
||||
sku = 738594937
|
||||
|
||||
[[products]]
|
||||
name = "Nail"
|
||||
sku = 284758393
|
||||
color = "gray"
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"pi": {"type": "float", "value": "3.14"},
|
||||
"negpi": {"type": "float", "value": "-3.14"}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
pi = 3.14
|
||||
negpi = -3.14
|
|
@ -0,0 +1 @@
|
|||
{"the":{"hard":{"another_test_string":{"type":"string","value":" Same thing, but with a string #"},"bit#":{"multi_line_array":{"type":"array","value":[{"type":"string","value":"]"}]},"what?":{"type":"string","value":"You don't think some user won't do that?"}},"harder_test_string":{"type":"string","value":" And when \"'s are in the string, along with # \""},"test_array":{"type":"array","value":[{"type":"string","value":"] "},{"type":"string","value":" # "}]},"test_array2":{"type":"array","value":[{"type":"string","value":"Test #11 ]proved that"},{"type":"string","value":"Experiment #9 was a success"}]}},"test_string":{"type":"string","value":"You'll hate me after this - #"}}}
|
|
@ -0,0 +1,33 @@
|
|||
# Test file for TOML
|
||||
# Only this one tries to emulate a TOML file written by a user of the kind of parser writers probably hate
|
||||
# This part you'll really hate
|
||||
|
||||
[the]
|
||||
test_string = "You'll hate me after this - #" # " Annoying, isn't it?
|
||||
|
||||
[the.hard]
|
||||
test_array = [ "] ", " # "] # ] There you go, parse this!
|
||||
test_array2 = [ "Test #11 ]proved that", "Experiment #9 was a success" ]
|
||||
# You didn't think it'd as easy as chucking out the last #, did you?
|
||||
another_test_string = " Same thing, but with a string #"
|
||||
harder_test_string = " And when \"'s are in the string, along with # \"" # "and comments are there too"
|
||||
# Things will get harder
|
||||
|
||||
[the.hard."bit#"]
|
||||
"what?" = "You don't think some user won't do that?"
|
||||
multi_line_array = [
|
||||
"]",
|
||||
# ] Oh yes I did
|
||||
]
|
||||
|
||||
# Each of the following keygroups/key value pairs should produce an error. Uncomment to them to test
|
||||
|
||||
#[error] if you didn't catch this, your parser is broken
|
||||
#string = "Anything other than tabs, spaces and newline after a keygroup or key value pair has ended should produce an error unless it is a comment" like this
|
||||
#array = [
|
||||
# "This might most likely happen in multiline arrays",
|
||||
# Like here,
|
||||
# "or here,
|
||||
# and here"
|
||||
# ] End of array comment, forgot the #
|
||||
#number = 3.14 pi <--again forgot the #
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"a": {
|
||||
"better": {"type": "integer", "value": "43"},
|
||||
"b": {
|
||||
"c": {
|
||||
"answer": {"type": "integer", "value": "42"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
[a.b.c]
|
||||
answer = 42
|
||||
|
||||
[a]
|
||||
better = 43
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"a": {
|
||||
"better": {"type": "integer", "value": "43"},
|
||||
"b": {
|
||||
"c": {
|
||||
"answer": {"type": "integer", "value": "42"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче