diff --git a/.travis.yml b/.travis.yml
index b208245..94efa46 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,34 @@
+sudo: false
+dist: trusty
language: rust
-rust:
-- stable
-- beta
-- nightly
-script:
-- cargo build --verbose
-- cargo test --verbose
+
+cache:
+- cargo
+
+before_cache:
+- rm -r $TRAVIS_BUILD_DIR/target/debug
+
+jobs:
+ include:
+ - os: linux
+ rust: stable
+ - os: linux
+ rust: beta
+ - os: linux
+ rust: nightly
+
+ # deploy
+ - stage: publish
+ os: linux
+ rust: stable
+ env:
+ # CARGO_TOKEN
+ - secure: "IPm2lHXAtXUY8pJtUMRV+jgLSCPKRJQyP3ax5aUhyaGlR83w4krEOcIpqQgu5a4rgw3SZUjDhxQMsRbzRZIehU+C3u2LTDpn0yycPnGmFRKVqnoS5dHdFMQ6IKLDZQK99MFZ/vMThipImnS9WFm2D1X/8XS31Mpn81Y7o54rIHk="
+ install: true
+ script: true
+
+ deploy:
+ - provider: script
+ script: 'cargo publish --verbose --token=$CARGO_TOKEN'
+ on:
+ tags: true
diff --git a/Cargo.toml b/Cargo.toml
index bdb5e22..7b92f07 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,20 +3,21 @@
name = "quire"
description = "A YAML-based configuration parsing library"
license = "MIT/Apache-2.0"
-readme = "README.rst"
+readme = "README.md"
keywords = ["config", "yaml", "parser"]
homepage = "http://github.com/tailhook/rust-quire"
-version = "0.2.3"
+categories = ["config"]
+version = "0.4.1"
authors = ["paul@colomiets.name"]
[dependencies]
-rustc-serialize = "0.3"
-quick-error = "1.0.0"
-regex = "0.1.80"
-humantime = "1.0.0"
-num-traits = "0.1.36"
+quick-error = "1.2.0"
+num-traits = "0.2.5"
humannum = "0.1.0"
serde = "1.0.10"
[dev-dependencies]
serde_derive = "1.0.10"
+serde_json = "1.0.2"
+serde-transcode = "1.0.0"
+serde-humantime = "0.1.1"
diff --git a/README.rst b/README.md
similarity index 57%
rename from README.rst
rename to README.md
index 7bf61c9..0910898 100644
--- a/README.rst
+++ b/README.md
@@ -1,18 +1,19 @@
-==========
Rust Quire
==========
-:Status: Beta
+*Status: Beta*
-The ``rust-quire`` is a Rust_ port for quire_ configuration parsing library.
-It also contains YAML_ parser (however, it tuned for configuration parsing
-rather than generic YAML parser, e.g. it assumes YAML always fits memory).
+The ``rust-quire`` is a [Rust][1] port for [quire][2] configuration parsing
+library. It also contains [YAML][3] parser (however, it tuned for
+configuration parsing rather than generic YAML parser, e.g. it assumes YAML
+always fits memory).
+See `doc/` for additional information.
-.. _quire: http://github.com/tailhook/quire
-.. _YAML: http://yaml.org
-.. _Rust: http://rust-lang.org
+[1]: http://github.com/tailhook/quire
+[2]: http://yaml.org
+[3]: http://rust-lang.org
License
diff --git a/bulk.yaml b/bulk.yaml
new file mode 100644
index 0000000..bf99854
--- /dev/null
+++ b/bulk.yaml
@@ -0,0 +1,15 @@
+minimum-bulk: v0.4.5
+
+versions:
+
+- file: Cargo.toml
+ block-start: ^\[package\]
+ block-end: ^\[.*\]
+ regex: ^version\s*=\s*"(\S+)"
+
+- file: doc/conf.py
+ regex: ^version\s*=\s*'(\S+)'
+ partial-version: ^\d+\.\d+
+
+- file: doc/conf.py
+ regex: ^release\s*=\s*'(\S+)'
diff --git a/doc/README.md b/doc/README.md
new file mode 100644
index 0000000..d27ec78
--- /dev/null
+++ b/doc/README.md
@@ -0,0 +1,11 @@
+doc/
+====
+
+In this directory documentation in [sphinx-doc][1] format.
+
+Type `make html` to get HTML generated. Expect to get errors
+when *sphinx-doc* is not installed on your system.
+
+With `make` you get a list of possible output formats.
+
+[1]: http://www.sphinx-doc.org/
diff --git a/doc/conf.py b/doc/conf.py
index 029ed57..98d8303 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -42,16 +42,16 @@
# General information about the project.
project = 'Quire'
-copyright = '2013, Paul Colomiets'
+copyright = '2013-2019, Paul Colomiets'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
-version = '2.0'
+version = '0.4'
# The full version, including alpha/beta/rc tags.
-release = '2.0-beta1'
+release = '0.4.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/doc/index.rst b/doc/index.rst
index 3abeeb4..69627be 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -20,6 +20,7 @@ Contents:
:maxdepth: 2
user
+ programmer
.. _Yaml: http://yaml.org
diff --git a/doc/programmer.rst b/doc/programmer.rst
new file mode 100644
index 0000000..5f1373e
--- /dev/null
+++ b/doc/programmer.rst
@@ -0,0 +1,44 @@
+
+================
+Programmer Guide
+================
+
+As a programmer you are looking for source code.
+You found it, here it is.
+
+.. literalinclude:: ../examples/simple.rs
+ :language: rust
+
+
+Key link
+========
+
+The key link between configuration file
+and the executable programm that you are writen
+is the configuration ``struct``.
+
+In the demo source code is that struct named *Config*
+and is *cfg* a variable of type Config.
+
+
+config.yaml
+===========
+
+Nothing of ``Config`` or ``cfg`` needs to be in the YAML.
+But the field names **must**.
+
+Here the *.yaml* that goes with the above example source code.
+
+
+.. literalinclude:: ../examples/config.yaml
+ :language: yaml
+
+
+See also
+========
+
+The documentation of `serde `_.
+
+Our ``examples`` directory.
+
+`Reverse dependencies `_.
diff --git a/doc/user.rst b/doc/user.rst
index 35f38d4..5df98c6 100644
--- a/doc/user.rst
+++ b/doc/user.rst
@@ -190,7 +190,7 @@ node that contains tag. For example:
.. code-block:: yaml
# config.yaml
- items: !Include items.yaml
+ items: !*Include items.yaml
.. code-block:: yaml
@@ -209,6 +209,89 @@ Is equivalent of:
- banana
+.. _include-seq:
+
+Include Sequences of Files
+--------------------------
+
+The ``!*IncludeSeq`` tag includes files matched by glob as a sequence:
+
+.. code-block:: yaml
+
+ items: !*IncludeSeq "fruits/*.yaml"
+
+Can be parsed as:
+
+.. code-block:: yaml
+
+ items:
+ - apple
+ - banana
+ - cherry
+
+This depends on the exact application, but usually files returned by `glob`
+are sorted in alphabetical order. All the power of globs is supported, so you
+can do:
+
+.. code-block:: yaml
+
+ items: !*IncludeSeq "files/**/*.yaml"
+
+Another trick is merge multiple files, each with it's own set of keys into
+a single one (see map-merge_ below):
+
+.. code-block:: yaml
+
+ # file1.yaml
+ key1: 1
+ key2: 2
+ # file2.yaml
+ key3: 3
+ key4: 4
+ # main.yaml
+ <<: !*IncludeSeq "configs/*.yaml"
+
+This results into the following config:
+
+.. code-block:: yaml
+
+ key1: 1
+ key2: 2
+ key3: 3
+ key4: 4
+
+Note: merging is not recursive, i.e. top level keys are considered as a whole,
+even if they are dicts.
+
+
+.. _include-map:
+
+Include Mapping from Files
+--------------------------
+
+The ``!*IncludeMap`` tag works similarly to ``!*IncludeSeq`` but requires to
+mark part of a name used as a key. For example:
+
+.. code-block:: yaml
+
+ items: !*IncludeMap "fruits/(*).yaml"
+
+Might result into the folowing:
+
+.. code-block:: yaml
+
+ items:
+ apple: { color: orange }
+ banana: { color: yellow }
+ cherry: { color: red }
+
+You can parenthesize any part as well as a whole path:
+
+.. code-block:: yaml
+
+ files: !*IncludeMap "(**/*.yaml)"
+
+
.. _map-merge:
Merging Mappings
diff --git a/examples/README.text b/examples/README.text
new file mode 100644
index 0000000..d5ff58d
--- /dev/null
+++ b/examples/README.text
@@ -0,0 +1,10 @@
+
+Run
+ cargo build --examples
+anywhere to get examples been compiled.
+
+For
+ cargo run --example simple
+be in the top directory of the project.
+That is for having the correct start point
+to get path to config.yaml right.
diff --git a/examples/config.yaml b/examples/config.yaml
new file mode 100644
index 0000000..ab8e2c9
--- /dev/null
+++ b/examples/config.yaml
@@ -0,0 +1,8 @@
+#
+---
+item1: foo
+# comment
+item2: bar
+_item3: underscore used to comment out a keyword
+...
+# l l
diff --git a/examples/simple.rs b/examples/simple.rs
new file mode 100644
index 0000000..ff7675b
--- /dev/null
+++ b/examples/simple.rs
@@ -0,0 +1,31 @@
+extern crate quire;
+#[macro_use] extern crate serde_derive;
+use quire::{parse_config, Options};
+use quire::validate::{Structure, Scalar};
+
+#[derive(Deserialize)]
+#[allow(dead_code)]
+struct Config {
+ item1: String,
+ item2: Option,
+}
+
+fn validator() -> Structure<'static> {
+ Structure::new()
+ .member("item1", Scalar::new())
+ .member("item2", Scalar::new().optional())
+}
+
+fn work(cfg: &Config) {
+ println!("item1 is {}.", cfg.item1);
+ //intln!("item2 is {}.", cfg.item2);
+ // hey, this is just demonstration code ...
+}
+
+fn main() {
+ let cfg: Config;
+ cfg = parse_config("examples/config.yaml",
+ &validator(), &Options::default())
+ .expect("valid config");
+ work(&cfg)
+}
diff --git a/src/ast.rs b/src/ast.rs
index ad6a7a0..f993a92 100644
--- a/src/ast.rs
+++ b/src/ast.rs
@@ -8,7 +8,7 @@ use std::collections::BTreeMap;
use super::tokenizer::Pos;
use super::errors::{Error, ErrorCollector};
use super::parser::Node as P;
-use super::parser::{Directive, Node, Document};
+use super::parser::{Node, Document};
use super::tokenizer::TokenType as T;
use self::Ast::*;
use self::NullKind::*;
@@ -118,7 +118,6 @@ impl Ast {
struct Context<'a, 'b: 'a> {
options: &'a Options<'b>,
- directives: Vec>,
err: &'a ErrorCollector,
}
@@ -179,6 +178,14 @@ impl<'a, 'b: 'a> Context<'a, 'b> {
return self.options.include(&tok.start,
&Include::File { filename: val }, self.err);
}
+ P::Scalar(Some("!*IncludeSeq"), _anch, ref val, ref tok) => {
+ return self.options.include(&tok.start,
+ &Include::Sequence { pattern: val }, self.err);
+ }
+ P::Scalar(Some("!*IncludeMap"), _anch, ref val, ref tok) => {
+ return self.options.include(&tok.start,
+ &Include::Mapping { pattern: val }, self.err);
+ }
P::Scalar(ref tag, _, ref val, ref tok) => {
let pos = tok.start.clone();
let tag = self.string_to_tag(&pos, tag);
@@ -371,7 +378,6 @@ impl<'a, 'b: 'a> Context<'a, 'b> {
pub fn process(opt: &Options, doc: Document, err: &ErrorCollector) -> Ast {
let mut ctx = Context {
options: opt,
- directives: doc.directives,
err: err,
};
return ctx.process(&doc.root);
diff --git a/src/de.rs b/src/de.rs
index f273c53..b665c86 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -1,38 +1,289 @@
-use std::marker::PhantomData;
-use std::ops::{Neg, AddAssign, MulAssign};
-use std::str::FromStr;
+use std::fmt::Write;
+use std::collections::BTreeMap;
+use std::collections::btree_map;
+use std::iter::{Peekable, Enumerate};
+use std::mem::replace;
use std::slice;
+use std::str::FromStr;
-use serde::de::{self, Deserialize, DeserializeSeed, Visitor, SeqAccess,
- MapAccess, EnumAccess, VariantAccess, IntoDeserializer};
+use serde::de::{self, DeserializeSeed, Visitor, SeqAccess};
+use serde::de::{MapAccess, EnumAccess, VariantAccess, IntoDeserializer};
-use ast::Ast;
-use ast::Ast as A;
-use errors::{Error, ErrorCollector};
+use ast::{Ast, Ast as A, Tag};
+use errors::{Error, add_info};
type Result = ::std::result::Result;
+#[derive(PartialEq, Eq, Copy, Clone, Debug)]
+pub enum Mode {
+ Normal,
+ Enum,
+}
+
+#[derive(Debug)]
pub struct Deserializer<'a> {
ast: &'a Ast,
- err: ErrorCollector,
+ mode: Mode,
path: String,
}
-struct ListVisitor<'a, 'b: 'a>(slice::Iter<'b, Ast>, &'a mut Deserializer<'b>);
+
+struct ListVisitor<'a, 'b: 'a>(Enumerate>, &'a mut Deserializer<'b>);
+struct MapVisitor<'a, 'b: 'a>(Peekable>,
+ usize, &'a mut Deserializer<'b>);
+struct EnumVisitor<'a, 'b: 'a>(&'a mut Deserializer<'b>);
+struct VariantVisitor<'a, 'b: 'a>(&'a mut Deserializer<'b>);
+struct KeyDeserializer(String);
impl<'de> Deserializer<'de> {
- // By convention, `Deserializer` constructors are named like `from_xyz`.
- // That way basic use cases are satisfied by something like
- // `serde_json::from_str(...)` while advanced use cases that require a
- // deserializer can make one with `serde_json::Deserializer::from_str(...)`.
- pub fn new<'x>(ast: &'x Ast, err: &ErrorCollector) -> Deserializer<'x> {
+ pub fn new<'x>(ast: &'x Ast) -> Deserializer<'x> {
Deserializer {
ast: &ast,
- err: err.clone(),
+ mode: Mode::Normal,
path: "".to_string(),
}
}
+
+ fn map_err(&self, result: Result) -> Result {
+ add_info(&self.ast.pos(), &self.path, result)
+ }
+}
+
+impl<'a> de::Deserializer<'a> for KeyDeserializer {
+ type Error = Error;
+
+ fn deserialize_any(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ visitor.visit_string(self.0)
+ }
+ fn deserialize_bool(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ let value = match &self.0[..] {
+ "true" => true,
+ "false" => false,
+ _ => {
+ let e: Error = de::Error::custom(format!("bad boolean {:?}", self.0));
+ return Err(e);
+ }
+ };
+ visitor.visit_bool(value)
+ }
+
+ fn deserialize_i8(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ visitor.visit_i8(FromStr::from_str(&self.0).map_err(Error::custom)?)
+ }
+
+ fn deserialize_i16(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ visitor.visit_i16(FromStr::from_str(&self.0).map_err(Error::custom)?)
+ }
+
+ fn deserialize_i32(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ visitor.visit_i32(FromStr::from_str(&self.0).map_err(Error::custom)?)
+ }
+
+ fn deserialize_i64(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ visitor.visit_i64(FromStr::from_str(&self.0).map_err(Error::custom)?)
+ }
+
+ fn deserialize_u8(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ visitor.visit_u8(FromStr::from_str(&self.0).map_err(Error::custom)?)
+ }
+
+ fn deserialize_u16(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ visitor.visit_u16(FromStr::from_str(&self.0).map_err(Error::custom)?)
+ }
+
+ fn deserialize_u32(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ visitor.visit_u32(FromStr::from_str(&self.0).map_err(Error::custom)?)
+ }
+
+ fn deserialize_u64(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ visitor.visit_u64(FromStr::from_str(&self.0).map_err(Error::custom)?)
+ }
+
+ fn deserialize_f32(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ visitor.visit_f32(FromStr::from_str(&self.0).map_err(Error::custom)?)
+ }
+
+ fn deserialize_f64(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ visitor.visit_f64(FromStr::from_str(&self.0).map_err(Error::custom)?)
+ }
+
+ fn deserialize_char(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ let mut chars = self.0.chars();
+ let val = (chars.next()
+ .ok_or_else(|| {
+ de::Error::custom("single character expected")
+ }) as Result<_>)?;
+ if chars.next().is_some() {
+ return Err(de::Error::custom("single character expected"))
+ }
+ visitor.visit_char(val)
+ }
+
+ fn deserialize_str(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ visitor.visit_str(&self.0)
+ }
+
+ fn deserialize_string(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ visitor.visit_string(self.0)
+ }
+
+ // The `Serializer` implementation on the previous page serialized byte
+ // arrays as JSON arrays of bytes. Handle that representation here.
+ fn deserialize_bytes(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ visitor.visit_bytes(self.0.as_bytes())
+ }
+
+ fn deserialize_byte_buf(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ visitor.visit_bytes(self.0.as_bytes())
+ }
+
+ fn deserialize_option(self, visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ match &self.0[..] {
+ "" | "~" | "null" => {
+ return visitor.visit_none();
+ }
+ _ => {
+ return visitor.visit_some(self)
+ }
+ }
+ }
+
+ fn deserialize_unit(self, _visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ unimplemented!();
+ }
+
+ fn deserialize_unit_struct(
+ self,
+ _name: &'static str,
+ _visitor: V
+ ) -> Result
+ where V: Visitor<'a>
+ {
+ unimplemented!();
+ }
+
+ fn deserialize_newtype_struct(
+ self,
+ _name: &'static str,
+ visitor: V
+ ) -> Result
+ where V: Visitor<'a>
+ {
+ visitor.visit_newtype_struct(self)
+ }
+
+ fn deserialize_seq(self, _visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ Err(de::Error::custom("sequence can't be mapping key in quire"))
+ }
+
+ fn deserialize_tuple(
+ self,
+ _len: usize,
+ _visitor: V
+ ) -> Result
+ where V: Visitor<'a>
+ {
+ Err(de::Error::custom("tuple can't be mapping key in quire"))
+ }
+
+ // Tuple structs look just like sequences in JSON.
+ fn deserialize_tuple_struct(
+ self,
+ _name: &'static str,
+ _len: usize,
+ _visitor: V
+ ) -> Result
+ where V: Visitor<'a>
+ {
+ Err(de::Error::custom("tuple struct can't be mapping key in quire"))
+ }
+
+ fn deserialize_map(self, _visitor: V) -> Result
+ where V: Visitor<'a>
+ {
+ Err(de::Error::custom("mapping can't be mapping key in quire"))
+ }
+ fn deserialize_struct(
+ self,
+ _name: &'static str,
+ _fields: &'static [&'static str],
+ _visitor: V
+ ) -> Result
+ where V: Visitor<'a>
+ {
+ Err(de::Error::custom("struct can't be mapping key in quire"))
+ }
+
+ fn deserialize_enum(
+ self,
+ _name: &'static str,
+ _variants: &'static [&'static str],
+ _visitor: V
+ ) -> Result
+ where V: Visitor<'a>
+ {
+ // TODO(tailhook) some support might work
+ Err(de::Error::custom("enum can't be mapping key in quire"))
+ }
+
+ fn deserialize_identifier(
+ self,
+ visitor: V
+ ) -> Result
+ where V: Visitor<'a>
+ {
+ visitor.visit_string(self.0)
+ }
+
+ fn deserialize_ignored_any(
+ self,
+ visitor: V
+ ) -> Result
+ where V: Visitor<'a>
+ {
+ self.deserialize_unit(visitor)
+ }
}
impl<'de: 'a, 'a, 'b> de::Deserializer<'de> for &'a mut Deserializer<'b> {
@@ -44,7 +295,12 @@ impl<'de: 'a, 'a, 'b> de::Deserializer<'de> for &'a mut Deserializer<'b> {
fn deserialize_any(self, visitor: V) -> Result
where V: Visitor<'de>
{
- unimplemented!();
+ match *self.ast {
+ A::Map(..) => self.deserialize_map(visitor),
+ A::Seq(..) => self.deserialize_seq(visitor),
+ A::Scalar(..) => self.deserialize_string(visitor),
+ A::Null(..) => self.deserialize_unit(visitor),
+ }
}
// Uses the `parse_bool` parsing function defined above to read the JSON
@@ -159,7 +415,7 @@ impl<'de: 'a, 'a, 'b> de::Deserializer<'de> for &'a mut Deserializer<'b> {
}
ref node => {
Err(Error::decode_error(&node.pos(), &self.path,
- format!("Can't parse {:?} as char", node)))
+ format!("expected a single character, got {}", node)))
}
};
visitor.visit_char(c?)
@@ -167,25 +423,25 @@ impl<'de: 'a, 'a, 'b> de::Deserializer<'de> for &'a mut Deserializer<'b> {
fn deserialize_str(self, visitor: V) -> Result
where V: Visitor<'de>
- {
- unimplemented!();
- }
-
- fn deserialize_string(self, visitor: V) -> Result
- where V: Visitor<'de>
{
let val = match *self.ast {
- A::Scalar(ref pos, _, _, ref val) => {
+ A::Scalar(_, _, _, ref val) => {
val
}
ref node => {
return Err(Error::decode_error(&node.pos(), &self.path,
- format!("Can't parse {:?} as char", node)))
+ format!("expected string, got {}", node)))
}
};
visitor.visit_str(val)
}
+ fn deserialize_string(self, visitor: V) -> Result
+ where V: Visitor<'de>
+ {
+ self.deserialize_str(visitor)
+ }
+
// The `Serializer` implementation on the previous page serialized byte
// arrays as JSON arrays of bytes. Handle that representation here.
fn deserialize_bytes(self, _visitor: V) -> Result
@@ -204,7 +460,10 @@ impl<'de: 'a, 'a, 'b> de::Deserializer<'de> for &'a mut Deserializer<'b> {
where V: Visitor<'de>
{
match *self.ast {
- A::Null(..) => {
+ A::Null(_, Tag::NonSpecific, _) => {
+ return visitor.visit_none()
+ }
+ A::Null(_, Tag::LocalTag(_), _) if self.mode == Mode::Enum => {
return visitor.visit_none()
}
_ => {
@@ -217,7 +476,7 @@ impl<'de: 'a, 'a, 'b> de::Deserializer<'de> for &'a mut Deserializer<'b> {
fn deserialize_unit(self, visitor: V) -> Result
where V: Visitor<'de>
{
- unimplemented!()
+ visitor.visit_unit()
}
// Unit struct means a named value containing no data.
@@ -228,7 +487,7 @@ impl<'de: 'a, 'a, 'b> de::Deserializer<'de> for &'a mut Deserializer<'b> {
) -> Result
where V: Visitor<'de>
{
- unimplemented!()
+ visitor.visit_unit()
}
// As is done here, serializers are encouraged to treat newtype structs as
@@ -241,25 +500,30 @@ impl<'de: 'a, 'a, 'b> de::Deserializer<'de> for &'a mut Deserializer<'b> {
) -> Result
where V: Visitor<'de>
{
- unimplemented!()
+ visitor.visit_newtype_struct(self)
}
// Deserialization of compound types like sequences and maps happens by
// passing the visitor an "Access" object that gives it the ability to
// iterate through the data contained in the sequence.
- fn deserialize_seq(mut self, visitor: V) -> Result
+ fn deserialize_seq(self, visitor: V) -> Result
where V: Visitor<'de>
{
match *self.ast {
A::Seq(_, _, ref seq) => {
let ast = self.ast;
- let result = visitor.visit_seq(ListVisitor(seq.iter(), self));
+ let result = visitor.visit_seq(
+ ListVisitor(seq.iter().enumerate(), self));
+ let result = self.map_err(result);
self.ast = ast;
return result;
}
+ A::Null(..) => {
+ return visitor.visit_seq(Vec::<()>::new().into_deserializer());
+ }
ref node => {
return Err(Error::decode_error(&node.pos(), &self.path,
- format!("sequence expected got {}", node)))
+ format!("sequence expected, got {}", node)))
}
}
}
@@ -273,7 +537,7 @@ impl<'de: 'a, 'a, 'b> de::Deserializer<'de> for &'a mut Deserializer<'b> {
fn deserialize_tuple(
self,
_len: usize,
- visitor: V
+ _visitor: V
) -> Result
where V: Visitor<'de>
{
@@ -285,17 +549,34 @@ impl<'de: 'a, 'a, 'b> de::Deserializer<'de> for &'a mut Deserializer<'b> {
self,
_name: &'static str,
_len: usize,
- visitor: V
+ _visitor: V
) -> Result
where V: Visitor<'de>
{
unimplemented!();
}
- fn deserialize_map(mut self, visitor: V) -> Result
+ fn deserialize_map(self, visitor: V) -> Result
where V: Visitor<'de>
{
- unimplemented!();
+ match *self.ast {
+ A::Map(_, _, ref map) => {
+ let ast = self.ast;
+ let result = visitor.visit_map(
+ MapVisitor(map.iter().peekable(), self.path.len(), self));
+ let result = self.map_err(result);
+ self.ast = ast;
+ return result;
+ }
+ A::Null(..) => {
+ return visitor.visit_map(
+ BTreeMap::<(), ()>::new().into_deserializer());
+ }
+ ref node => {
+ return Err(Error::decode_error(&node.pos(), &self.path,
+ format!("mapping expected, got {}", node)))
+ }
+ }
}
// Structs look just like maps in JSON.
@@ -312,7 +593,7 @@ impl<'de: 'a, 'a, 'b> de::Deserializer<'de> for &'a mut Deserializer<'b> {
) -> Result
where V: Visitor<'de>
{
- unimplemented!();
+ self.deserialize_map(visitor)
}
fn deserialize_enum(
@@ -323,7 +604,10 @@ impl<'de: 'a, 'a, 'b> de::Deserializer<'de> for &'a mut Deserializer<'b> {
) -> Result
where V: Visitor<'de>
{
- unimplemented!();
+ let mode = replace(&mut self.mode, Mode::Enum);
+ let result = visitor.visit_enum(EnumVisitor(self));
+ self.mode = mode;
+ return result;
}
// An identifier in Serde is the type that identifies a field of a struct or
@@ -336,7 +620,20 @@ impl<'de: 'a, 'a, 'b> de::Deserializer<'de> for &'a mut Deserializer<'b> {
) -> Result
where V: Visitor<'de>
{
- unimplemented!();
+ match *self.ast.tag() {
+ Tag::GlobalTag(_) => unimplemented!(),
+ Tag::LocalTag(ref val) => Ok(visitor.visit_str::(val)?),
+ Tag::NonSpecific => match *self.ast {
+ A::Scalar(_, _, _, ref val) => {
+ Ok(visitor.visit_string::(val.replace("-", "_"))?)
+ }
+ ref node => {
+ return Err(Error::decode_error(&node.pos(), &self.path,
+ format!("identifier (string, or tag) \
+ expected, got {}", node)))
+ }
+ },
+ }
}
// Like `deserialize_any` but indicates to the `Deserializer` that it makes
@@ -356,7 +653,7 @@ impl<'de: 'a, 'a, 'b> de::Deserializer<'de> for &'a mut Deserializer<'b> {
) -> Result
where V: Visitor<'de>
{
- unimplemented!();
+ self.deserialize_unit(visitor)
}
}
@@ -385,9 +682,13 @@ impl<'de, 'a, 'b: 'a> SeqAccess<'de> for ListVisitor<'a, 'b> {
where T: DeserializeSeed<'de>
{
match self.0.next() {
- Some(x) => {
+ Some((idx, x)) => {
self.1.ast = x;
- seed.deserialize(&mut *self.1).map(Some)
+ let plen = self.1.path.len();
+ write!(&mut self.1.path, "[{}]", idx).unwrap();
+ let result = seed.deserialize(&mut *self.1).map(Some);
+ self.1.path.truncate(plen);
+ return result;
}
None => {
return Ok(None);
@@ -396,11 +697,98 @@ impl<'de, 'a, 'b: 'a> SeqAccess<'de> for ListVisitor<'a, 'b> {
}
}
+impl<'de, 'a, 'b: 'a> MapAccess<'de> for MapVisitor<'a, 'b> {
+ type Error = Error;
+
+ fn next_key_seed(&mut self, seed: T) -> Result