yaml-rust2
-> saphyr-parser
* Drop non-parser files * Rename everything * Fix tests * Start rewriting a bit of docs
This commit is contained in:
parent
787403daa1
commit
6b01b554e0
24 changed files with 271 additions and 2777 deletions
|
@ -1,32 +1,32 @@
|
||||||
[package]
|
[package]
|
||||||
name = "yaml-rust2"
|
name = "saphyr-parser"
|
||||||
version = "0.8.0"
|
version = "0.0.1"
|
||||||
authors = [
|
authors = [
|
||||||
"Yuheng Chen <yuhengchen@sensetime.com>",
|
"Yuheng Chen <yuhengchen@sensetime.com>",
|
||||||
"Ethiraric <ethiraric@gmail.com>",
|
"Ethiraric <ethiraric@gmail.com>",
|
||||||
"David Aguilar <davvid@gmail.com>"
|
"David Aguilar <davvid@gmail.com>"
|
||||||
]
|
]
|
||||||
documentation = "https://docs.rs/yaml-rust2"
|
documentation = "https://docs.rs/saphyr-parser"
|
||||||
|
keywords = [ "yaml", "parser", "deserialization" ]
|
||||||
|
categories = [ "encoding", "parser-implementations" ]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "A fully YAML 1.2 compliant YAML parser"
|
description = "A fully YAML 1.2 compliant YAML parser"
|
||||||
repository = "https://github.com/Ethiraric/yaml-rust2"
|
repository = "https://github.com/saphyr-rs/saphyr-parser"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.70.0"
|
rust-version = "1.70.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = [ "encoding" ]
|
|
||||||
debug_prints = []
|
debug_prints = []
|
||||||
encoding = [ "dep:encoding_rs" ]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
arraydeque = "0.5.1"
|
arraydeque = "0.5.1"
|
||||||
encoding_rs = { version = "0.8.33", optional = true }
|
|
||||||
hashlink = "0.8"
|
hashlink = "0.8"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
libtest-mimic = "0.3.0"
|
libtest-mimic = "0.3.0"
|
||||||
quickcheck = "1.0"
|
quickcheck = "1.0"
|
||||||
|
yaml-rust2 = "0.8.0"
|
||||||
|
|
||||||
[profile.release-lto]
|
[profile.release-lto]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
|
@ -39,11 +39,3 @@ harness = false
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "dump_events"
|
name = "dump_events"
|
||||||
path = "tools/dump_events.rs"
|
path = "tools/dump_events.rs"
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "time_parse"
|
|
||||||
path = "tools/time_parse.rs"
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "run_bench"
|
|
||||||
path = "tools/run_bench.rs"
|
|
||||||
|
|
105
parser/README.md
105
parser/README.md
|
@ -1,81 +1,32 @@
|
||||||
# yaml-rust2
|
# saphyr-parser
|
||||||
|
|
||||||
[yaml-rust2](https://github.com/Ethiraric/yaml-rust2) is a fully compliant YAML 1.2
|
[saphyr-parser](https://github.com/saphyr-rs/saphyr-parser) is a fully compliant YAML 1.2
|
||||||
implementation written in pure Rust.
|
parser implementation written in pure Rust.
|
||||||
|
|
||||||
|
**If you want to load to a YAML Rust structure or manipulate YAML objects, use
|
||||||
|
`saphyr` instead of `saphyr-parser`. This crate contains only the parser.**
|
||||||
|
|
||||||
This work is based on [`yaml-rust`](https://github.com/chyh1990/yaml-rust) with
|
This work is based on [`yaml-rust`](https://github.com/chyh1990/yaml-rust) with
|
||||||
fixes towards being compliant to the [YAML test
|
fixes towards being compliant to the [YAML test
|
||||||
suite](https://github.com/yaml/yaml-test-suite/). `yaml-rust`'s parser is
|
suite](https://github.com/yaml/yaml-test-suite/). `yaml-rust`'s parser is
|
||||||
heavily influenced by `libyaml` and `yaml-cpp`.
|
heavily influenced by `libyaml` and `yaml-cpp`.
|
||||||
|
|
||||||
`yaml-rust2` is a pure Rust YAML 1.2 implementation that benefits from the
|
`saphyr-parser` is a pure Rust YAML 1.2 implementation that benefits from the
|
||||||
memory safety and other benefits from the Rust language.
|
memory safety and other benefits from the Rust language.
|
||||||
|
|
||||||
## Quick Start
|
## Installing
|
||||||
|
Add the following to your Cargo.toml:
|
||||||
Add the following to the Cargo.toml of your project:
|
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
yaml-rust2 = "0.8"
|
saphyr-parser = "0.0.1"
|
||||||
|
```
|
||||||
|
or use `cargo add` to get the latest version automatically:
|
||||||
|
```sh
|
||||||
|
cargo add saphyr-parser
|
||||||
```
|
```
|
||||||
|
|
||||||
Use `yaml_rust2::YamlLoader` to load YAML documents and access them as `Yaml` objects:
|
## TODO how-to
|
||||||
|
|
||||||
```rust
|
|
||||||
use yaml_rust2::{YamlLoader, YamlEmitter};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let s =
|
|
||||||
"
|
|
||||||
foo:
|
|
||||||
- list1
|
|
||||||
- list2
|
|
||||||
bar:
|
|
||||||
- 1
|
|
||||||
- 2.0
|
|
||||||
";
|
|
||||||
let docs = YamlLoader::load_from_str(s).unwrap();
|
|
||||||
|
|
||||||
// Multi document support, doc is a yaml::Yaml
|
|
||||||
let doc = &docs[0];
|
|
||||||
|
|
||||||
// Debug support
|
|
||||||
println!("{:?}", doc);
|
|
||||||
|
|
||||||
// Index access for map & array
|
|
||||||
assert_eq!(doc["foo"][0].as_str().unwrap(), "list1");
|
|
||||||
assert_eq!(doc["bar"][1].as_f64().unwrap(), 2.0);
|
|
||||||
|
|
||||||
// Array/map-like accesses are checked and won't panic.
|
|
||||||
// They will return `BadValue` if the access is invalid.
|
|
||||||
assert!(doc["INVALID_KEY"][100].is_badvalue());
|
|
||||||
|
|
||||||
// Dump the YAML object
|
|
||||||
let mut out_str = String::new();
|
|
||||||
{
|
|
||||||
let mut emitter = YamlEmitter::new(&mut out_str);
|
|
||||||
emitter.dump(doc).unwrap(); // dump the YAML object to a String
|
|
||||||
}
|
|
||||||
println!("{}", out_str);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that `yaml_rust2::Yaml` implements `Index<&'a str>` and `Index<usize>`:
|
|
||||||
|
|
||||||
* `Index<usize>` assumes the container is an array
|
|
||||||
* `Index<&'a str>` assumes the container is a string to value map
|
|
||||||
* otherwise, `Yaml::BadValue` is returned
|
|
||||||
|
|
||||||
If your document does not conform to this convention (e.g. map with complex
|
|
||||||
type key), you can use the `Yaml::as_XXX` family API of functions to access
|
|
||||||
your objects.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
* Pure Rust
|
|
||||||
* `Vec`/`HashMap` access API
|
|
||||||
* Low-level YAML events emission
|
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
|
@ -89,21 +40,6 @@ This implementation is fully compatible with the YAML 1.2 specification. In
|
||||||
order to help with compliance, `yaml-rust2` tests against (and passes) the [YAML
|
order to help with compliance, `yaml-rust2` tests against (and passes) the [YAML
|
||||||
test suite](https://github.com/yaml/yaml-test-suite/).
|
test suite](https://github.com/yaml/yaml-test-suite/).
|
||||||
|
|
||||||
## Upgrading from yaml-rust
|
|
||||||
|
|
||||||
You can use `yaml-rust2` as a drop-in replacement for the original `yaml-rust` crate.
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[dependencies]
|
|
||||||
yaml-rust = { version = "#.#", package = "yaml-rust2" }
|
|
||||||
```
|
|
||||||
|
|
||||||
This `Cargo.toml` declaration allows you to refer to this crate as `yaml_rust` in your code.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use yaml_rust::{YamlLoader, YamlEmitter};
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
Licensed under either of
|
||||||
|
@ -122,10 +58,9 @@ You can find licences in the [`.licenses`](.licenses) subfolder.
|
||||||
|
|
||||||
## Contribution
|
## Contribution
|
||||||
|
|
||||||
[Fork this repository](https://github.com/Ethiraric/yaml-rust2/fork) and
|
[Fork this repository](https://github.com/saphyr-rs/saphyr-parser/fork) and
|
||||||
[Create a Pull Request on Github](https://github.com/Ethiraric/yaml-rust2/compare/master...Ethiraric:yaml-rust2:master).
|
[Create a Pull Request on Github](https://github.com/saphyr-rs/saphyr-parser/compare/master...saphyr-rs:saphyr-parser:master).
|
||||||
You may need to click on "compare across forks" and select your fork's branch.
|
You may need to click on "compare across forks" and select your fork's branch.
|
||||||
Make sure that `Ethiraric` is selected as the base repository, not `chyh1990`.
|
|
||||||
|
|
||||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall
|
for inclusion in the work by you, as defined in the Apache-2.0 license, shall
|
||||||
|
@ -133,10 +68,10 @@ be dual licensed as above, without any additional terms or conditions.
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
* [yaml-rust2 source code repository](https://github.com/Ethiraric/yaml-rust2)
|
* [saphyr-parser source code repository](https://github.com/saphyr-rs/saphyr-parser)
|
||||||
|
|
||||||
* [yaml-rust2 releases on crates.io](https://crates.io/crates/yaml-rust2)
|
* [saphyr-parser releases on crates.io](https://crates.io/crates/saphyr-parser)
|
||||||
|
|
||||||
* [yaml-rust2 documentation on docs.rs](https://docs.rs/yaml-rust2/latest/yaml_rust2/)
|
* [saphyr-parser documentation on docs.rs](https://docs.rs/saphyr-parser/latest/saphyr-parser/)
|
||||||
|
|
||||||
* [yaml-test-suite](https://github.com/yaml/yaml-test-suite)
|
* [yaml-test-suite](https://github.com/yaml/yaml-test-suite)
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
clone_depth: 1
|
|
||||||
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
|
|
||||||
environment:
|
|
||||||
LLVM_VERSION: 9.0.1
|
|
||||||
PLATFORM: x64
|
|
||||||
matrix:
|
|
||||||
- channel: stable
|
|
||||||
target: i686-pc-windows-msvc
|
|
||||||
type: msvc
|
|
||||||
- channel: stable
|
|
||||||
target: x86_64-pc-windows-msvc
|
|
||||||
type: msvc
|
|
||||||
- channel: stable
|
|
||||||
target: i686-pc-windows-gnu
|
|
||||||
type: gnu
|
|
||||||
- channel: stable
|
|
||||||
target: x86_64-pc-windows-gnu
|
|
||||||
type: gnu
|
|
||||||
- channel: nightly
|
|
||||||
target: i686-pc-windows-msvc
|
|
||||||
type: msvc
|
|
||||||
- channel: nightly
|
|
||||||
target: x86_64-pc-windows-msvc
|
|
||||||
type: msvc
|
|
||||||
- channel: nightly
|
|
||||||
target: i686-pc-windows-gnu
|
|
||||||
type: gnu
|
|
||||||
- channel: nightly
|
|
||||||
target: x86_64-pc-windows-gnu
|
|
||||||
type: gnu
|
|
||||||
|
|
||||||
install:
|
|
||||||
- if %PLATFORM% == x86 (set RUST_PLATFORM=i686&set MINGW_BITS=32) else (set RUST_PLATFORM=x86_64&set MINGW_BITS=64)
|
|
||||||
- ps: >-
|
|
||||||
If ($env:target -eq 'x86_64-pc-windows-gnu') {
|
|
||||||
$env:PATH += ';C:\msys64\mingw64\bin'
|
|
||||||
} ElseIf ($env:target -eq 'i686-pc-windows-gnu') {
|
|
||||||
$env:PATH += ';C:\msys64\mingw32\bin'
|
|
||||||
}
|
|
||||||
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
|
||||||
- rustup-init -yv --default-toolchain %channel% --default-host %target%
|
|
||||||
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
|
||||||
- rustc -vV
|
|
||||||
- cargo -vV
|
|
||||||
# Install LLVM for GNU
|
|
||||||
- if %type%==gnu set PATH=C:\msys64\mingw%MINGW_BITS%\bin;C:\msys64\usr\bin;%PATH%
|
|
||||||
- if %type%==gnu set "MINGW_URL=http://repo.msys2.org/mingw/%RUST_PLATFORM%/mingw-w64-%RUST_PLATFORM%"
|
|
||||||
- if %type%==gnu set "URL_VER=%LLVM_VERSION%-1-any.pkg.tar.xz"
|
|
||||||
- if %type%==gnu bash -lc "pacman -U --noconfirm $MINGW_URL-clang-$URL_VER $MINGW_URL-llvm-$URL_VER"
|
|
||||||
- if %type%==gnu bash -lc "clang --version"
|
|
||||||
# Use preinstalled LLVM for MSVC
|
|
||||||
- if %type%==msvc set PATH=%PATH%;C:\Program Files\LLVM\bin
|
|
||||||
- if %type%==msvc where clang
|
|
||||||
- if %type%==msvc clang --version
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- cargo build -vv
|
|
||||||
test_script:
|
|
||||||
- cargo test -vv
|
|
||||||
deploy: off
|
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
use std::env;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
use yaml_rust2::yaml;
|
|
||||||
|
|
||||||
fn print_indent(indent: usize) {
|
|
||||||
for _ in 0..indent {
|
|
||||||
print!(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dump_node(doc: &yaml::Yaml, indent: usize) {
|
|
||||||
match *doc {
|
|
||||||
yaml::Yaml::Array(ref v) => {
|
|
||||||
for x in v {
|
|
||||||
dump_node(x, indent + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
yaml::Yaml::Hash(ref h) => {
|
|
||||||
for (k, v) in h {
|
|
||||||
print_indent(indent);
|
|
||||||
println!("{k:?}:");
|
|
||||||
dump_node(v, indent + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
print_indent(indent);
|
|
||||||
println!("{doc:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let args: Vec<_> = env::args().collect();
|
|
||||||
let mut f = File::open(&args[1]).unwrap();
|
|
||||||
let mut s = String::new();
|
|
||||||
f.read_to_string(&mut s).unwrap();
|
|
||||||
|
|
||||||
let docs = yaml::YamlLoader::load_from_str(&s).unwrap();
|
|
||||||
for doc in &docs {
|
|
||||||
println!("---");
|
|
||||||
dump_node(doc, 0);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,5 @@
|
||||||
before_commit:
|
before_commit:
|
||||||
|
cargo fmt --check
|
||||||
cargo clippy --release --all-targets -- -D warnings
|
cargo clippy --release --all-targets -- -D warnings
|
||||||
cargo clippy --all-targets -- -D warnings
|
cargo clippy --all-targets -- -D warnings
|
||||||
cargo build --release --all-targets
|
cargo build --release --all-targets
|
||||||
|
|
|
@ -109,16 +109,3 @@ pub(crate) fn is_uri_char(c: char) -> bool {
|
||||||
pub(crate) fn is_tag_char(c: char) -> bool {
|
pub(crate) fn is_tag_char(c: char) -> bool {
|
||||||
is_uri_char(c) && !is_flow(c) && c != '!'
|
is_uri_char(c) && !is_flow(c) && c != '!'
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the string can be expressed a valid literal block scalar.
|
|
||||||
/// The YAML spec supports all of the following in block literals except `#xFEFF`:
|
|
||||||
/// ```no_compile
|
|
||||||
/// #x9 | #xA | [#x20-#x7E] /* 8 bit */
|
|
||||||
/// | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] /* 16 bit */
|
|
||||||
/// | [#x10000-#x10FFFF] /* 32 bit */
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn is_valid_literal_block_scalar(string: &str) -> bool {
|
|
||||||
string.chars().all(|character: char|
|
|
||||||
matches!(character, '\t' | '\n' | '\x20'..='\x7e' | '\u{0085}' | '\u{00a0}'..='\u{d7fff}'))
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
//!
|
//!
|
||||||
//! Debugging is governed by two conditions:
|
//! Debugging is governed by two conditions:
|
||||||
//! 1. The build mode. Debugging code is not emitted in release builds and thus not available.
|
//! 1. The build mode. Debugging code is not emitted in release builds and thus not available.
|
||||||
//! 2. The `YAMLALL_DEBUG` environment variable. If built in debug mode, the program must be fed
|
//! 2. The `SAPHYR_DEBUG` environment variable. If built in debug mode, the program must be fed
|
||||||
//! the `YAMLALL_DEBUG` variable in its environment. While debugging code is present in debug
|
//! the `SAPHYR_DEBUG` variable in its environment. While debugging code is present in debug
|
||||||
//! build, debug helpers will only trigger if that variable is set when running the program.
|
//! build, debug helpers will only trigger if that variable is set when running the program.
|
||||||
|
|
||||||
// If a debug build, use stuff in the debug submodule.
|
// If a debug build, use stuff in the debug submodule.
|
||||||
|
@ -36,6 +36,6 @@ mod debug {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub fn enabled() -> bool {
|
pub fn enabled() -> bool {
|
||||||
static ENABLED: OnceLock<bool> = OnceLock::new();
|
static ENABLED: OnceLock<bool> = OnceLock::new();
|
||||||
*ENABLED.get_or_init(|| std::env::var("YAMLRUST2_DEBUG").is_ok())
|
*ENABLED.get_or_init(|| std::env::var("SAPHYR_DEBUG").is_ok())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,422 +0,0 @@
|
||||||
//! YAML serialization helpers.
|
|
||||||
|
|
||||||
use crate::char_traits;
|
|
||||||
use crate::yaml::{Hash, Yaml};
|
|
||||||
use std::convert::From;
|
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt::{self, Display};
|
|
||||||
|
|
||||||
/// An error when emitting YAML.
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub enum EmitError {
|
|
||||||
/// A formatting error.
|
|
||||||
FmtError(fmt::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for EmitError {
|
|
||||||
fn cause(&self) -> Option<&dyn Error> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for EmitError {
|
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
EmitError::FmtError(ref err) => Display::fmt(err, formatter),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<fmt::Error> for EmitError {
|
|
||||||
fn from(f: fmt::Error) -> Self {
|
|
||||||
EmitError::FmtError(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The YAML serializer.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use yaml_rust2::{YamlLoader, YamlEmitter};
|
|
||||||
/// let input_string = "a: b\nc: d";
|
|
||||||
/// let yaml = YamlLoader::load_from_str(input_string).unwrap();
|
|
||||||
///
|
|
||||||
/// let mut output = String::new();
|
|
||||||
/// YamlEmitter::new(&mut output).dump(&yaml[0]).unwrap();
|
|
||||||
///
|
|
||||||
/// assert_eq!(output, r#"---
|
|
||||||
/// a: b
|
|
||||||
/// c: d"#);
|
|
||||||
/// ```
|
|
||||||
#[allow(clippy::module_name_repetitions)]
|
|
||||||
pub struct YamlEmitter<'a> {
|
|
||||||
writer: &'a mut dyn fmt::Write,
|
|
||||||
best_indent: usize,
|
|
||||||
compact: bool,
|
|
||||||
level: isize,
|
|
||||||
multiline_strings: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A convenience alias for emitter functions that may fail without returning a value.
|
|
||||||
pub type EmitResult = Result<(), EmitError>;
|
|
||||||
|
|
||||||
// from serialize::json
|
|
||||||
fn escape_str(wr: &mut dyn fmt::Write, v: &str) -> Result<(), fmt::Error> {
|
|
||||||
wr.write_str("\"")?;
|
|
||||||
|
|
||||||
let mut start = 0;
|
|
||||||
|
|
||||||
for (i, byte) in v.bytes().enumerate() {
|
|
||||||
let escaped = match byte {
|
|
||||||
b'"' => "\\\"",
|
|
||||||
b'\\' => "\\\\",
|
|
||||||
b'\x00' => "\\u0000",
|
|
||||||
b'\x01' => "\\u0001",
|
|
||||||
b'\x02' => "\\u0002",
|
|
||||||
b'\x03' => "\\u0003",
|
|
||||||
b'\x04' => "\\u0004",
|
|
||||||
b'\x05' => "\\u0005",
|
|
||||||
b'\x06' => "\\u0006",
|
|
||||||
b'\x07' => "\\u0007",
|
|
||||||
b'\x08' => "\\b",
|
|
||||||
b'\t' => "\\t",
|
|
||||||
b'\n' => "\\n",
|
|
||||||
b'\x0b' => "\\u000b",
|
|
||||||
b'\x0c' => "\\f",
|
|
||||||
b'\r' => "\\r",
|
|
||||||
b'\x0e' => "\\u000e",
|
|
||||||
b'\x0f' => "\\u000f",
|
|
||||||
b'\x10' => "\\u0010",
|
|
||||||
b'\x11' => "\\u0011",
|
|
||||||
b'\x12' => "\\u0012",
|
|
||||||
b'\x13' => "\\u0013",
|
|
||||||
b'\x14' => "\\u0014",
|
|
||||||
b'\x15' => "\\u0015",
|
|
||||||
b'\x16' => "\\u0016",
|
|
||||||
b'\x17' => "\\u0017",
|
|
||||||
b'\x18' => "\\u0018",
|
|
||||||
b'\x19' => "\\u0019",
|
|
||||||
b'\x1a' => "\\u001a",
|
|
||||||
b'\x1b' => "\\u001b",
|
|
||||||
b'\x1c' => "\\u001c",
|
|
||||||
b'\x1d' => "\\u001d",
|
|
||||||
b'\x1e' => "\\u001e",
|
|
||||||
b'\x1f' => "\\u001f",
|
|
||||||
b'\x7f' => "\\u007f",
|
|
||||||
_ => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
if start < i {
|
|
||||||
wr.write_str(&v[start..i])?;
|
|
||||||
}
|
|
||||||
|
|
||||||
wr.write_str(escaped)?;
|
|
||||||
|
|
||||||
start = i + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if start != v.len() {
|
|
||||||
wr.write_str(&v[start..])?;
|
|
||||||
}
|
|
||||||
|
|
||||||
wr.write_str("\"")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> YamlEmitter<'a> {
|
|
||||||
/// Create a new emitter serializing into `writer`.
|
|
||||||
pub fn new(writer: &'a mut dyn fmt::Write) -> YamlEmitter {
|
|
||||||
YamlEmitter {
|
|
||||||
writer,
|
|
||||||
best_indent: 2,
|
|
||||||
compact: true,
|
|
||||||
level: -1,
|
|
||||||
multiline_strings: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set 'compact inline notation' on or off, as described for block
|
|
||||||
/// [sequences](http://www.yaml.org/spec/1.2/spec.html#id2797382)
|
|
||||||
/// and
|
|
||||||
/// [mappings](http://www.yaml.org/spec/1.2/spec.html#id2798057).
|
|
||||||
///
|
|
||||||
/// In this form, blocks cannot have any properties (such as anchors
|
|
||||||
/// or tags), which should be OK, because this emitter doesn't
|
|
||||||
/// (currently) emit those anyways.
|
|
||||||
pub fn compact(&mut self, compact: bool) {
|
|
||||||
self.compact = compact;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determine if this emitter is using 'compact inline notation'.
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_compact(&self) -> bool {
|
|
||||||
self.compact
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Render strings containing multiple lines in [literal style].
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use yaml_rust2::{Yaml, YamlEmitter, YamlLoader};
|
|
||||||
///
|
|
||||||
/// let input = r#"{foo: "bar!\nbar!", baz: 42}"#;
|
|
||||||
/// let parsed = YamlLoader::load_from_str(input).unwrap();
|
|
||||||
/// eprintln!("{:?}", parsed);
|
|
||||||
///
|
|
||||||
/// let mut output = String::new();
|
|
||||||
/// let mut emitter = YamlEmitter::new(&mut output);
|
|
||||||
/// emitter.multiline_strings(true);
|
|
||||||
/// emitter.dump(&parsed[0]).unwrap();
|
|
||||||
/// assert_eq!(output.as_str(), "\
|
|
||||||
/// ---
|
|
||||||
/// foo: |
|
|
||||||
/// bar!
|
|
||||||
/// bar!
|
|
||||||
/// baz: 42");
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// [literal style]: https://yaml.org/spec/1.2/spec.html#id2795688
|
|
||||||
pub fn multiline_strings(&mut self, multiline_strings: bool) {
|
|
||||||
self.multiline_strings = multiline_strings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determine if this emitter will emit multiline strings when appropriate.
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_multiline_strings(&self) -> bool {
|
|
||||||
self.multiline_strings
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dump Yaml to an output stream.
|
|
||||||
/// # Errors
|
|
||||||
/// Returns `EmitError` when an error occurs.
|
|
||||||
pub fn dump(&mut self, doc: &Yaml) -> EmitResult {
|
|
||||||
// write DocumentStart
|
|
||||||
writeln!(self.writer, "---")?;
|
|
||||||
self.level = -1;
|
|
||||||
self.emit_node(doc)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_indent(&mut self) -> EmitResult {
|
|
||||||
if self.level <= 0 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
for _ in 0..self.level {
|
|
||||||
for _ in 0..self.best_indent {
|
|
||||||
write!(self.writer, " ")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn emit_node(&mut self, node: &Yaml) -> EmitResult {
|
|
||||||
match *node {
|
|
||||||
Yaml::Array(ref v) => self.emit_array(v),
|
|
||||||
Yaml::Hash(ref h) => self.emit_hash(h),
|
|
||||||
Yaml::String(ref v) => {
|
|
||||||
if self.multiline_strings
|
|
||||||
&& v.contains('\n')
|
|
||||||
&& char_traits::is_valid_literal_block_scalar(v)
|
|
||||||
{
|
|
||||||
write!(self.writer, "|")?;
|
|
||||||
self.level += 1;
|
|
||||||
for line in v.lines() {
|
|
||||||
writeln!(self.writer)?;
|
|
||||||
self.write_indent()?;
|
|
||||||
// It's literal text, so don't escape special chars.
|
|
||||||
write!(self.writer, "{line}")?;
|
|
||||||
}
|
|
||||||
self.level -= 1;
|
|
||||||
} else if need_quotes(v) {
|
|
||||||
escape_str(self.writer, v)?;
|
|
||||||
} else {
|
|
||||||
write!(self.writer, "{v}")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Yaml::Boolean(v) => {
|
|
||||||
if v {
|
|
||||||
self.writer.write_str("true")?;
|
|
||||||
} else {
|
|
||||||
self.writer.write_str("false")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Yaml::Integer(v) => {
|
|
||||||
write!(self.writer, "{v}")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Yaml::Real(ref v) => {
|
|
||||||
write!(self.writer, "{v}")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Yaml::Null | Yaml::BadValue => {
|
|
||||||
write!(self.writer, "~")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
// XXX(chenyh) Alias
|
|
||||||
Yaml::Alias(_) => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn emit_array(&mut self, v: &[Yaml]) -> EmitResult {
|
|
||||||
if v.is_empty() {
|
|
||||||
write!(self.writer, "[]")?;
|
|
||||||
} else {
|
|
||||||
self.level += 1;
|
|
||||||
for (cnt, x) in v.iter().enumerate() {
|
|
||||||
if cnt > 0 {
|
|
||||||
writeln!(self.writer)?;
|
|
||||||
self.write_indent()?;
|
|
||||||
}
|
|
||||||
write!(self.writer, "-")?;
|
|
||||||
self.emit_val(true, x)?;
|
|
||||||
}
|
|
||||||
self.level -= 1;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn emit_hash(&mut self, h: &Hash) -> EmitResult {
|
|
||||||
if h.is_empty() {
|
|
||||||
self.writer.write_str("{}")?;
|
|
||||||
} else {
|
|
||||||
self.level += 1;
|
|
||||||
for (cnt, (k, v)) in h.iter().enumerate() {
|
|
||||||
let complex_key = matches!(*k, Yaml::Hash(_) | Yaml::Array(_));
|
|
||||||
if cnt > 0 {
|
|
||||||
writeln!(self.writer)?;
|
|
||||||
self.write_indent()?;
|
|
||||||
}
|
|
||||||
if complex_key {
|
|
||||||
write!(self.writer, "?")?;
|
|
||||||
self.emit_val(true, k)?;
|
|
||||||
writeln!(self.writer)?;
|
|
||||||
self.write_indent()?;
|
|
||||||
write!(self.writer, ":")?;
|
|
||||||
self.emit_val(true, v)?;
|
|
||||||
} else {
|
|
||||||
self.emit_node(k)?;
|
|
||||||
write!(self.writer, ":")?;
|
|
||||||
self.emit_val(false, v)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.level -= 1;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Emit a yaml as a hash or array value: i.e., which should appear
|
|
||||||
/// following a ":" or "-", either after a space, or on a new line.
|
|
||||||
/// If `inline` is true, then the preceding characters are distinct
|
|
||||||
/// and short enough to respect the compact flag.
|
|
||||||
fn emit_val(&mut self, inline: bool, val: &Yaml) -> EmitResult {
|
|
||||||
match *val {
|
|
||||||
Yaml::Array(ref v) => {
|
|
||||||
if (inline && self.compact) || v.is_empty() {
|
|
||||||
write!(self.writer, " ")?;
|
|
||||||
} else {
|
|
||||||
writeln!(self.writer)?;
|
|
||||||
self.level += 1;
|
|
||||||
self.write_indent()?;
|
|
||||||
self.level -= 1;
|
|
||||||
}
|
|
||||||
self.emit_array(v)
|
|
||||||
}
|
|
||||||
Yaml::Hash(ref h) => {
|
|
||||||
if (inline && self.compact) || h.is_empty() {
|
|
||||||
write!(self.writer, " ")?;
|
|
||||||
} else {
|
|
||||||
writeln!(self.writer)?;
|
|
||||||
self.level += 1;
|
|
||||||
self.write_indent()?;
|
|
||||||
self.level -= 1;
|
|
||||||
}
|
|
||||||
self.emit_hash(h)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
write!(self.writer, " ")?;
|
|
||||||
self.emit_node(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if the string requires quoting.
|
|
||||||
/// Strings starting with any of the following characters must be quoted.
|
|
||||||
/// :, &, *, ?, |, -, <, >, =, !, %, @
|
|
||||||
/// Strings containing any of the following characters must be quoted.
|
|
||||||
/// {, }, \[, t \], ,, #, `
|
|
||||||
///
|
|
||||||
/// If the string contains any of the following control characters, it must be escaped with double quotes:
|
|
||||||
/// \0, \x01, \x02, \x03, \x04, \x05, \x06, \a, \b, \t, \n, \v, \f, \r, \x0e, \x0f, \x10, \x11, \x12, \x13, \x14, \x15, \x16, \x17, \x18, \x19, \x1a, \e, \x1c, \x1d, \x1e, \x1f, \N, \_, \L, \P
|
|
||||||
///
|
|
||||||
/// Finally, there are other cases when the strings must be quoted, no matter if you're using single or double quotes:
|
|
||||||
/// * When the string is true or false (otherwise, it would be treated as a boolean value);
|
|
||||||
/// * When the string is null or ~ (otherwise, it would be considered as a null value);
|
|
||||||
/// * When the string looks like a number, such as integers (e.g. 2, 14, etc.), floats (e.g. 2.6, 14.9) and exponential numbers (e.g. 12e7, etc.) (otherwise, it would be treated as a numeric value);
|
|
||||||
/// * When the string looks like a date (e.g. 2014-12-31) (otherwise it would be automatically converted into a Unix timestamp).
|
|
||||||
#[allow(clippy::doc_markdown)]
|
|
||||||
fn need_quotes(string: &str) -> bool {
|
|
||||||
fn need_quotes_spaces(string: &str) -> bool {
|
|
||||||
string.starts_with(' ') || string.ends_with(' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
string.is_empty()
|
|
||||||
|| need_quotes_spaces(string)
|
|
||||||
|| string.starts_with(|character: char| {
|
|
||||||
matches!(
|
|
||||||
character,
|
|
||||||
'&' | '*' | '?' | '|' | '-' | '<' | '>' | '=' | '!' | '%' | '@'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|| string.contains(|character: char| {
|
|
||||||
matches!(character, ':'
|
|
||||||
| '{'
|
|
||||||
| '}'
|
|
||||||
| '['
|
|
||||||
| ']'
|
|
||||||
| ','
|
|
||||||
| '#'
|
|
||||||
| '`'
|
|
||||||
| '\"'
|
|
||||||
| '\''
|
|
||||||
| '\\'
|
|
||||||
| '\0'..='\x06'
|
|
||||||
| '\t'
|
|
||||||
| '\n'
|
|
||||||
| '\r'
|
|
||||||
| '\x0e'..='\x1a'
|
|
||||||
| '\x1c'..='\x1f')
|
|
||||||
})
|
|
||||||
|| [
|
|
||||||
// http://yaml.org/type/bool.html
|
|
||||||
// Note: 'y', 'Y', 'n', 'N', is not quoted deliberately, as in libyaml. PyYAML also parse
|
|
||||||
// them as string, not booleans, although it is violating the YAML 1.1 specification.
|
|
||||||
// See https://github.com/dtolnay/serde-yaml/pull/83#discussion_r152628088.
|
|
||||||
"yes", "Yes", "YES", "no", "No", "NO", "True", "TRUE", "true", "False", "FALSE",
|
|
||||||
"false", "on", "On", "ON", "off", "Off", "OFF",
|
|
||||||
// http://yaml.org/type/null.html
|
|
||||||
"null", "Null", "NULL", "~",
|
|
||||||
]
|
|
||||||
.contains(&string)
|
|
||||||
|| string.starts_with('.')
|
|
||||||
|| string.starts_with("0x")
|
|
||||||
|| string.parse::<i64>().is_ok()
|
|
||||||
|| string.parse::<f64>().is_ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::YamlEmitter;
|
|
||||||
use crate::YamlLoader;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_multiline_string() {
|
|
||||||
let input = r#"{foo: "bar!\nbar!", baz: 42}"#;
|
|
||||||
let parsed = YamlLoader::load_from_str(input).unwrap();
|
|
||||||
let mut output = String::new();
|
|
||||||
let mut emitter = YamlEmitter::new(&mut output);
|
|
||||||
emitter.multiline_strings(true);
|
|
||||||
emitter.dump(&parsed[0]).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,40 +2,27 @@
|
||||||
// Copyright 2023, Ethiraric.
|
// Copyright 2023, Ethiraric.
|
||||||
// See the LICENSE file at the top-level directory of this distribution.
|
// See the LICENSE file at the top-level directory of this distribution.
|
||||||
|
|
||||||
//! YAML 1.2 implementation in pure Rust.
|
//! YAML 1.2 parser implementation in pure Rust.
|
||||||
|
//!
|
||||||
|
//! **If you want to load to a YAML Rust structure or manipulate YAML objects, use `saphyr` instead
|
||||||
|
//! of `saphyr-parser`. This crate contains only the parser.**
|
||||||
|
//!
|
||||||
|
//! This is YAML 1.2 parser implementation and low-level parsing API for YAML. It allows users to
|
||||||
|
//! fetch a stream of YAML events from a stream of characters/bytes.
|
||||||
//!
|
//!
|
||||||
//! # Usage
|
//! # Usage
|
||||||
//!
|
//!
|
||||||
//! This crate is [on github](https://github.com/Ethiraric/yaml-rust2) and can be used by adding
|
//! This crate is [on github](https://github.com/saphyr-rs/saphyr-parser) and can be used by adding
|
||||||
//! `yaml-rust2` to the dependencies in your project's `Cargo.toml`.
|
//! `saphyr-parser` to the dependencies in your project's `Cargo.toml`.
|
||||||
//!
|
//!
|
||||||
//! ```toml
|
//! ```toml
|
||||||
//! [dependencies]
|
//! [dependencies]
|
||||||
//! yaml-rust2 = "0.8.0"
|
//! saphyr-parser = "0.0.1"
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! # Examples
|
|
||||||
//! Parse a string into `Vec<Yaml>` and then serialize it as a YAML string.
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! use yaml_rust2::{YamlLoader, YamlEmitter};
|
|
||||||
//!
|
|
||||||
//! let docs = YamlLoader::load_from_str("[1, 2, 3]").unwrap();
|
|
||||||
//! let doc = &docs[0]; // select the first YAML document
|
|
||||||
//! assert_eq!(doc[0].as_i64().unwrap(), 1); // access elements by index
|
|
||||||
//!
|
|
||||||
//! let mut out_str = String::new();
|
|
||||||
//! let mut emitter = YamlEmitter::new(&mut out_str);
|
|
||||||
//! emitter.dump(doc).unwrap(); // dump the YAML object to a String
|
|
||||||
//!
|
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! # Features
|
//! # Features
|
||||||
//! **Note:** With all features disabled, this crate's MSRV is `1.65.0`.
|
//! **Note:** With all features disabled, this crate's MSRV is `1.65.0`.
|
||||||
//!
|
//!
|
||||||
//! #### `encoding` (_enabled by default_)
|
|
||||||
//! Enables encoding-aware decoding of Yaml documents.
|
|
||||||
//!
|
|
||||||
//! The MSRV for this feature is `1.70.0`.
|
//! The MSRV for this feature is `1.70.0`.
|
||||||
//!
|
//!
|
||||||
//! #### `debug_prints`
|
//! #### `debug_prints`
|
||||||
|
@ -47,18 +34,11 @@
|
||||||
|
|
||||||
#![warn(missing_docs, clippy::pedantic)]
|
#![warn(missing_docs, clippy::pedantic)]
|
||||||
|
|
||||||
extern crate hashlink;
|
|
||||||
|
|
||||||
pub(crate) mod char_traits;
|
pub(crate) mod char_traits;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub(crate) mod debug;
|
pub(crate) mod debug;
|
||||||
pub mod emitter;
|
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod scanner;
|
pub mod scanner;
|
||||||
pub mod yaml;
|
|
||||||
|
|
||||||
// reexport key APIs
|
pub use crate::parser::{Event, EventReceiver, MarkedEventReceiver, Parser, Tag};
|
||||||
pub use crate::emitter::{EmitError, YamlEmitter};
|
pub use crate::scanner::{Marker, ScanError, TScalarStyle};
|
||||||
pub use crate::parser::Event;
|
|
||||||
pub use crate::scanner::ScanError;
|
|
||||||
pub use crate::yaml::{Yaml, YamlLoader};
|
|
||||||
|
|
|
@ -1,23 +1,20 @@
|
||||||
//! Home to the YAML Parser.
|
//! Home to the YAML Parser.
|
||||||
//!
|
//!
|
||||||
//! The parser takes input from the [`crate::scanner::Scanner`], performs final checks for YAML
|
//! The parser takes input from the [`crate::scanner::Scanner`], performs final checks for YAML
|
||||||
//! compliance, and emits a stream of tokens that can be used by the [`crate::YamlLoader`] to
|
//! compliance, and emits a stream of YAML events. This stream can for instance be used to create
|
||||||
//! construct the [`crate::Yaml`] object.
|
//! YAML objects.
|
||||||
|
|
||||||
use crate::scanner::{Marker, ScanError, Scanner, TScalarStyle, Token, TokenType};
|
use crate::scanner::{Marker, ScanError, Scanner, TScalarStyle, Token, TokenType};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug, Eq)]
|
#[derive(Clone, Copy, PartialEq, Debug, Eq)]
|
||||||
enum State {
|
enum State {
|
||||||
/// We await the start of the stream.
|
|
||||||
StreamStart,
|
StreamStart,
|
||||||
ImplicitDocumentStart,
|
ImplicitDocumentStart,
|
||||||
DocumentStart,
|
DocumentStart,
|
||||||
DocumentContent,
|
DocumentContent,
|
||||||
DocumentEnd,
|
DocumentEnd,
|
||||||
BlockNode,
|
BlockNode,
|
||||||
// BlockNodeOrIndentlessSequence,
|
|
||||||
// FlowNode,
|
|
||||||
BlockSequenceFirstEntry,
|
BlockSequenceFirstEntry,
|
||||||
BlockSequenceEntry,
|
BlockSequenceEntry,
|
||||||
IndentlessSequenceEntry,
|
IndentlessSequenceEntry,
|
||||||
|
@ -165,7 +162,7 @@ pub struct Parser<T> {
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// # use yaml_rust2::parser::{Event, EventReceiver, Parser};
|
/// # use saphyr_parser::{Event, EventReceiver, Parser};
|
||||||
/// #
|
/// #
|
||||||
/// /// Sink of events. Collects them into an array.
|
/// /// Sink of events. Collects them into an array.
|
||||||
/// struct EventSink {
|
/// struct EventSink {
|
||||||
|
@ -1088,10 +1085,17 @@ impl<T: Iterator<Item = char>> Parser<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Iterator<Item = char>> Iterator for Parser<T> {
|
||||||
|
type Item = Result<(Event, Marker), ScanError>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
Some(self.next_token())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::{Event, Parser};
|
use super::{Event, Parser};
|
||||||
use crate::YamlLoader;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_peek_eq_parse() {
|
fn test_peek_eq_parse() {
|
||||||
|
@ -1126,18 +1130,29 @@ foo: "bar"
|
||||||
--- !t!2 &2
|
--- !t!2 &2
|
||||||
baz: "qux"
|
baz: "qux"
|
||||||
"#;
|
"#;
|
||||||
let mut parser = Parser::new_from_str(text).keep_tags(true);
|
for x in Parser::new_from_str(text).keep_tags(true) {
|
||||||
let result = YamlLoader::load_from_parser(&mut parser);
|
let x = x.unwrap();
|
||||||
assert!(result.is_ok());
|
match x.0 {
|
||||||
let docs = result.unwrap();
|
Event::StreamEnd => break,
|
||||||
assert_eq!(docs.len(), 2);
|
Event::MappingStart(_, tag) => {
|
||||||
let yaml = &docs[0];
|
let tag = tag.unwrap();
|
||||||
assert_eq!(yaml["foo"].as_str(), Some("bar"));
|
assert_eq!(tag.handle, "tag:test,2024:");
|
||||||
let yaml = &docs[1];
|
}
|
||||||
assert_eq!(yaml["baz"].as_str(), Some("qux"));
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut parser = Parser::new_from_str(text).keep_tags(false);
|
for x in Parser::new_from_str(text).keep_tags(false) {
|
||||||
let result = YamlLoader::load_from_parser(&mut parser);
|
match x {
|
||||||
assert!(result.is_err());
|
Err(..) => {
|
||||||
|
// Test successful
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Ok((Event::StreamEnd, _)) => {
|
||||||
|
panic!("Test failed, did not encounter error")
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,833 +0,0 @@
|
||||||
//! YAML objects manipulation utilities.
|
|
||||||
|
|
||||||
#![allow(clippy::module_name_repetitions)]
|
|
||||||
|
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::ops::ControlFlow;
|
|
||||||
use std::{collections::BTreeMap, convert::TryFrom, mem, ops::Index, ops::IndexMut};
|
|
||||||
|
|
||||||
#[cfg(feature = "encoding")]
|
|
||||||
use encoding_rs::{Decoder, DecoderResult, Encoding};
|
|
||||||
use hashlink::LinkedHashMap;
|
|
||||||
|
|
||||||
use crate::parser::{Event, MarkedEventReceiver, Parser, Tag};
|
|
||||||
use crate::scanner::{Marker, ScanError, TScalarStyle};
|
|
||||||
|
|
||||||
/// A YAML node is stored as this `Yaml` enumeration, which provides an easy way to
|
|
||||||
/// access your YAML document.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use yaml_rust2::Yaml;
|
|
||||||
/// let foo = Yaml::from_str("-123"); // convert the string to the appropriate YAML type
|
|
||||||
/// assert_eq!(foo.as_i64().unwrap(), -123);
|
|
||||||
///
|
|
||||||
/// // iterate over an Array
|
|
||||||
/// let vec = Yaml::Array(vec![Yaml::Integer(1), Yaml::Integer(2)]);
|
|
||||||
/// for v in vec.as_vec().unwrap() {
|
|
||||||
/// assert!(v.as_i64().is_some());
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
#[derive(Clone, PartialEq, PartialOrd, Debug, Eq, Ord, Hash)]
|
|
||||||
pub enum Yaml {
|
|
||||||
/// Float types are stored as String and parsed on demand.
|
|
||||||
/// Note that `f64` does NOT implement Eq trait and can NOT be stored in `BTreeMap`.
|
|
||||||
Real(String),
|
|
||||||
/// YAML int is stored as i64.
|
|
||||||
Integer(i64),
|
|
||||||
/// YAML scalar.
|
|
||||||
String(String),
|
|
||||||
/// YAML bool, e.g. `true` or `false`.
|
|
||||||
Boolean(bool),
|
|
||||||
/// YAML array, can be accessed as a `Vec`.
|
|
||||||
Array(Array),
|
|
||||||
/// YAML hash, can be accessed as a `LinkedHashMap`.
|
|
||||||
///
|
|
||||||
/// Insertion order will match the order of insertion into the map.
|
|
||||||
Hash(Hash),
|
|
||||||
/// Alias, not fully supported yet.
|
|
||||||
Alias(usize),
|
|
||||||
/// YAML null, e.g. `null` or `~`.
|
|
||||||
Null,
|
|
||||||
/// Accessing a nonexistent node via the Index trait returns `BadValue`. This
|
|
||||||
/// simplifies error handling in the calling code. Invalid type conversion also
|
|
||||||
/// returns `BadValue`.
|
|
||||||
BadValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The type contained in the `Yaml::Array` variant. This corresponds to YAML sequences.
|
|
||||||
pub type Array = Vec<Yaml>;
|
|
||||||
/// The type contained in the `Yaml::Hash` variant. This corresponds to YAML mappings.
|
|
||||||
pub type Hash = LinkedHashMap<Yaml, Yaml>;
|
|
||||||
|
|
||||||
// parse f64 as Core schema
|
|
||||||
// See: https://github.com/chyh1990/yaml-rust/issues/51
|
|
||||||
fn parse_f64(v: &str) -> Option<f64> {
|
|
||||||
match v {
|
|
||||||
".inf" | ".Inf" | ".INF" | "+.inf" | "+.Inf" | "+.INF" => Some(f64::INFINITY),
|
|
||||||
"-.inf" | "-.Inf" | "-.INF" => Some(f64::NEG_INFINITY),
|
|
||||||
".nan" | "NaN" | ".NAN" => Some(f64::NAN),
|
|
||||||
_ => v.parse::<f64>().ok(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Main structure for quickly parsing YAML.
|
|
||||||
///
|
|
||||||
/// See [`YamlLoader::load_from_str`].
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct YamlLoader {
|
|
||||||
/// The different YAML documents that are loaded.
|
|
||||||
docs: Vec<Yaml>,
|
|
||||||
// states
|
|
||||||
// (current node, anchor_id) tuple
|
|
||||||
doc_stack: Vec<(Yaml, usize)>,
|
|
||||||
key_stack: Vec<Yaml>,
|
|
||||||
anchor_map: BTreeMap<usize, Yaml>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MarkedEventReceiver for YamlLoader {
|
|
||||||
fn on_event(&mut self, ev: Event, _: Marker) {
|
|
||||||
// println!("EV {:?}", ev);
|
|
||||||
match ev {
|
|
||||||
Event::DocumentStart | Event::Nothing | Event::StreamStart | Event::StreamEnd => {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
Event::DocumentEnd => {
|
|
||||||
match self.doc_stack.len() {
|
|
||||||
// empty document
|
|
||||||
0 => self.docs.push(Yaml::BadValue),
|
|
||||||
1 => self.docs.push(self.doc_stack.pop().unwrap().0),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::SequenceStart(aid, _) => {
|
|
||||||
self.doc_stack.push((Yaml::Array(Vec::new()), aid));
|
|
||||||
}
|
|
||||||
Event::SequenceEnd => {
|
|
||||||
let node = self.doc_stack.pop().unwrap();
|
|
||||||
self.insert_new_node(node);
|
|
||||||
}
|
|
||||||
Event::MappingStart(aid, _) => {
|
|
||||||
self.doc_stack.push((Yaml::Hash(Hash::new()), aid));
|
|
||||||
self.key_stack.push(Yaml::BadValue);
|
|
||||||
}
|
|
||||||
Event::MappingEnd => {
|
|
||||||
self.key_stack.pop().unwrap();
|
|
||||||
let node = self.doc_stack.pop().unwrap();
|
|
||||||
self.insert_new_node(node);
|
|
||||||
}
|
|
||||||
Event::Scalar(v, style, aid, tag) => {
|
|
||||||
let node = if style != TScalarStyle::Plain {
|
|
||||||
Yaml::String(v)
|
|
||||||
} else if let Some(Tag {
|
|
||||||
ref handle,
|
|
||||||
ref suffix,
|
|
||||||
}) = tag
|
|
||||||
{
|
|
||||||
if handle == "tag:yaml.org,2002:" {
|
|
||||||
match suffix.as_ref() {
|
|
||||||
"bool" => {
|
|
||||||
// "true" or "false"
|
|
||||||
match v.parse::<bool>() {
|
|
||||||
Err(_) => Yaml::BadValue,
|
|
||||||
Ok(v) => Yaml::Boolean(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"int" => match v.parse::<i64>() {
|
|
||||||
Err(_) => Yaml::BadValue,
|
|
||||||
Ok(v) => Yaml::Integer(v),
|
|
||||||
},
|
|
||||||
"float" => match parse_f64(&v) {
|
|
||||||
Some(_) => Yaml::Real(v),
|
|
||||||
None => Yaml::BadValue,
|
|
||||||
},
|
|
||||||
"null" => match v.as_ref() {
|
|
||||||
"~" | "null" => Yaml::Null,
|
|
||||||
_ => Yaml::BadValue,
|
|
||||||
},
|
|
||||||
_ => Yaml::String(v),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Yaml::String(v)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Datatype is not specified, or unrecognized
|
|
||||||
Yaml::from_str(&v)
|
|
||||||
};
|
|
||||||
|
|
||||||
self.insert_new_node((node, aid));
|
|
||||||
}
|
|
||||||
Event::Alias(id) => {
|
|
||||||
let n = match self.anchor_map.get(&id) {
|
|
||||||
Some(v) => v.clone(),
|
|
||||||
None => Yaml::BadValue,
|
|
||||||
};
|
|
||||||
self.insert_new_node((n, 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// println!("DOC {:?}", self.doc_stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An error that happened when loading a YAML document.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum LoadError {
|
|
||||||
/// An I/O error.
|
|
||||||
IO(std::io::Error),
|
|
||||||
/// An error within the scanner. This indicates a malformed YAML input.
|
|
||||||
Scan(ScanError),
|
|
||||||
/// A decoding error (e.g.: Invalid UTF_8).
|
|
||||||
Decode(std::borrow::Cow<'static, str>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for LoadError {
|
|
||||||
fn from(error: std::io::Error) -> Self {
|
|
||||||
LoadError::IO(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl YamlLoader {
|
|
||||||
fn insert_new_node(&mut self, node: (Yaml, usize)) {
|
|
||||||
// valid anchor id starts from 1
|
|
||||||
if node.1 > 0 {
|
|
||||||
self.anchor_map.insert(node.1, node.0.clone());
|
|
||||||
}
|
|
||||||
if self.doc_stack.is_empty() {
|
|
||||||
self.doc_stack.push(node);
|
|
||||||
} else {
|
|
||||||
let parent = self.doc_stack.last_mut().unwrap();
|
|
||||||
match *parent {
|
|
||||||
(Yaml::Array(ref mut v), _) => v.push(node.0),
|
|
||||||
(Yaml::Hash(ref mut h), _) => {
|
|
||||||
let cur_key = self.key_stack.last_mut().unwrap();
|
|
||||||
// current node is a key
|
|
||||||
if cur_key.is_badvalue() {
|
|
||||||
*cur_key = node.0;
|
|
||||||
// current node is a value
|
|
||||||
} else {
|
|
||||||
let mut newkey = Yaml::BadValue;
|
|
||||||
mem::swap(&mut newkey, cur_key);
|
|
||||||
h.insert(newkey, node.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load the given string as a set of YAML documents.
|
|
||||||
///
|
|
||||||
/// The `source` is interpreted as YAML documents and is parsed. Parsing succeeds if and only
|
|
||||||
/// if all documents are parsed successfully. An error in a latter document prevents the former
|
|
||||||
/// from being returned.
|
|
||||||
/// # Errors
|
|
||||||
/// Returns `ScanError` when loading fails.
|
|
||||||
pub fn load_from_str(source: &str) -> Result<Vec<Yaml>, ScanError> {
|
|
||||||
Self::load_from_iter(source.chars())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load the contents of the given iterator as a set of YAML documents.
|
|
||||||
///
|
|
||||||
/// The `source` is interpreted as YAML documents and is parsed. Parsing succeeds if and only
|
|
||||||
/// if all documents are parsed successfully. An error in a latter document prevents the former
|
|
||||||
/// from being returned.
|
|
||||||
/// # Errors
|
|
||||||
/// Returns `ScanError` when loading fails.
|
|
||||||
pub fn load_from_iter<I: Iterator<Item = char>>(source: I) -> Result<Vec<Yaml>, ScanError> {
|
|
||||||
let mut parser = Parser::new(source);
|
|
||||||
Self::load_from_parser(&mut parser)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load the contents from the specified Parser as a set of YAML documents.
|
|
||||||
///
|
|
||||||
/// Parsing succeeds if and only if all documents are parsed successfully.
|
|
||||||
/// An error in a latter document prevents the former from being returned.
|
|
||||||
/// # Errors
|
|
||||||
/// Returns `ScanError` when loading fails.
|
|
||||||
pub fn load_from_parser<I: Iterator<Item = char>>(
|
|
||||||
parser: &mut Parser<I>,
|
|
||||||
) -> Result<Vec<Yaml>, ScanError> {
|
|
||||||
let mut loader = YamlLoader::default();
|
|
||||||
parser.load(&mut loader, true)?;
|
|
||||||
Ok(loader.docs)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return a reference to the parsed Yaml documents.
|
|
||||||
#[must_use]
|
|
||||||
pub fn documents(&self) -> &[Yaml] {
|
|
||||||
&self.docs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The signature of the function to call when using [`YAMLDecodingTrap::Call`].
|
|
||||||
///
|
|
||||||
/// The arguments are as follows:
|
|
||||||
/// * `malformation_length`: The length of the sequence the decoder failed to decode.
|
|
||||||
/// * `bytes_read_after_malformation`: The number of lookahead bytes the decoder consumed after
|
|
||||||
/// the malformation.
|
|
||||||
/// * `input_at_malformation`: What the input buffer is at the malformation.
|
|
||||||
/// This is the buffer starting at the malformation. The first `malformation_length` bytes are
|
|
||||||
/// the problematic sequence. The following `bytes_read_after_malformation` are already stored
|
|
||||||
/// in the decoder and will not be re-fed.
|
|
||||||
/// * `output`: The output string.
|
|
||||||
///
|
|
||||||
/// The function must modify `output` as it feels is best. For instance, one could recreate the
|
|
||||||
/// behavior of [`YAMLDecodingTrap::Ignore`] with an empty function, [`YAMLDecodingTrap::Replace`]
|
|
||||||
/// by pushing a `\u{FFFD}` into `output` and [`YAMLDecodingTrap::Strict`] by returning
|
|
||||||
/// [`ControlFlow::Break`].
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// The function must return [`ControlFlow::Continue`] if decoding may continue or
|
|
||||||
/// [`ControlFlow::Break`] if decoding must be aborted. An optional error string may be supplied.
|
|
||||||
#[cfg(feature = "encoding")]
|
|
||||||
pub type YAMLDecodingTrapFn = fn(
|
|
||||||
malformation_length: u8,
|
|
||||||
bytes_read_after_malformation: u8,
|
|
||||||
input_at_malformation: &[u8],
|
|
||||||
output: &mut String,
|
|
||||||
) -> ControlFlow<Cow<'static, str>>;
|
|
||||||
|
|
||||||
/// The behavior [`YamlDecoder`] must have when an decoding error occurs.
|
|
||||||
#[cfg(feature = "encoding")]
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum YAMLDecodingTrap {
|
|
||||||
/// Ignore the offending bytes, remove them from the output.
|
|
||||||
Ignore,
|
|
||||||
/// Error out.
|
|
||||||
Strict,
|
|
||||||
/// Replace them with the Unicode REPLACEMENT CHARACTER.
|
|
||||||
Replace,
|
|
||||||
/// Call the user-supplied function upon decoding malformation.
|
|
||||||
Call(YAMLDecodingTrapFn),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `YamlDecoder` is a `YamlLoader` builder that allows you to supply your own encoding error trap.
|
|
||||||
/// For example, to read a YAML file while ignoring Unicode decoding errors you can set the
|
|
||||||
/// `encoding_trap` to `encoding::DecoderTrap::Ignore`.
|
|
||||||
/// ```rust
|
|
||||||
/// use yaml_rust2::yaml::{YamlDecoder, YAMLDecodingTrap};
|
|
||||||
///
|
|
||||||
/// let string = b"---
|
|
||||||
/// a\xa9: 1
|
|
||||||
/// b: 2.2
|
|
||||||
/// c: [1, 2]
|
|
||||||
/// ";
|
|
||||||
/// let out = YamlDecoder::read(string as &[u8])
|
|
||||||
/// .encoding_trap(YAMLDecodingTrap::Ignore)
|
|
||||||
/// .decode()
|
|
||||||
/// .unwrap();
|
|
||||||
/// ```
|
|
||||||
#[cfg(feature = "encoding")]
|
|
||||||
pub struct YamlDecoder<T: std::io::Read> {
|
|
||||||
source: T,
|
|
||||||
trap: YAMLDecodingTrap,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "encoding")]
|
|
||||||
impl<T: std::io::Read> YamlDecoder<T> {
|
|
||||||
/// Create a `YamlDecoder` decoding the given source.
|
|
||||||
pub fn read(source: T) -> YamlDecoder<T> {
|
|
||||||
YamlDecoder {
|
|
||||||
source,
|
|
||||||
trap: YAMLDecodingTrap::Strict,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the behavior of the decoder when the encoding is invalid.
|
|
||||||
pub fn encoding_trap(&mut self, trap: YAMLDecodingTrap) -> &mut Self {
|
|
||||||
self.trap = trap;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run the decode operation with the source and trap the `YamlDecoder` was built with.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// Returns `LoadError` when decoding fails.
|
|
||||||
pub fn decode(&mut self) -> Result<Vec<Yaml>, LoadError> {
|
|
||||||
let mut buffer = Vec::new();
|
|
||||||
self.source.read_to_end(&mut buffer)?;
|
|
||||||
|
|
||||||
// Check if the `encoding` library can detect encoding from the BOM, otherwise use
|
|
||||||
// `detect_utf16_endianness`.
|
|
||||||
let (encoding, _) =
|
|
||||||
Encoding::for_bom(&buffer).unwrap_or_else(|| (detect_utf16_endianness(&buffer), 2));
|
|
||||||
let mut decoder = encoding.new_decoder();
|
|
||||||
let mut output = String::new();
|
|
||||||
|
|
||||||
// Decode the input buffer.
|
|
||||||
decode_loop(&buffer, &mut output, &mut decoder, self.trap)?;
|
|
||||||
|
|
||||||
YamlLoader::load_from_str(&output).map_err(LoadError::Scan)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform a loop of [`Decoder::decode_to_string`], reallocating `output` if needed.
|
|
||||||
#[cfg(feature = "encoding")]
|
|
||||||
fn decode_loop(
|
|
||||||
input: &[u8],
|
|
||||||
output: &mut String,
|
|
||||||
decoder: &mut Decoder,
|
|
||||||
trap: YAMLDecodingTrap,
|
|
||||||
) -> Result<(), LoadError> {
|
|
||||||
output.reserve(input.len());
|
|
||||||
let mut total_bytes_read = 0;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match decoder.decode_to_string_without_replacement(&input[total_bytes_read..], output, true)
|
|
||||||
{
|
|
||||||
// If the input is empty, we processed the whole input.
|
|
||||||
(DecoderResult::InputEmpty, _) => break Ok(()),
|
|
||||||
// If the output is full, we must reallocate.
|
|
||||||
(DecoderResult::OutputFull, bytes_read) => {
|
|
||||||
total_bytes_read += bytes_read;
|
|
||||||
// The output is already reserved to the size of the input. We slowly resize. Here,
|
|
||||||
// we're expecting that 10% of bytes will double in size when converting to UTF-8.
|
|
||||||
output.reserve(input.len() / 10);
|
|
||||||
}
|
|
||||||
(DecoderResult::Malformed(malformed_len, bytes_after_malformed), bytes_read) => {
|
|
||||||
total_bytes_read += bytes_read;
|
|
||||||
match trap {
|
|
||||||
// Ignore (skip over) malformed character.
|
|
||||||
YAMLDecodingTrap::Ignore => {}
|
|
||||||
// Replace them with the Unicode REPLACEMENT CHARACTER.
|
|
||||||
YAMLDecodingTrap::Replace => {
|
|
||||||
output.push('\u{FFFD}');
|
|
||||||
}
|
|
||||||
// Otherwise error, getting as much context as possible.
|
|
||||||
YAMLDecodingTrap::Strict => {
|
|
||||||
let malformed_len = malformed_len as usize;
|
|
||||||
let bytes_after_malformed = bytes_after_malformed as usize;
|
|
||||||
let byte_idx = total_bytes_read - (malformed_len + bytes_after_malformed);
|
|
||||||
let malformed_sequence = &input[byte_idx..byte_idx + malformed_len];
|
|
||||||
|
|
||||||
break Err(LoadError::Decode(Cow::Owned(format!(
|
|
||||||
"Invalid character sequence at {byte_idx}: {malformed_sequence:?}",
|
|
||||||
))));
|
|
||||||
}
|
|
||||||
YAMLDecodingTrap::Call(callback) => {
|
|
||||||
let byte_idx =
|
|
||||||
total_bytes_read - ((malformed_len + bytes_after_malformed) as usize);
|
|
||||||
let malformed_sequence =
|
|
||||||
&input[byte_idx..byte_idx + malformed_len as usize];
|
|
||||||
if let ControlFlow::Break(error) = callback(
|
|
||||||
malformed_len,
|
|
||||||
bytes_after_malformed,
|
|
||||||
&input[byte_idx..],
|
|
||||||
output,
|
|
||||||
) {
|
|
||||||
if error.is_empty() {
|
|
||||||
break Err(LoadError::Decode(Cow::Owned(format!(
|
|
||||||
"Invalid character sequence at {byte_idx}: {malformed_sequence:?}",
|
|
||||||
))));
|
|
||||||
}
|
|
||||||
break Err(LoadError::Decode(error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The encoding crate knows how to tell apart UTF-8 from UTF-16LE and utf-16BE, when the
|
|
||||||
/// bytestream starts with BOM codepoint.
|
|
||||||
/// However, it doesn't even attempt to guess the UTF-16 endianness of the input bytestream since
|
|
||||||
/// in the general case the bytestream could start with a codepoint that uses both bytes.
|
|
||||||
///
|
|
||||||
/// The YAML-1.2 spec mandates that the first character of a YAML document is an ASCII character.
|
|
||||||
/// This allows the encoding to be deduced by the pattern of null (#x00) characters.
|
|
||||||
//
|
|
||||||
/// See spec at <https://yaml.org/spec/1.2/spec.html#id2771184>
|
|
||||||
#[cfg(feature = "encoding")]
|
|
||||||
fn detect_utf16_endianness(b: &[u8]) -> &'static Encoding {
|
|
||||||
if b.len() > 1 && (b[0] != b[1]) {
|
|
||||||
if b[0] == 0 {
|
|
||||||
return encoding_rs::UTF_16BE;
|
|
||||||
} else if b[1] == 0 {
|
|
||||||
return encoding_rs::UTF_16LE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
encoding_rs::UTF_8
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! define_as (
|
|
||||||
($name:ident, $t:ident, $yt:ident) => (
|
|
||||||
/// Get a copy of the inner object in the YAML enum if it is a `$t`.
|
|
||||||
///
|
|
||||||
/// # Return
|
|
||||||
/// If the variant of `self` is `Yaml::$yt`, return `Some($t)` with a copy of the `$t` contained.
|
|
||||||
/// Otherwise, return `None`.
|
|
||||||
#[must_use]
|
|
||||||
pub fn $name(&self) -> Option<$t> {
|
|
||||||
match *self {
|
|
||||||
Yaml::$yt(v) => Some(v),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
);
|
|
||||||
|
|
||||||
macro_rules! define_as_ref (
|
|
||||||
($name:ident, $t:ty, $yt:ident) => (
|
|
||||||
/// Get a reference to the inner object in the YAML enum if it is a `$t`.
|
|
||||||
///
|
|
||||||
/// # Return
|
|
||||||
/// If the variant of `self` is `Yaml::$yt`, return `Some(&$t)` with the `$t` contained. Otherwise,
|
|
||||||
/// return `None`.
|
|
||||||
#[must_use]
|
|
||||||
pub fn $name(&self) -> Option<$t> {
|
|
||||||
match *self {
|
|
||||||
Yaml::$yt(ref v) => Some(v),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
);
|
|
||||||
|
|
||||||
macro_rules! define_as_mut_ref (
|
|
||||||
($name:ident, $t:ty, $yt:ident) => (
|
|
||||||
/// Get a mutable reference to the inner object in the YAML enum if it is a `$t`.
|
|
||||||
///
|
|
||||||
/// # Return
|
|
||||||
/// If the variant of `self` is `Yaml::$yt`, return `Some(&mut $t)` with the `$t` contained.
|
|
||||||
/// Otherwise, return `None`.
|
|
||||||
#[must_use]
|
|
||||||
pub fn $name(&mut self) -> Option<$t> {
|
|
||||||
match *self {
|
|
||||||
Yaml::$yt(ref mut v) => Some(v),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
);
|
|
||||||
|
|
||||||
macro_rules! define_into (
|
|
||||||
($name:ident, $t:ty, $yt:ident) => (
|
|
||||||
/// Get the inner object in the YAML enum if it is a `$t`.
|
|
||||||
///
|
|
||||||
/// # Return
|
|
||||||
/// If the variant of `self` is `Yaml::$yt`, return `Some($t)` with the `$t` contained. Otherwise,
|
|
||||||
/// return `None`.
|
|
||||||
#[must_use]
|
|
||||||
pub fn $name(self) -> Option<$t> {
|
|
||||||
match self {
|
|
||||||
Yaml::$yt(v) => Some(v),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
);
|
|
||||||
|
|
||||||
impl Yaml {
|
|
||||||
define_as!(as_bool, bool, Boolean);
|
|
||||||
define_as!(as_i64, i64, Integer);
|
|
||||||
|
|
||||||
define_as_ref!(as_str, &str, String);
|
|
||||||
define_as_ref!(as_hash, &Hash, Hash);
|
|
||||||
define_as_ref!(as_vec, &Array, Array);
|
|
||||||
|
|
||||||
define_as_mut_ref!(as_mut_hash, &mut Hash, Hash);
|
|
||||||
define_as_mut_ref!(as_mut_vec, &mut Array, Array);
|
|
||||||
|
|
||||||
define_into!(into_bool, bool, Boolean);
|
|
||||||
define_into!(into_i64, i64, Integer);
|
|
||||||
define_into!(into_string, String, String);
|
|
||||||
define_into!(into_hash, Hash, Hash);
|
|
||||||
define_into!(into_vec, Array, Array);
|
|
||||||
|
|
||||||
/// Return whether `self` is a [`Yaml::Null`] node.
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_null(&self) -> bool {
|
|
||||||
matches!(*self, Yaml::Null)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return whether `self` is a [`Yaml::BadValue`] node.
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_badvalue(&self) -> bool {
|
|
||||||
matches!(*self, Yaml::BadValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return whether `self` is a [`Yaml::Array`] node.
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_array(&self) -> bool {
|
|
||||||
matches!(*self, Yaml::Array(_))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the `f64` value contained in this YAML node.
|
|
||||||
///
|
|
||||||
/// If the node is not a [`Yaml::Real`] YAML node or its contents is not a valid `f64` string,
|
|
||||||
/// `None` is returned.
|
|
||||||
#[must_use]
|
|
||||||
pub fn as_f64(&self) -> Option<f64> {
|
|
||||||
if let Yaml::Real(ref v) = self {
|
|
||||||
parse_f64(v)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the `f64` value contained in this YAML node.
|
|
||||||
///
|
|
||||||
/// If the node is not a [`Yaml::Real`] YAML node or its contents is not a valid `f64` string,
|
|
||||||
/// `None` is returned.
|
|
||||||
#[must_use]
|
|
||||||
pub fn into_f64(self) -> Option<f64> {
|
|
||||||
self.as_f64()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If a value is null or otherwise bad (see variants), consume it and
|
|
||||||
/// replace it with a given value `other`. Otherwise, return self unchanged.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use yaml_rust2::yaml::Yaml;
|
|
||||||
///
|
|
||||||
/// assert_eq!(Yaml::BadValue.or(Yaml::Integer(3)), Yaml::Integer(3));
|
|
||||||
/// assert_eq!(Yaml::Integer(3).or(Yaml::BadValue), Yaml::Integer(3));
|
|
||||||
/// ```
|
|
||||||
#[must_use]
|
|
||||||
pub fn or(self, other: Self) -> Self {
|
|
||||||
match self {
|
|
||||||
Yaml::BadValue | Yaml::Null => other,
|
|
||||||
this => this,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See `or` for behavior. This performs the same operations, but with
|
|
||||||
/// borrowed values for less linear pipelines.
|
|
||||||
#[must_use]
|
|
||||||
pub fn borrowed_or<'a>(&'a self, other: &'a Self) -> &'a Self {
|
|
||||||
match self {
|
|
||||||
Yaml::BadValue | Yaml::Null => other,
|
|
||||||
this => this,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::should_implement_trait))]
|
|
||||||
impl Yaml {
|
|
||||||
/// Convert a string to a [`Yaml`] node.
|
|
||||||
///
|
|
||||||
/// [`Yaml`] does not implement [`std::str::FromStr`] since conversion may not fail. This
|
|
||||||
/// function falls back to [`Yaml::String`] if nothing else matches.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```
|
|
||||||
/// # use yaml_rust2::yaml::Yaml;
|
|
||||||
/// assert!(matches!(Yaml::from_str("42"), Yaml::Integer(42)));
|
|
||||||
/// assert!(matches!(Yaml::from_str("0x2A"), Yaml::Integer(42)));
|
|
||||||
/// assert!(matches!(Yaml::from_str("0o52"), Yaml::Integer(42)));
|
|
||||||
/// assert!(matches!(Yaml::from_str("~"), Yaml::Null));
|
|
||||||
/// assert!(matches!(Yaml::from_str("null"), Yaml::Null));
|
|
||||||
/// assert!(matches!(Yaml::from_str("true"), Yaml::Boolean(true)));
|
|
||||||
/// assert!(matches!(Yaml::from_str("3.14"), Yaml::Real(_)));
|
|
||||||
/// assert!(matches!(Yaml::from_str("foo"), Yaml::String(_)));
|
|
||||||
/// ```
|
|
||||||
#[must_use]
|
|
||||||
pub fn from_str(v: &str) -> Yaml {
|
|
||||||
if let Some(number) = v.strip_prefix("0x") {
|
|
||||||
if let Ok(i) = i64::from_str_radix(number, 16) {
|
|
||||||
return Yaml::Integer(i);
|
|
||||||
}
|
|
||||||
} else if let Some(number) = v.strip_prefix("0o") {
|
|
||||||
if let Ok(i) = i64::from_str_radix(number, 8) {
|
|
||||||
return Yaml::Integer(i);
|
|
||||||
}
|
|
||||||
} else if let Some(number) = v.strip_prefix('+') {
|
|
||||||
if let Ok(i) = number.parse::<i64>() {
|
|
||||||
return Yaml::Integer(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match v {
|
|
||||||
"~" | "null" => Yaml::Null,
|
|
||||||
"true" => Yaml::Boolean(true),
|
|
||||||
"false" => Yaml::Boolean(false),
|
|
||||||
_ => {
|
|
||||||
if let Ok(integer) = v.parse::<i64>() {
|
|
||||||
Yaml::Integer(integer)
|
|
||||||
} else if parse_f64(v).is_some() {
|
|
||||||
Yaml::Real(v.to_owned())
|
|
||||||
} else {
|
|
||||||
Yaml::String(v.to_owned())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static BAD_VALUE: Yaml = Yaml::BadValue;
|
|
||||||
impl<'a> Index<&'a str> for Yaml {
|
|
||||||
type Output = Yaml;
|
|
||||||
|
|
||||||
fn index(&self, idx: &'a str) -> &Yaml {
|
|
||||||
let key = Yaml::String(idx.to_owned());
|
|
||||||
match self.as_hash() {
|
|
||||||
Some(h) => h.get(&key).unwrap_or(&BAD_VALUE),
|
|
||||||
None => &BAD_VALUE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IndexMut<&'a str> for Yaml {
|
|
||||||
fn index_mut(&mut self, idx: &'a str) -> &mut Yaml {
|
|
||||||
let key = Yaml::String(idx.to_owned());
|
|
||||||
match self.as_mut_hash() {
|
|
||||||
Some(h) => h.get_mut(&key).unwrap(),
|
|
||||||
None => panic!("Not a hash type"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Index<usize> for Yaml {
|
|
||||||
type Output = Yaml;
|
|
||||||
|
|
||||||
fn index(&self, idx: usize) -> &Yaml {
|
|
||||||
if let Some(v) = self.as_vec() {
|
|
||||||
v.get(idx).unwrap_or(&BAD_VALUE)
|
|
||||||
} else if let Some(v) = self.as_hash() {
|
|
||||||
let key = Yaml::Integer(i64::try_from(idx).unwrap());
|
|
||||||
v.get(&key).unwrap_or(&BAD_VALUE)
|
|
||||||
} else {
|
|
||||||
&BAD_VALUE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IndexMut<usize> for Yaml {
|
|
||||||
/// Perform indexing if `self` is a sequence or a mapping.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
/// This function panics if the index given is out of range (as per [`IndexMut`]). If `self` i
|
|
||||||
/// a [`Yaml::Array`], this is when the index is bigger or equal to the length of the
|
|
||||||
/// underlying `Vec`. If `self` is a [`Yaml::Hash`], this is when the mapping sequence does no
|
|
||||||
/// contain [`Yaml::Integer`]`(idx)` as a key.
|
|
||||||
///
|
|
||||||
/// This function also panics if `self` is not a [`Yaml::Array`] nor a [`Yaml::Hash`].
|
|
||||||
fn index_mut(&mut self, idx: usize) -> &mut Yaml {
|
|
||||||
match self {
|
|
||||||
Yaml::Array(sequence) => sequence.index_mut(idx),
|
|
||||||
Yaml::Hash(mapping) => {
|
|
||||||
let key = Yaml::Integer(i64::try_from(idx).unwrap());
|
|
||||||
mapping.get_mut(&key).unwrap()
|
|
||||||
}
|
|
||||||
_ => panic!("Attempting to index but `self` is not a sequence nor a mapping"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoIterator for Yaml {
|
|
||||||
type Item = Yaml;
|
|
||||||
type IntoIter = YamlIter;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
YamlIter {
|
|
||||||
yaml: self.into_vec().unwrap_or_default().into_iter(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An iterator over a [`Yaml`] node.
|
|
||||||
pub struct YamlIter {
|
|
||||||
yaml: std::vec::IntoIter<Yaml>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for YamlIter {
|
|
||||||
type Item = Yaml;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Yaml> {
|
|
||||||
self.yaml.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::{YAMLDecodingTrap, Yaml, YamlDecoder};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_bom() {
|
|
||||||
let s = b"\xef\xbb\xbf---
|
|
||||||
a: 1
|
|
||||||
b: 2.2
|
|
||||||
c: [1, 2]
|
|
||||||
";
|
|
||||||
let out = YamlDecoder::read(s as &[u8]).decode().unwrap();
|
|
||||||
let doc = &out[0];
|
|
||||||
assert_eq!(doc["a"].as_i64().unwrap(), 1i64);
|
|
||||||
assert!((doc["b"].as_f64().unwrap() - 2.2f64).abs() <= f64::EPSILON);
|
|
||||||
assert_eq!(doc["c"][1].as_i64().unwrap(), 2i64);
|
|
||||||
assert!(doc["d"][0].is_badvalue());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_utf16le() {
|
|
||||||
let s = b"\xff\xfe-\x00-\x00-\x00
|
|
||||||
\x00a\x00:\x00 \x001\x00
|
|
||||||
\x00b\x00:\x00 \x002\x00.\x002\x00
|
|
||||||
\x00c\x00:\x00 \x00[\x001\x00,\x00 \x002\x00]\x00
|
|
||||||
\x00";
|
|
||||||
let out = YamlDecoder::read(s as &[u8]).decode().unwrap();
|
|
||||||
let doc = &out[0];
|
|
||||||
println!("GOT: {doc:?}");
|
|
||||||
assert_eq!(doc["a"].as_i64().unwrap(), 1i64);
|
|
||||||
assert!((doc["b"].as_f64().unwrap() - 2.2f64) <= f64::EPSILON);
|
|
||||||
assert_eq!(doc["c"][1].as_i64().unwrap(), 2i64);
|
|
||||||
assert!(doc["d"][0].is_badvalue());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_utf16be() {
|
|
||||||
let s = b"\xfe\xff\x00-\x00-\x00-\x00
|
|
||||||
\x00a\x00:\x00 \x001\x00
|
|
||||||
\x00b\x00:\x00 \x002\x00.\x002\x00
|
|
||||||
\x00c\x00:\x00 \x00[\x001\x00,\x00 \x002\x00]\x00
|
|
||||||
";
|
|
||||||
let out = YamlDecoder::read(s as &[u8]).decode().unwrap();
|
|
||||||
let doc = &out[0];
|
|
||||||
println!("GOT: {doc:?}");
|
|
||||||
assert_eq!(doc["a"].as_i64().unwrap(), 1i64);
|
|
||||||
assert!((doc["b"].as_f64().unwrap() - 2.2f64).abs() <= f64::EPSILON);
|
|
||||||
assert_eq!(doc["c"][1].as_i64().unwrap(), 2i64);
|
|
||||||
assert!(doc["d"][0].is_badvalue());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_utf16le_nobom() {
|
|
||||||
let s = b"-\x00-\x00-\x00
|
|
||||||
\x00a\x00:\x00 \x001\x00
|
|
||||||
\x00b\x00:\x00 \x002\x00.\x002\x00
|
|
||||||
\x00c\x00:\x00 \x00[\x001\x00,\x00 \x002\x00]\x00
|
|
||||||
\x00";
|
|
||||||
let out = YamlDecoder::read(s as &[u8]).decode().unwrap();
|
|
||||||
let doc = &out[0];
|
|
||||||
println!("GOT: {doc:?}");
|
|
||||||
assert_eq!(doc["a"].as_i64().unwrap(), 1i64);
|
|
||||||
assert!((doc["b"].as_f64().unwrap() - 2.2f64).abs() <= f64::EPSILON);
|
|
||||||
assert_eq!(doc["c"][1].as_i64().unwrap(), 2i64);
|
|
||||||
assert!(doc["d"][0].is_badvalue());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_trap() {
|
|
||||||
let s = b"---
|
|
||||||
a\xa9: 1
|
|
||||||
b: 2.2
|
|
||||||
c: [1, 2]
|
|
||||||
";
|
|
||||||
let out = YamlDecoder::read(s as &[u8])
|
|
||||||
.encoding_trap(YAMLDecodingTrap::Ignore)
|
|
||||||
.decode()
|
|
||||||
.unwrap();
|
|
||||||
let doc = &out[0];
|
|
||||||
println!("GOT: {doc:?}");
|
|
||||||
assert_eq!(doc["a"].as_i64().unwrap(), 1i64);
|
|
||||||
assert!((doc["b"].as_f64().unwrap() - 2.2f64).abs() <= f64::EPSILON);
|
|
||||||
assert_eq!(doc["c"][1].as_i64().unwrap(), 2i64);
|
|
||||||
assert!(doc["d"][0].is_badvalue());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_or() {
|
|
||||||
assert_eq!(Yaml::Null.or(Yaml::Integer(3)), Yaml::Integer(3));
|
|
||||||
assert_eq!(Yaml::Integer(3).or(Yaml::Integer(7)), Yaml::Integer(3));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +1,23 @@
|
||||||
#![allow(clippy::bool_assert_comparison)]
|
#![allow(clippy::bool_assert_comparison)]
|
||||||
#![allow(clippy::float_cmp)]
|
#![allow(clippy::float_cmp)]
|
||||||
|
|
||||||
use std::vec;
|
use saphyr_parser::{Event, Parser, ScanError, TScalarStyle};
|
||||||
use yaml_rust2::{Yaml, YamlEmitter, YamlLoader};
|
|
||||||
|
|
||||||
#[test]
|
/// Run the parser through the string.
|
||||||
fn test_api() {
|
///
|
||||||
let s = "
|
/// # Returns
|
||||||
# from yaml-cpp example
|
/// This functions returns the events if parsing succeeds, the error the parser returned otherwise.
|
||||||
- name: Ogre
|
fn run_parser(input: &str) -> Result<Vec<Event>, ScanError> {
|
||||||
position: [0, 5, 0]
|
let mut events = vec![];
|
||||||
powers:
|
for x in Parser::new_from_str(input) {
|
||||||
- name: Club
|
let x = x?;
|
||||||
damage: 10
|
let end = x.0 == Event::StreamEnd;
|
||||||
- name: Fist
|
events.push(x.0);
|
||||||
damage: 8
|
if end {
|
||||||
- name: Dragon
|
break;
|
||||||
position: [1, 0, 10]
|
|
||||||
powers:
|
|
||||||
- name: Fire Breath
|
|
||||||
damage: 25
|
|
||||||
- name: Claws
|
|
||||||
damage: 15
|
|
||||||
- name: Wizard
|
|
||||||
position: [5, -3, 0]
|
|
||||||
powers:
|
|
||||||
- name: Acid Rain
|
|
||||||
damage: 50
|
|
||||||
- name: Staff
|
|
||||||
damage: 3
|
|
||||||
";
|
|
||||||
let docs = YamlLoader::load_from_str(s).unwrap();
|
|
||||||
let doc = &docs[0];
|
|
||||||
|
|
||||||
assert_eq!(doc[0]["name"].as_str().unwrap(), "Ogre");
|
|
||||||
|
|
||||||
let mut writer = String::new();
|
|
||||||
{
|
|
||||||
let mut emitter = YamlEmitter::new(&mut writer);
|
|
||||||
emitter.dump(doc).unwrap();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
assert!(!writer.is_empty());
|
Ok(events)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -52,9 +28,7 @@ scalar
|
||||||
key: [1, 2]]
|
key: [1, 2]]
|
||||||
key1:a2
|
key1:a2
|
||||||
";
|
";
|
||||||
let Err(error) = YamlLoader::load_from_str(s) else {
|
let Err(error) = run_parser(s) else { panic!() };
|
||||||
panic!()
|
|
||||||
};
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
error.info(),
|
error.info(),
|
||||||
"mapping values are not allowed in this context"
|
"mapping values are not allowed in this context"
|
||||||
|
@ -65,176 +39,143 @@ key1:a2
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_coerce() {
|
|
||||||
let s = "---
|
|
||||||
a: 1
|
|
||||||
b: 2.2
|
|
||||||
c: [1, 2]
|
|
||||||
";
|
|
||||||
let out = YamlLoader::load_from_str(s).unwrap();
|
|
||||||
let doc = &out[0];
|
|
||||||
assert_eq!(doc["a"].as_i64().unwrap(), 1i64);
|
|
||||||
assert_eq!(doc["b"].as_f64().unwrap(), 2.2f64);
|
|
||||||
assert_eq!(doc["c"][1].as_i64().unwrap(), 2i64);
|
|
||||||
assert!(doc["d"][0].is_badvalue());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_empty_doc() {
|
fn test_empty_doc() {
|
||||||
let s: String = String::new();
|
assert_eq!(
|
||||||
YamlLoader::load_from_str(&s).unwrap();
|
run_parser("").unwrap(),
|
||||||
let s: String = "---".to_owned();
|
[Event::StreamStart, Event::StreamEnd]
|
||||||
assert_eq!(YamlLoader::load_from_str(&s).unwrap()[0], Yaml::Null);
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
run_parser("---").unwrap(),
|
||||||
|
[
|
||||||
|
Event::StreamStart,
|
||||||
|
Event::DocumentStart,
|
||||||
|
Event::Scalar("~".to_string(), TScalarStyle::Plain, 0, None),
|
||||||
|
Event::DocumentEnd,
|
||||||
|
Event::StreamEnd,
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parser() {
|
fn test_utf() {
|
||||||
let s: String = "
|
assert_eq!(
|
||||||
# comment
|
run_parser("a: 你好").unwrap(),
|
||||||
a0 bb: val
|
[
|
||||||
a1:
|
Event::StreamStart,
|
||||||
b1: 4
|
Event::DocumentStart,
|
||||||
b2: d
|
Event::MappingStart(0, None),
|
||||||
a2: 4 # i'm comment
|
Event::Scalar("a".to_string(), TScalarStyle::Plain, 0, None),
|
||||||
a3: [1, 2, 3]
|
Event::Scalar("你好".to_string(), TScalarStyle::Plain, 0, None),
|
||||||
a4:
|
Event::MappingEnd,
|
||||||
- - a1
|
Event::DocumentEnd,
|
||||||
- a2
|
Event::StreamEnd,
|
||||||
- 2
|
]
|
||||||
a5: 'single_quoted'
|
);
|
||||||
a6: \"double_quoted\"
|
}
|
||||||
a7: 你好
|
|
||||||
"
|
#[test]
|
||||||
.to_owned();
|
fn test_comments() {
|
||||||
let out = YamlLoader::load_from_str(&s).unwrap();
|
let s = "
|
||||||
let doc = &out[0];
|
# This is a comment
|
||||||
assert_eq!(doc["a7"].as_str().unwrap(), "你好");
|
a: b # This is another comment
|
||||||
|
##
|
||||||
|
#
|
||||||
|
";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
run_parser(s).unwrap(),
|
||||||
|
[
|
||||||
|
Event::StreamStart,
|
||||||
|
Event::DocumentStart,
|
||||||
|
Event::MappingStart(0, None),
|
||||||
|
Event::Scalar("a".to_string(), TScalarStyle::Plain, 0, None),
|
||||||
|
Event::Scalar("b".to_string(), TScalarStyle::Plain, 0, None),
|
||||||
|
Event::MappingEnd,
|
||||||
|
Event::DocumentEnd,
|
||||||
|
Event::StreamEnd,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_quoting() {
|
||||||
|
let s = "
|
||||||
|
- plain
|
||||||
|
- 'squote'
|
||||||
|
- \"dquote\"
|
||||||
|
";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
run_parser(s).unwrap(),
|
||||||
|
[
|
||||||
|
Event::StreamStart,
|
||||||
|
Event::DocumentStart,
|
||||||
|
Event::SequenceStart(0, None),
|
||||||
|
Event::Scalar("plain".to_string(), TScalarStyle::Plain, 0, None),
|
||||||
|
Event::Scalar("squote".to_string(), TScalarStyle::SingleQuoted, 0, None),
|
||||||
|
Event::Scalar("dquote".to_string(), TScalarStyle::DoubleQuoted, 0, None),
|
||||||
|
Event::SequenceEnd,
|
||||||
|
Event::DocumentEnd,
|
||||||
|
Event::StreamEnd,
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_multi_doc() {
|
fn test_multi_doc() {
|
||||||
let s = "
|
let s = "
|
||||||
'a scalar'
|
a scalar
|
||||||
---
|
---
|
||||||
'a scalar'
|
a scalar
|
||||||
---
|
---
|
||||||
'a scalar'
|
a scalar
|
||||||
";
|
";
|
||||||
let out = YamlLoader::load_from_str(s).unwrap();
|
assert_eq!(
|
||||||
assert_eq!(out.len(), 3);
|
run_parser(s).unwrap(),
|
||||||
}
|
[
|
||||||
|
Event::StreamStart,
|
||||||
#[test]
|
Event::DocumentStart,
|
||||||
fn test_anchor() {
|
Event::Scalar("a scalar".to_string(), TScalarStyle::Plain, 0, None),
|
||||||
let s = "
|
Event::DocumentEnd,
|
||||||
a1: &DEFAULT
|
Event::DocumentStart,
|
||||||
b1: 4
|
Event::Scalar("a scalar".to_string(), TScalarStyle::Plain, 0, None),
|
||||||
b2: d
|
Event::DocumentEnd,
|
||||||
a2: *DEFAULT
|
Event::DocumentStart,
|
||||||
";
|
Event::Scalar("a scalar".to_string(), TScalarStyle::Plain, 0, None),
|
||||||
let out = YamlLoader::load_from_str(s).unwrap();
|
Event::DocumentEnd,
|
||||||
let doc = &out[0];
|
Event::StreamEnd,
|
||||||
assert_eq!(doc["a2"]["b1"].as_i64().unwrap(), 4);
|
]
|
||||||
}
|
);
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_bad_anchor() {
|
|
||||||
let s = "
|
|
||||||
a1: &DEFAULT
|
|
||||||
b1: 4
|
|
||||||
b2: *DEFAULT
|
|
||||||
";
|
|
||||||
let out = YamlLoader::load_from_str(s).unwrap();
|
|
||||||
let doc = &out[0];
|
|
||||||
assert_eq!(doc["a1"]["b2"], Yaml::BadValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_github_27() {
|
fn test_github_27() {
|
||||||
// https://github.com/chyh1990/yaml-rust/issues/27
|
// https://github.com/chyh1990/yaml-rust/issues/27
|
||||||
let s = "&a";
|
assert_eq!(
|
||||||
let out = YamlLoader::load_from_str(s).unwrap();
|
run_parser("&a").unwrap(),
|
||||||
let doc = &out[0];
|
[
|
||||||
assert_eq!(doc.as_str().unwrap(), "");
|
Event::StreamStart,
|
||||||
}
|
Event::DocumentStart,
|
||||||
|
Event::Scalar(String::new(), TScalarStyle::Plain, 1, None),
|
||||||
#[test]
|
Event::DocumentEnd,
|
||||||
fn test_plain_datatype() {
|
Event::StreamEnd,
|
||||||
let s = "
|
]
|
||||||
- 'string'
|
);
|
||||||
- \"string\"
|
|
||||||
- string
|
|
||||||
- 123
|
|
||||||
- -321
|
|
||||||
- 1.23
|
|
||||||
- -1e4
|
|
||||||
- ~
|
|
||||||
- null
|
|
||||||
- true
|
|
||||||
- false
|
|
||||||
- !!str 0
|
|
||||||
- !!int 100
|
|
||||||
- !!float 2
|
|
||||||
- !!null ~
|
|
||||||
- !!bool true
|
|
||||||
- !!bool false
|
|
||||||
- 0xFF
|
|
||||||
# bad values
|
|
||||||
- !!int string
|
|
||||||
- !!float string
|
|
||||||
- !!bool null
|
|
||||||
- !!null val
|
|
||||||
- 0o77
|
|
||||||
- [ 0xF, 0xF ]
|
|
||||||
- +12345
|
|
||||||
- [ true, false ]
|
|
||||||
";
|
|
||||||
let out = YamlLoader::load_from_str(s).unwrap();
|
|
||||||
let doc = &out[0];
|
|
||||||
|
|
||||||
assert_eq!(doc[0].as_str().unwrap(), "string");
|
|
||||||
assert_eq!(doc[1].as_str().unwrap(), "string");
|
|
||||||
assert_eq!(doc[2].as_str().unwrap(), "string");
|
|
||||||
assert_eq!(doc[3].as_i64().unwrap(), 123);
|
|
||||||
assert_eq!(doc[4].as_i64().unwrap(), -321);
|
|
||||||
assert_eq!(doc[5].as_f64().unwrap(), 1.23);
|
|
||||||
assert_eq!(doc[6].as_f64().unwrap(), -1e4);
|
|
||||||
assert!(doc[7].is_null());
|
|
||||||
assert!(doc[8].is_null());
|
|
||||||
assert_eq!(doc[9].as_bool().unwrap(), true);
|
|
||||||
assert_eq!(doc[10].as_bool().unwrap(), false);
|
|
||||||
assert_eq!(doc[11].as_str().unwrap(), "0");
|
|
||||||
assert_eq!(doc[12].as_i64().unwrap(), 100);
|
|
||||||
assert_eq!(doc[13].as_f64().unwrap(), 2.0);
|
|
||||||
assert!(doc[14].is_null());
|
|
||||||
assert_eq!(doc[15].as_bool().unwrap(), true);
|
|
||||||
assert_eq!(doc[16].as_bool().unwrap(), false);
|
|
||||||
assert_eq!(doc[17].as_i64().unwrap(), 255);
|
|
||||||
assert!(doc[18].is_badvalue());
|
|
||||||
assert!(doc[19].is_badvalue());
|
|
||||||
assert!(doc[20].is_badvalue());
|
|
||||||
assert!(doc[21].is_badvalue());
|
|
||||||
assert_eq!(doc[22].as_i64().unwrap(), 63);
|
|
||||||
assert_eq!(doc[23][0].as_i64().unwrap(), 15);
|
|
||||||
assert_eq!(doc[23][1].as_i64().unwrap(), 15);
|
|
||||||
assert_eq!(doc[24].as_i64().unwrap(), 12345);
|
|
||||||
assert!(doc[25][0].as_bool().unwrap());
|
|
||||||
assert!(!doc[25][1].as_bool().unwrap());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bad_hyphen() {
|
fn test_bad_hyphen() {
|
||||||
// See: https://github.com/chyh1990/yaml-rust/issues/23
|
// See: https://github.com/chyh1990/yaml-rust/issues/23
|
||||||
let s = "{-";
|
assert!(run_parser("{-").is_err());
|
||||||
assert!(YamlLoader::load_from_str(s).is_err());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_issue_65() {
|
fn test_issue_65() {
|
||||||
// See: https://github.com/chyh1990/yaml-rust/issues/65
|
// See: https://github.com/chyh1990/yaml-rust/issues/65
|
||||||
let b = "\n\"ll\\\"ll\\\r\n\"ll\\\"ll\\\r\r\r\rU\r\r\rU";
|
let b = "\n\"ll\\\"ll\\\r\n\"ll\\\"ll\\\r\r\r\rU\r\r\rU";
|
||||||
assert!(YamlLoader::load_from_str(b).is_err());
|
assert!(run_parser(b).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -242,201 +183,104 @@ fn test_issue_65_mwe() {
|
||||||
// A MWE for `test_issue_65`. The error over there is that there is invalid trailing content
|
// A MWE for `test_issue_65`. The error over there is that there is invalid trailing content
|
||||||
// after a double quoted string.
|
// after a double quoted string.
|
||||||
let b = r#""foo" l"#;
|
let b = r#""foo" l"#;
|
||||||
assert!(YamlLoader::load_from_str(b).is_err());
|
assert!(run_parser(b).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bad_docstart() {
|
fn test_bad_docstart() {
|
||||||
assert!(YamlLoader::load_from_str("---This used to cause an infinite loop").is_ok());
|
assert!(run_parser("---This used to cause an infinite loop").is_ok());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
YamlLoader::load_from_str("----"),
|
run_parser("----").unwrap(),
|
||||||
Ok(vec![Yaml::String(String::from("----"))])
|
[
|
||||||
|
Event::StreamStart,
|
||||||
|
Event::DocumentStart,
|
||||||
|
Event::Scalar("----".to_string(), TScalarStyle::Plain, 0, None),
|
||||||
|
Event::DocumentEnd,
|
||||||
|
Event::StreamEnd,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
|
||||||
YamlLoader::load_from_str("--- #here goes a comment"),
|
|
||||||
Ok(vec![Yaml::Null])
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
YamlLoader::load_from_str("---- #here goes a comment"),
|
|
||||||
Ok(vec![Yaml::String(String::from("----"))])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_plain_datatype_with_into_methods() {
|
|
||||||
let s = "
|
|
||||||
- 'string'
|
|
||||||
- \"string\"
|
|
||||||
- string
|
|
||||||
- 123
|
|
||||||
- -321
|
|
||||||
- 1.23
|
|
||||||
- -1e4
|
|
||||||
- true
|
|
||||||
- false
|
|
||||||
- !!str 0
|
|
||||||
- !!int 100
|
|
||||||
- !!float 2
|
|
||||||
- !!bool true
|
|
||||||
- !!bool false
|
|
||||||
- 0xFF
|
|
||||||
- 0o77
|
|
||||||
- +12345
|
|
||||||
- -.INF
|
|
||||||
- .NAN
|
|
||||||
- !!float .INF
|
|
||||||
";
|
|
||||||
let mut out = YamlLoader::load_from_str(s).unwrap().into_iter();
|
|
||||||
let mut doc = out.next().unwrap().into_iter();
|
|
||||||
|
|
||||||
assert_eq!(doc.next().unwrap().into_string().unwrap(), "string");
|
|
||||||
assert_eq!(doc.next().unwrap().into_string().unwrap(), "string");
|
|
||||||
assert_eq!(doc.next().unwrap().into_string().unwrap(), "string");
|
|
||||||
assert_eq!(doc.next().unwrap().into_i64().unwrap(), 123);
|
|
||||||
assert_eq!(doc.next().unwrap().into_i64().unwrap(), -321);
|
|
||||||
assert_eq!(doc.next().unwrap().into_f64().unwrap(), 1.23);
|
|
||||||
assert_eq!(doc.next().unwrap().into_f64().unwrap(), -1e4);
|
|
||||||
assert_eq!(doc.next().unwrap().into_bool().unwrap(), true);
|
|
||||||
assert_eq!(doc.next().unwrap().into_bool().unwrap(), false);
|
|
||||||
assert_eq!(doc.next().unwrap().into_string().unwrap(), "0");
|
|
||||||
assert_eq!(doc.next().unwrap().into_i64().unwrap(), 100);
|
|
||||||
assert_eq!(doc.next().unwrap().into_f64().unwrap(), 2.0);
|
|
||||||
assert_eq!(doc.next().unwrap().into_bool().unwrap(), true);
|
|
||||||
assert_eq!(doc.next().unwrap().into_bool().unwrap(), false);
|
|
||||||
assert_eq!(doc.next().unwrap().into_i64().unwrap(), 255);
|
|
||||||
assert_eq!(doc.next().unwrap().into_i64().unwrap(), 63);
|
|
||||||
assert_eq!(doc.next().unwrap().into_i64().unwrap(), 12345);
|
|
||||||
assert_eq!(doc.next().unwrap().into_f64().unwrap(), f64::NEG_INFINITY);
|
|
||||||
assert!(doc.next().unwrap().into_f64().is_some());
|
|
||||||
assert_eq!(doc.next().unwrap().into_f64().unwrap(), f64::INFINITY);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_hash_order() {
|
|
||||||
let s = "---
|
|
||||||
b: ~
|
|
||||||
a: ~
|
|
||||||
c: ~
|
|
||||||
";
|
|
||||||
let out = YamlLoader::load_from_str(s).unwrap();
|
|
||||||
let first = out.into_iter().next().unwrap();
|
|
||||||
let mut iter = first.into_hash().unwrap().into_iter();
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some((Yaml::String("b".to_owned()), Yaml::Null)),
|
run_parser("--- #comment").unwrap(),
|
||||||
iter.next()
|
[
|
||||||
|
Event::StreamStart,
|
||||||
|
Event::DocumentStart,
|
||||||
|
Event::Scalar("~".to_string(), TScalarStyle::Plain, 0, None),
|
||||||
|
Event::DocumentEnd,
|
||||||
|
Event::StreamEnd,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
|
||||||
Some((Yaml::String("a".to_owned()), Yaml::Null)),
|
|
||||||
iter.next()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Some((Yaml::String("c".to_owned()), Yaml::Null)),
|
|
||||||
iter.next()
|
|
||||||
);
|
|
||||||
assert_eq!(None, iter.next());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
assert_eq!(
|
||||||
fn test_integer_key() {
|
run_parser("---- #comment").unwrap(),
|
||||||
let s = "
|
[
|
||||||
0:
|
Event::StreamStart,
|
||||||
important: true
|
Event::DocumentStart,
|
||||||
1:
|
Event::Scalar("----".to_string(), TScalarStyle::Plain, 0, None),
|
||||||
important: false
|
Event::DocumentEnd,
|
||||||
";
|
Event::StreamEnd,
|
||||||
let out = YamlLoader::load_from_str(s).unwrap();
|
]
|
||||||
let first = out.into_iter().next().unwrap();
|
);
|
||||||
assert_eq!(first[0]["important"].as_bool().unwrap(), true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_indentation_equality() {
|
fn test_indentation_equality() {
|
||||||
let four_spaces = YamlLoader::load_from_str(
|
let four_spaces = run_parser(
|
||||||
r"
|
r"
|
||||||
hash:
|
hash:
|
||||||
with:
|
with:
|
||||||
indentations
|
indentations
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.unwrap()
|
|
||||||
.into_iter()
|
|
||||||
.next()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let two_spaces = YamlLoader::load_from_str(
|
let two_spaces = run_parser(
|
||||||
r"
|
r"
|
||||||
hash:
|
hash:
|
||||||
with:
|
with:
|
||||||
indentations
|
indentations
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.unwrap()
|
|
||||||
.into_iter()
|
|
||||||
.next()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let one_space = YamlLoader::load_from_str(
|
let one_space = run_parser(
|
||||||
r"
|
r"
|
||||||
hash:
|
hash:
|
||||||
with:
|
with:
|
||||||
indentations
|
indentations
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.unwrap()
|
|
||||||
.into_iter()
|
|
||||||
.next()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mixed_spaces = YamlLoader::load_from_str(
|
let mixed_spaces = run_parser(
|
||||||
r"
|
r"
|
||||||
hash:
|
hash:
|
||||||
with:
|
with:
|
||||||
indentations
|
indentations
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.unwrap()
|
|
||||||
.into_iter()
|
|
||||||
.next()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(four_spaces, two_spaces);
|
for (((a, b), c), d) in four_spaces
|
||||||
assert_eq!(two_spaces, one_space);
|
.iter()
|
||||||
assert_eq!(four_spaces, mixed_spaces);
|
.zip(two_spaces.iter())
|
||||||
|
.zip(one_space.iter())
|
||||||
|
.zip(mixed_spaces.iter())
|
||||||
|
{
|
||||||
|
assert!(a == b);
|
||||||
|
assert!(a == c);
|
||||||
|
assert!(a == d);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_two_space_indentations() {
|
|
||||||
// https://github.com/kbknapp/clap-rs/issues/965
|
|
||||||
|
|
||||||
let s = r"
|
|
||||||
subcommands:
|
|
||||||
- server:
|
|
||||||
about: server related commands
|
|
||||||
subcommands2:
|
|
||||||
- server:
|
|
||||||
about: server related commands
|
|
||||||
subcommands3:
|
|
||||||
- server:
|
|
||||||
about: server related commands
|
|
||||||
";
|
|
||||||
|
|
||||||
let out = YamlLoader::load_from_str(s).unwrap();
|
|
||||||
let doc = &out.into_iter().next().unwrap();
|
|
||||||
|
|
||||||
println!("{doc:#?}");
|
|
||||||
assert_eq!(doc["subcommands"][0]["server"], Yaml::Null);
|
|
||||||
assert!(doc["subcommands2"][0]["server"].as_hash().is_some());
|
|
||||||
assert!(doc["subcommands3"][0]["server"].as_hash().is_some());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_recursion_depth_check_objects() {
|
fn test_recursion_depth_check_objects() {
|
||||||
let s = "{a:".repeat(10_000) + &"}".repeat(10_000);
|
let s = "{a:".repeat(10_000) + &"}".repeat(10_000);
|
||||||
assert!(YamlLoader::load_from_str(&s).is_err());
|
assert!(run_parser(&s).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_recursion_depth_check_arrays() {
|
fn test_recursion_depth_check_arrays() {
|
||||||
let s = "[".repeat(10_000) + &"]".repeat(10_000);
|
let s = "[".repeat(10_000) + &"]".repeat(10_000);
|
||||||
assert!(YamlLoader::load_from_str(&s).is_err());
|
assert!(run_parser(&s).is_err());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,294 +0,0 @@
|
||||||
use yaml_rust2::{YamlEmitter, YamlLoader};
|
|
||||||
|
|
||||||
#[allow(clippy::similar_names)]
|
|
||||||
#[test]
|
|
||||||
fn test_emit_simple() {
|
|
||||||
let s = "
|
|
||||||
# comment
|
|
||||||
a0 bb: val
|
|
||||||
a1:
|
|
||||||
b1: 4
|
|
||||||
b2: d
|
|
||||||
a2: 4 # i'm comment
|
|
||||||
a3: [1, 2, 3]
|
|
||||||
a4:
|
|
||||||
- [a1, a2]
|
|
||||||
- 2
|
|
||||||
";
|
|
||||||
|
|
||||||
let docs = YamlLoader::load_from_str(s).unwrap();
|
|
||||||
let doc = &docs[0];
|
|
||||||
let mut writer = String::new();
|
|
||||||
{
|
|
||||||
let mut emitter = YamlEmitter::new(&mut writer);
|
|
||||||
emitter.dump(doc).unwrap();
|
|
||||||
}
|
|
||||||
println!("original:\n{s}");
|
|
||||||
println!("emitted:\n{writer}");
|
|
||||||
let docs_new = match YamlLoader::load_from_str(&writer) {
|
|
||||||
Ok(y) => y,
|
|
||||||
Err(e) => panic!("{}", e),
|
|
||||||
};
|
|
||||||
let doc_new = &docs_new[0];
|
|
||||||
|
|
||||||
assert_eq!(doc, doc_new);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_emit_complex() {
|
|
||||||
let s = r"
|
|
||||||
catalogue:
|
|
||||||
product: &coffee { name: Coffee, price: 2.5 , unit: 1l }
|
|
||||||
product: &cookies { name: Cookies!, price: 3.40 , unit: 400g}
|
|
||||||
|
|
||||||
products:
|
|
||||||
*coffee :
|
|
||||||
amount: 4
|
|
||||||
*cookies :
|
|
||||||
amount: 4
|
|
||||||
[1,2,3,4]:
|
|
||||||
array key
|
|
||||||
2.4:
|
|
||||||
real key
|
|
||||||
true:
|
|
||||||
bool key
|
|
||||||
{}:
|
|
||||||
empty hash key
|
|
||||||
";
|
|
||||||
let docs = YamlLoader::load_from_str(s).unwrap();
|
|
||||||
let doc = &docs[0];
|
|
||||||
let mut writer = String::new();
|
|
||||||
{
|
|
||||||
let mut emitter = YamlEmitter::new(&mut writer);
|
|
||||||
emitter.dump(doc).unwrap();
|
|
||||||
}
|
|
||||||
let docs_new = match YamlLoader::load_from_str(&writer) {
|
|
||||||
Ok(y) => y,
|
|
||||||
Err(e) => panic!("{}", e),
|
|
||||||
};
|
|
||||||
let new_doc = &docs_new[0];
|
|
||||||
assert_eq!(doc, new_doc);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_emit_avoid_quotes() {
|
|
||||||
let s = r#"---
|
|
||||||
a7: 你好
|
|
||||||
boolean: "true"
|
|
||||||
boolean2: "false"
|
|
||||||
date: 2014-12-31
|
|
||||||
empty_string: ""
|
|
||||||
empty_string1: " "
|
|
||||||
empty_string2: " a"
|
|
||||||
empty_string3: " a "
|
|
||||||
exp: "12e7"
|
|
||||||
field: ":"
|
|
||||||
field2: "{"
|
|
||||||
field3: "\\"
|
|
||||||
field4: "\n"
|
|
||||||
field5: "can't avoid quote"
|
|
||||||
float: "2.6"
|
|
||||||
int: "4"
|
|
||||||
nullable: "null"
|
|
||||||
nullable2: "~"
|
|
||||||
products:
|
|
||||||
"*coffee":
|
|
||||||
amount: 4
|
|
||||||
"*cookies":
|
|
||||||
amount: 4
|
|
||||||
".milk":
|
|
||||||
amount: 1
|
|
||||||
"2.4": real key
|
|
||||||
"[1,2,3,4]": array key
|
|
||||||
"true": bool key
|
|
||||||
"{}": empty hash key
|
|
||||||
x: test
|
|
||||||
y: avoid quoting here
|
|
||||||
z: string with spaces"#;
|
|
||||||
|
|
||||||
let docs = YamlLoader::load_from_str(s).unwrap();
|
|
||||||
let doc = &docs[0];
|
|
||||||
let mut writer = String::new();
|
|
||||||
{
|
|
||||||
let mut emitter = YamlEmitter::new(&mut writer);
|
|
||||||
emitter.dump(doc).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(s, writer, "actual:\n\n{writer}\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn emit_quoted_bools() {
|
|
||||||
let input = r#"---
|
|
||||||
string0: yes
|
|
||||||
string1: no
|
|
||||||
string2: "true"
|
|
||||||
string3: "false"
|
|
||||||
string4: "~"
|
|
||||||
null0: ~
|
|
||||||
[true, false]: real_bools
|
|
||||||
[True, TRUE, False, FALSE, y,Y,yes,Yes,YES,n,N,no,No,NO,on,On,ON,off,Off,OFF]: false_bools
|
|
||||||
bool0: true
|
|
||||||
bool1: false"#;
|
|
||||||
let expected = r#"---
|
|
||||||
string0: "yes"
|
|
||||||
string1: "no"
|
|
||||||
string2: "true"
|
|
||||||
string3: "false"
|
|
||||||
string4: "~"
|
|
||||||
null0: ~
|
|
||||||
? - true
|
|
||||||
- false
|
|
||||||
: real_bools
|
|
||||||
? - "True"
|
|
||||||
- "TRUE"
|
|
||||||
- "False"
|
|
||||||
- "FALSE"
|
|
||||||
- y
|
|
||||||
- Y
|
|
||||||
- "yes"
|
|
||||||
- "Yes"
|
|
||||||
- "YES"
|
|
||||||
- n
|
|
||||||
- N
|
|
||||||
- "no"
|
|
||||||
- "No"
|
|
||||||
- "NO"
|
|
||||||
- "on"
|
|
||||||
- "On"
|
|
||||||
- "ON"
|
|
||||||
- "off"
|
|
||||||
- "Off"
|
|
||||||
- "OFF"
|
|
||||||
: false_bools
|
|
||||||
bool0: true
|
|
||||||
bool1: false"#;
|
|
||||||
|
|
||||||
let docs = YamlLoader::load_from_str(input).unwrap();
|
|
||||||
let doc = &docs[0];
|
|
||||||
let mut writer = String::new();
|
|
||||||
{
|
|
||||||
let mut emitter = YamlEmitter::new(&mut writer);
|
|
||||||
emitter.dump(doc).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
expected, writer,
|
|
||||||
"expected:\n{expected}\nactual:\n{writer}\n",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_empty_and_nested() {
|
|
||||||
test_empty_and_nested_flag(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_empty_and_nested_compact() {
|
|
||||||
test_empty_and_nested_flag(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_empty_and_nested_flag(compact: bool) {
|
|
||||||
let s = if compact {
|
|
||||||
r"---
|
|
||||||
a:
|
|
||||||
b:
|
|
||||||
c: hello
|
|
||||||
d: {}
|
|
||||||
e:
|
|
||||||
- f
|
|
||||||
- g
|
|
||||||
- h: []"
|
|
||||||
} else {
|
|
||||||
r"---
|
|
||||||
a:
|
|
||||||
b:
|
|
||||||
c: hello
|
|
||||||
d: {}
|
|
||||||
e:
|
|
||||||
- f
|
|
||||||
- g
|
|
||||||
-
|
|
||||||
h: []"
|
|
||||||
};
|
|
||||||
|
|
||||||
let docs = YamlLoader::load_from_str(s).unwrap();
|
|
||||||
let doc = &docs[0];
|
|
||||||
let mut writer = String::new();
|
|
||||||
{
|
|
||||||
let mut emitter = YamlEmitter::new(&mut writer);
|
|
||||||
emitter.compact(compact);
|
|
||||||
emitter.dump(doc).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(s, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_nested_arrays() {
|
|
||||||
let s = r"---
|
|
||||||
a:
|
|
||||||
- b
|
|
||||||
- - c
|
|
||||||
- d
|
|
||||||
- - e
|
|
||||||
- f";
|
|
||||||
|
|
||||||
let docs = YamlLoader::load_from_str(s).unwrap();
|
|
||||||
let doc = &docs[0];
|
|
||||||
let mut writer = String::new();
|
|
||||||
{
|
|
||||||
let mut emitter = YamlEmitter::new(&mut writer);
|
|
||||||
emitter.dump(doc).unwrap();
|
|
||||||
}
|
|
||||||
println!("original:\n{s}");
|
|
||||||
println!("emitted:\n{writer}");
|
|
||||||
|
|
||||||
assert_eq!(s, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_deeply_nested_arrays() {
|
|
||||||
let s = r"---
|
|
||||||
a:
|
|
||||||
- b
|
|
||||||
- - c
|
|
||||||
- d
|
|
||||||
- - e
|
|
||||||
- - f
|
|
||||||
- - e";
|
|
||||||
|
|
||||||
let docs = YamlLoader::load_from_str(s).unwrap();
|
|
||||||
let doc = &docs[0];
|
|
||||||
let mut writer = String::new();
|
|
||||||
{
|
|
||||||
let mut emitter = YamlEmitter::new(&mut writer);
|
|
||||||
emitter.dump(doc).unwrap();
|
|
||||||
}
|
|
||||||
println!("original:\n{s}");
|
|
||||||
println!("emitted:\n{writer}");
|
|
||||||
|
|
||||||
assert_eq!(s, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_nested_hashes() {
|
|
||||||
let s = r"---
|
|
||||||
a:
|
|
||||||
b:
|
|
||||||
c:
|
|
||||||
d:
|
|
||||||
e: f";
|
|
||||||
|
|
||||||
let docs = YamlLoader::load_from_str(s).unwrap();
|
|
||||||
let doc = &docs[0];
|
|
||||||
let mut writer = String::new();
|
|
||||||
{
|
|
||||||
let mut emitter = YamlEmitter::new(&mut writer);
|
|
||||||
emitter.dump(doc).unwrap();
|
|
||||||
}
|
|
||||||
println!("original:\n{s}");
|
|
||||||
println!("emitted:\n{writer}");
|
|
||||||
|
|
||||||
assert_eq!(s, writer);
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
extern crate yaml_rust2;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate quickcheck;
|
|
||||||
|
|
||||||
use quickcheck::TestResult;
|
|
||||||
use yaml_rust2::{Yaml, YamlEmitter, YamlLoader};
|
|
||||||
|
|
||||||
quickcheck! {
|
|
||||||
fn test_check_weird_keys(xs: Vec<String>) -> TestResult {
|
|
||||||
let mut out_str = String::new();
|
|
||||||
let input = Yaml::Array(xs.into_iter().map(Yaml::String).collect());
|
|
||||||
{
|
|
||||||
let mut emitter = YamlEmitter::new(&mut out_str);
|
|
||||||
emitter.dump(&input).unwrap();
|
|
||||||
}
|
|
||||||
match YamlLoader::load_from_str(&out_str) {
|
|
||||||
Ok(output) => TestResult::from_bool(output.len() == 1 && input == output[0]),
|
|
||||||
Err(err) => TestResult::error(err.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,440 +0,0 @@
|
||||||
#![allow(clippy::enum_glob_use)]
|
|
||||||
|
|
||||||
use yaml_rust2::{scanner::TokenType::*, scanner::*};
|
|
||||||
|
|
||||||
macro_rules! next {
|
|
||||||
($p:ident, $tk:pat) => {{
|
|
||||||
let tok = $p.next().unwrap();
|
|
||||||
match tok.1 {
|
|
||||||
$tk => {}
|
|
||||||
_ => panic!("unexpected token: {:?}", tok),
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! next_scalar {
|
|
||||||
($p:ident, $tk:expr, $v:expr) => {{
|
|
||||||
let tok = $p.next().unwrap();
|
|
||||||
match tok.1 {
|
|
||||||
Scalar(style, ref v) => {
|
|
||||||
assert_eq!(style, $tk);
|
|
||||||
assert_eq!(v, $v);
|
|
||||||
}
|
|
||||||
_ => panic!("unexpected token: {:?}", tok),
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! end {
|
|
||||||
($p:ident) => {{
|
|
||||||
assert_eq!($p.next(), None);
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
/// test cases in libyaml scanner.c
|
|
||||||
#[test]
|
|
||||||
fn test_empty() {
|
|
||||||
let s = "";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_scalar() {
|
|
||||||
let s = "a scalar";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next!(p, Scalar(TScalarStyle::Plain, _));
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_explicit_scalar() {
|
|
||||||
let s = "---
|
|
||||||
'a scalar'
|
|
||||||
...
|
|
||||||
";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next!(p, DocumentStart);
|
|
||||||
next!(p, Scalar(TScalarStyle::SingleQuoted, _));
|
|
||||||
next!(p, DocumentEnd);
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_multiple_documents() {
|
|
||||||
let s = "
|
|
||||||
'a scalar'
|
|
||||||
---
|
|
||||||
'a scalar'
|
|
||||||
---
|
|
||||||
'a scalar'
|
|
||||||
";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next!(p, Scalar(TScalarStyle::SingleQuoted, _));
|
|
||||||
next!(p, DocumentStart);
|
|
||||||
next!(p, Scalar(TScalarStyle::SingleQuoted, _));
|
|
||||||
next!(p, DocumentStart);
|
|
||||||
next!(p, Scalar(TScalarStyle::SingleQuoted, _));
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_a_flow_sequence() {
|
|
||||||
let s = "[item 1, item 2, item 3]";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next!(p, FlowSequenceStart);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "item 1");
|
|
||||||
next!(p, FlowEntry);
|
|
||||||
next!(p, Scalar(TScalarStyle::Plain, _));
|
|
||||||
next!(p, FlowEntry);
|
|
||||||
next!(p, Scalar(TScalarStyle::Plain, _));
|
|
||||||
next!(p, FlowSequenceEnd);
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_a_flow_mapping() {
|
|
||||||
let s = "
|
|
||||||
{
|
|
||||||
a simple key: a value, # Note that the KEY token is produced.
|
|
||||||
? a complex key: another value,
|
|
||||||
}
|
|
||||||
";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next!(p, FlowMappingStart);
|
|
||||||
next!(p, Key);
|
|
||||||
next!(p, Scalar(TScalarStyle::Plain, _));
|
|
||||||
next!(p, Value);
|
|
||||||
next!(p, Scalar(TScalarStyle::Plain, _));
|
|
||||||
next!(p, FlowEntry);
|
|
||||||
next!(p, Key);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "a complex key");
|
|
||||||
next!(p, Value);
|
|
||||||
next!(p, Scalar(TScalarStyle::Plain, _));
|
|
||||||
next!(p, FlowEntry);
|
|
||||||
next!(p, FlowMappingEnd);
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_block_sequences() {
|
|
||||||
let s = "
|
|
||||||
- item 1
|
|
||||||
- item 2
|
|
||||||
-
|
|
||||||
- item 3.1
|
|
||||||
- item 3.2
|
|
||||||
-
|
|
||||||
key 1: value 1
|
|
||||||
key 2: value 2
|
|
||||||
";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next!(p, BlockSequenceStart);
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "item 1");
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "item 2");
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next!(p, BlockSequenceStart);
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "item 3.1");
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "item 3.2");
|
|
||||||
next!(p, BlockEnd);
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next!(p, BlockMappingStart);
|
|
||||||
next!(p, Key);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "key 1");
|
|
||||||
next!(p, Value);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "value 1");
|
|
||||||
next!(p, Key);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "key 2");
|
|
||||||
next!(p, Value);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "value 2");
|
|
||||||
next!(p, BlockEnd);
|
|
||||||
next!(p, BlockEnd);
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_block_mappings() {
|
|
||||||
let s = "
|
|
||||||
a simple key: a value # The KEY token is produced here.
|
|
||||||
? a complex key
|
|
||||||
: another value
|
|
||||||
a mapping:
|
|
||||||
key 1: value 1
|
|
||||||
key 2: value 2
|
|
||||||
a sequence:
|
|
||||||
- item 1
|
|
||||||
- item 2
|
|
||||||
";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next!(p, BlockMappingStart);
|
|
||||||
next!(p, Key);
|
|
||||||
next!(p, Scalar(_, _));
|
|
||||||
next!(p, Value);
|
|
||||||
next!(p, Scalar(_, _));
|
|
||||||
next!(p, Key);
|
|
||||||
next!(p, Scalar(_, _));
|
|
||||||
next!(p, Value);
|
|
||||||
next!(p, Scalar(_, _));
|
|
||||||
next!(p, Key);
|
|
||||||
next!(p, Scalar(_, _));
|
|
||||||
next!(p, Value); // libyaml comment seems to be wrong
|
|
||||||
next!(p, BlockMappingStart);
|
|
||||||
next!(p, Key);
|
|
||||||
next!(p, Scalar(_, _));
|
|
||||||
next!(p, Value);
|
|
||||||
next!(p, Scalar(_, _));
|
|
||||||
next!(p, Key);
|
|
||||||
next!(p, Scalar(_, _));
|
|
||||||
next!(p, Value);
|
|
||||||
next!(p, Scalar(_, _));
|
|
||||||
next!(p, BlockEnd);
|
|
||||||
next!(p, Key);
|
|
||||||
next!(p, Scalar(_, _));
|
|
||||||
next!(p, Value);
|
|
||||||
next!(p, BlockSequenceStart);
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next!(p, Scalar(_, _));
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next!(p, Scalar(_, _));
|
|
||||||
next!(p, BlockEnd);
|
|
||||||
next!(p, BlockEnd);
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_no_block_sequence_start() {
|
|
||||||
let s = "
|
|
||||||
key:
|
|
||||||
- item 1
|
|
||||||
- item 2
|
|
||||||
";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next!(p, BlockMappingStart);
|
|
||||||
next!(p, Key);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "key");
|
|
||||||
next!(p, Value);
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "item 1");
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "item 2");
|
|
||||||
next!(p, BlockEnd);
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_collections_in_sequence() {
|
|
||||||
let s = "
|
|
||||||
- - item 1
|
|
||||||
- item 2
|
|
||||||
- key 1: value 1
|
|
||||||
key 2: value 2
|
|
||||||
- ? complex key
|
|
||||||
: complex value
|
|
||||||
";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next!(p, BlockSequenceStart);
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next!(p, BlockSequenceStart);
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "item 1");
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "item 2");
|
|
||||||
next!(p, BlockEnd);
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next!(p, BlockMappingStart);
|
|
||||||
next!(p, Key);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "key 1");
|
|
||||||
next!(p, Value);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "value 1");
|
|
||||||
next!(p, Key);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "key 2");
|
|
||||||
next!(p, Value);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "value 2");
|
|
||||||
next!(p, BlockEnd);
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next!(p, BlockMappingStart);
|
|
||||||
next!(p, Key);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "complex key");
|
|
||||||
next!(p, Value);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "complex value");
|
|
||||||
next!(p, BlockEnd);
|
|
||||||
next!(p, BlockEnd);
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_collections_in_mapping() {
|
|
||||||
let s = "
|
|
||||||
? a sequence
|
|
||||||
: - item 1
|
|
||||||
- item 2
|
|
||||||
? a mapping
|
|
||||||
: key 1: value 1
|
|
||||||
key 2: value 2
|
|
||||||
";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next!(p, BlockMappingStart);
|
|
||||||
next!(p, Key);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "a sequence");
|
|
||||||
next!(p, Value);
|
|
||||||
next!(p, BlockSequenceStart);
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "item 1");
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "item 2");
|
|
||||||
next!(p, BlockEnd);
|
|
||||||
next!(p, Key);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "a mapping");
|
|
||||||
next!(p, Value);
|
|
||||||
next!(p, BlockMappingStart);
|
|
||||||
next!(p, Key);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "key 1");
|
|
||||||
next!(p, Value);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "value 1");
|
|
||||||
next!(p, Key);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "key 2");
|
|
||||||
next!(p, Value);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "value 2");
|
|
||||||
next!(p, BlockEnd);
|
|
||||||
next!(p, BlockEnd);
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_spec_ex7_3() {
|
|
||||||
let s = "
|
|
||||||
{
|
|
||||||
? foo :,
|
|
||||||
: bar,
|
|
||||||
}
|
|
||||||
";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next!(p, FlowMappingStart);
|
|
||||||
next!(p, Key);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "foo");
|
|
||||||
next!(p, Value);
|
|
||||||
next!(p, FlowEntry);
|
|
||||||
next!(p, Value);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "bar");
|
|
||||||
next!(p, FlowEntry);
|
|
||||||
next!(p, FlowMappingEnd);
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_plain_scalar_starting_with_indicators_in_flow() {
|
|
||||||
// "Plain scalars must not begin with most indicators, as this would cause ambiguity with
|
|
||||||
// other YAML constructs. However, the “:”, “?” and “-” indicators may be used as the first
|
|
||||||
// character if followed by a non-space “safe” character, as this causes no ambiguity."
|
|
||||||
|
|
||||||
let s = "{a: :b}";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next!(p, FlowMappingStart);
|
|
||||||
next!(p, Key);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "a");
|
|
||||||
next!(p, Value);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, ":b");
|
|
||||||
next!(p, FlowMappingEnd);
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
|
|
||||||
let s = "{a: ?b}";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next!(p, FlowMappingStart);
|
|
||||||
next!(p, Key);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "a");
|
|
||||||
next!(p, Value);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "?b");
|
|
||||||
next!(p, FlowMappingEnd);
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_plain_scalar_starting_with_indicators_in_block() {
|
|
||||||
let s = ":a";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, ":a");
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
|
|
||||||
let s = "?a";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "?a");
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_plain_scalar_containing_indicators_in_block() {
|
|
||||||
let s = "a:,b";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "a:,b");
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
|
|
||||||
let s = ":,b";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, ":,b");
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_scanner_cr() {
|
|
||||||
let s = "---\r\n- tok1\r\n- tok2";
|
|
||||||
let mut p = Scanner::new(s.chars());
|
|
||||||
next!(p, StreamStart(..));
|
|
||||||
next!(p, DocumentStart);
|
|
||||||
next!(p, BlockSequenceStart);
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "tok1");
|
|
||||||
next!(p, BlockEntry);
|
|
||||||
next_scalar!(p, TScalarStyle::Plain, "tok2");
|
|
||||||
next!(p, BlockEnd);
|
|
||||||
next!(p, StreamEnd);
|
|
||||||
end!(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_uri() {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_uri_escapes() {
|
|
||||||
// TODO
|
|
||||||
}
|
|
|
@ -1,9 +1,8 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
#![allow(non_upper_case_globals)]
|
#![allow(non_upper_case_globals)]
|
||||||
extern crate yaml_rust2;
|
extern crate saphyr_parser;
|
||||||
|
|
||||||
use yaml_rust2::parser::{Event, EventReceiver, Parser};
|
use saphyr_parser::{Event, EventReceiver, Parser, TScalarStyle};
|
||||||
use yaml_rust2::scanner::TScalarStyle;
|
|
||||||
|
|
||||||
// These names match the names used in the C++ test suite.
|
// These names match the names used in the C++ test suite.
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::enum_variant_names))]
|
#[cfg_attr(feature = "cargo-clippy", allow(clippy::enum_variant_names))]
|
||||||
|
@ -68,67 +67,3 @@ macro_rules! assert_next {
|
||||||
// auto generated from handler_spec_test.cpp
|
// auto generated from handler_spec_test.cpp
|
||||||
include!("specexamples.rs.inc");
|
include!("specexamples.rs.inc");
|
||||||
include!("spec_test.rs.inc");
|
include!("spec_test.rs.inc");
|
||||||
|
|
||||||
// hand-crafted tests
|
|
||||||
//#[test]
|
|
||||||
//fn test_hc_alias() {
|
|
||||||
//}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mapvec_legal() {
|
|
||||||
use yaml_rust2::yaml::{Hash, Yaml};
|
|
||||||
use yaml_rust2::{YamlEmitter, YamlLoader};
|
|
||||||
|
|
||||||
// Emitting a `map<map<seq<_>>, _>` should result in legal yaml that
|
|
||||||
// we can parse.
|
|
||||||
|
|
||||||
let key = vec![Yaml::Integer(1), Yaml::Integer(2), Yaml::Integer(3)];
|
|
||||||
|
|
||||||
let mut keyhash = Hash::new();
|
|
||||||
keyhash.insert(Yaml::String("key".into()), Yaml::Array(key));
|
|
||||||
|
|
||||||
let val = vec![Yaml::Integer(4), Yaml::Integer(5), Yaml::Integer(6)];
|
|
||||||
|
|
||||||
let mut hash = Hash::new();
|
|
||||||
hash.insert(Yaml::Hash(keyhash), Yaml::Array(val));
|
|
||||||
|
|
||||||
let mut out_str = String::new();
|
|
||||||
{
|
|
||||||
let mut emitter = YamlEmitter::new(&mut out_str);
|
|
||||||
emitter.dump(&Yaml::Hash(hash)).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point, we are tempted to naively render like this:
|
|
||||||
//
|
|
||||||
// ```yaml
|
|
||||||
// ---
|
|
||||||
// {key:
|
|
||||||
// - 1
|
|
||||||
// - 2
|
|
||||||
// - 3}:
|
|
||||||
// - 4
|
|
||||||
// - 5
|
|
||||||
// - 6
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// However, this doesn't work, because the key sequence [1, 2, 3] is
|
|
||||||
// rendered in block mode, which is not legal (as far as I can tell)
|
|
||||||
// inside the flow mode of the key. We need to either fully render
|
|
||||||
// everything that's in a key in flow mode (which may make for some
|
|
||||||
// long lines), or use the explicit map identifier '?':
|
|
||||||
//
|
|
||||||
// ```yaml
|
|
||||||
// ---
|
|
||||||
// ?
|
|
||||||
// key:
|
|
||||||
// - 1
|
|
||||||
// - 2
|
|
||||||
// - 3
|
|
||||||
// :
|
|
||||||
// - 4
|
|
||||||
// - 5
|
|
||||||
// - 6
|
|
||||||
// ```
|
|
||||||
|
|
||||||
YamlLoader::load_from_str(&out_str).unwrap();
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
extern crate yaml_rust2;
|
|
||||||
|
|
||||||
use yaml_rust2::{Yaml, YamlEmitter, YamlLoader};
|
|
||||||
|
|
||||||
fn roundtrip(original: &Yaml) {
|
|
||||||
let mut emitted = String::new();
|
|
||||||
YamlEmitter::new(&mut emitted).dump(original).unwrap();
|
|
||||||
|
|
||||||
let documents = YamlLoader::load_from_str(&emitted).unwrap();
|
|
||||||
println!("emitted {emitted}");
|
|
||||||
|
|
||||||
assert_eq!(documents.len(), 1);
|
|
||||||
assert_eq!(documents[0], *original);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn double_roundtrip(original: &str) {
|
|
||||||
let parsed = YamlLoader::load_from_str(original).unwrap();
|
|
||||||
|
|
||||||
let mut serialized = String::new();
|
|
||||||
YamlEmitter::new(&mut serialized).dump(&parsed[0]).unwrap();
|
|
||||||
|
|
||||||
let reparsed = YamlLoader::load_from_str(&serialized).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(parsed, reparsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_escape_character() {
|
|
||||||
let y = Yaml::String("\x1b".to_owned());
|
|
||||||
roundtrip(&y);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_colon_in_string() {
|
|
||||||
let y = Yaml::String("x: %".to_owned());
|
|
||||||
roundtrip(&y);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_numberlike_strings() {
|
|
||||||
let docs = [
|
|
||||||
r#"x: "1234""#,
|
|
||||||
r#"x: "01234""#,
|
|
||||||
r#""1234""#,
|
|
||||||
r#""01234""#,
|
|
||||||
r#"" 01234""#,
|
|
||||||
r#""0x1234""#,
|
|
||||||
r#"" 0x1234""#,
|
|
||||||
];
|
|
||||||
|
|
||||||
for doc in &docs {
|
|
||||||
roundtrip(&Yaml::String((*doc).to_string()));
|
|
||||||
double_roundtrip(doc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Example from <https://github.com/chyh1990/yaml-rust/issues/133>
|
|
||||||
#[test]
|
|
||||||
fn test_issue133() {
|
|
||||||
let doc = YamlLoader::load_from_str("\"0x123\"")
|
|
||||||
.unwrap()
|
|
||||||
.pop()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(doc, Yaml::String("0x123".to_string()));
|
|
||||||
|
|
||||||
let mut out_str = String::new();
|
|
||||||
YamlEmitter::new(&mut out_str).dump(&doc).unwrap();
|
|
||||||
let doc2 = YamlLoader::load_from_str(&out_str).unwrap().pop().unwrap();
|
|
||||||
assert_eq!(doc, doc2); // This failed because the type has changed to a number now
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_newline() {
|
|
||||||
let y = Yaml::Array(vec![Yaml::String("\n".to_owned())]);
|
|
||||||
roundtrip(&y);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_crlf() {
|
|
||||||
let y = Yaml::Array(vec![Yaml::String("\r\n".to_owned())]);
|
|
||||||
roundtrip(&y);
|
|
||||||
}
|
|
|
@ -2,11 +2,7 @@ use std::fs::{self, DirEntry};
|
||||||
|
|
||||||
use libtest_mimic::{run_tests, Arguments, Outcome, Test};
|
use libtest_mimic::{run_tests, Arguments, Outcome, Test};
|
||||||
|
|
||||||
use yaml_rust2::{
|
use saphyr_parser::{Event, EventReceiver, Parser, ScanError, TScalarStyle, Tag};
|
||||||
parser::{Event, EventReceiver, Parser, Tag},
|
|
||||||
scanner::TScalarStyle,
|
|
||||||
yaml, ScanError, Yaml, YamlLoader,
|
|
||||||
};
|
|
||||||
|
|
||||||
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
||||||
|
|
||||||
|
@ -76,6 +72,8 @@ fn run_yaml_test(test: &Test<YamlTest>) -> Outcome {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_tests_from_file(entry: &DirEntry) -> Result<Vec<Test<YamlTest>>> {
|
fn load_tests_from_file(entry: &DirEntry) -> Result<Vec<Test<YamlTest>>> {
|
||||||
|
use yaml_rust2::{yaml, Yaml, YamlLoader};
|
||||||
|
|
||||||
let file_name = entry.file_name().to_string_lossy().to_string();
|
let file_name = entry.file_name().to_string_lossy().to_string();
|
||||||
let test_name = file_name
|
let test_name = file_name
|
||||||
.strip_suffix(".yaml")
|
.strip_suffix(".yaml")
|
||||||
|
@ -123,7 +121,15 @@ fn load_tests_from_file(entry: &DirEntry) -> Result<Vec<Test<YamlTest>>> {
|
||||||
|
|
||||||
fn parse_to_events(source: &str) -> Result<Vec<String>, ScanError> {
|
fn parse_to_events(source: &str) -> Result<Vec<String>, ScanError> {
|
||||||
let mut reporter = EventReporter::new();
|
let mut reporter = EventReporter::new();
|
||||||
Parser::new_from_str(source).load(&mut reporter, true)?;
|
for x in Parser::new_from_str(source) {
|
||||||
|
match x? {
|
||||||
|
(Event::StreamEnd, _) => {
|
||||||
|
reporter.on_event(Event::StreamEnd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(x, _) => reporter.on_event(x),
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(reporter.events)
|
Ok(reporter.events)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# `yaml-rust2` tools
|
# `saphyr-parser` tools
|
||||||
This directory contains tools that are used to develop the crate.
|
This directory contains tools that are used to develop the crate.
|
||||||
Due to dependency management, only some of them are available as binaries from the `yaml-rust2` crate.
|
Due to dependency management, only some of them are available as binaries from the `saphyr-parser` crate.
|
||||||
|
|
||||||
| Tool | Invocation |
|
| Tool | Invocation |
|
||||||
|------|------------|
|
|------|------------|
|
||||||
|
@ -14,7 +14,7 @@ Due to dependency management, only some of them are available as binaries from t
|
||||||
See the [dedicated README file](./bench_compare/README.md).
|
See the [dedicated README file](./bench_compare/README.md).
|
||||||
|
|
||||||
## `dump_events`
|
## `dump_events`
|
||||||
This is a debugging helper for the parser. It outputs events emitted by the parser for a given file. This can be paired with the `YAMLRUST2_DEBUG` environment variable to have an in-depth overview of which steps the scanner and the parser are taking.
|
This is a debugging helper for the parser. It outputs events emitted by the parser for a given file. This can be paired with the `SAPHYR_DEBUG` environment variable to have an in-depth overview of which steps the scanner and the parser are taking.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
Consider the following `input.yaml` YAML file:
|
Consider the following `input.yaml` YAML file:
|
||||||
|
@ -48,7 +48,7 @@ Running `cargo run --bin dump_events -- input.yaml` outputs:
|
||||||
↳ StreamEnd
|
↳ StreamEnd
|
||||||
```
|
```
|
||||||
|
|
||||||
Running `YAMLRUST2_DEBUG=1 cargo run --bin dump_events -- input.yaml` outputs much more details:
|
Running `SAPHYR_DEBUG=1 cargo run --bin dump_events -- input.yaml` outputs much more details:
|
||||||
<details>
|
<details>
|
||||||
<summary> Full output </summary>
|
<summary> Full output </summary>
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ The generated files are the following:
|
||||||
|
|
||||||
All generated files are meant to be between 200 and 250 MiB in size.
|
All generated files are meant to be between 200 and 250 MiB in size.
|
||||||
|
|
||||||
This tool depends on external dependencies that are not part of `yaml-rust2`'s dependencies or `dev-dependencies` and as such can't be called through `cargo run` directly. A dedicated `cargo gen_large_yaml` alias can be used to generate the benchmark files.
|
This tool depends on external dependencies that are not part of `saphyr-parser`'s dependencies or `dev-dependencies` and as such can't be called through `cargo run` directly. A dedicated `cargo gen_large_yaml` alias can be used to generate the benchmark files.
|
||||||
|
|
||||||
## `run_bench`
|
## `run_bench`
|
||||||
This is a benchmarking helper that runs the parser on the given file a given number of times and is able to extract simple metrics out of the results. The `--output-yaml` flag can be specified to make the output a YAML file that can be fed into other tools.
|
This is a benchmarking helper that runs the parser on the given file a given number of times and is able to extract simple metrics out of the results. The `--output-yaml` flag can be specified to make the output a YAML file that can be fed into other tools.
|
||||||
|
@ -192,7 +192,7 @@ Max: 1.633045284s
|
||||||
95%: 1.633045284s
|
95%: 1.633045284s
|
||||||
|
|
||||||
$> cargo run --release --bin run_bench -- bench_yaml/big.yaml 10 --output-yaml
|
$> cargo run --release --bin run_bench -- bench_yaml/big.yaml 10 --output-yaml
|
||||||
parser: yaml-rust2
|
parser: saphyr
|
||||||
input: bench_yaml/big.yaml
|
input: bench_yaml/big.yaml
|
||||||
average: 1649847674
|
average: 1649847674
|
||||||
min: 1648277149
|
min: 1648277149
|
||||||
|
|
|
@ -69,7 +69,7 @@ Max : 1.597028s
|
||||||
# This will be read by this tool.
|
# This will be read by this tool.
|
||||||
# This must output a YAML as described below.
|
# This must output a YAML as described below.
|
||||||
$> run_bench ../file.yaml 10 --output-yaml
|
$> run_bench ../file.yaml 10 --output-yaml
|
||||||
parser: yaml-rust2
|
parser: saphyr
|
||||||
input: ../file.yaml
|
input: ../file.yaml
|
||||||
average: 1620303590
|
average: 1620303590
|
||||||
min: 1611632108
|
min: 1611632108
|
||||||
|
@ -110,7 +110,7 @@ yaml_output_dir = "yaml_output" # The directory in which `run_bench`'s yamls are
|
||||||
csv_output = "benchmark.csv" # The CSV output aggregating times for each parser and file
|
csv_output = "benchmark.csv" # The CSV output aggregating times for each parser and file
|
||||||
|
|
||||||
[[parsers]] # A parser, can be repeated as many times as there are parsers
|
[[parsers]] # A parser, can be repeated as many times as there are parsers
|
||||||
name = "yaml-rust2" # The name of the parser (used for logging)
|
name = "saphyr" # The name of the parser (used for logging)
|
||||||
path = "target/release/" # The path in which the parsers' `run_bench` and `time_parse` are
|
path = "target/release/" # The path in which the parsers' `run_bench` and `time_parse` are
|
||||||
|
|
||||||
# If there is another parser, another block can be added
|
# If there is another parser, another block can be added
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use yaml_rust2::{
|
|
||||||
|
use saphyr_parser::{
|
||||||
parser::{MarkedEventReceiver, Parser},
|
parser::{MarkedEventReceiver, Parser},
|
||||||
scanner::Marker,
|
scanner::Marker,
|
||||||
Event,
|
Event,
|
||||||
|
|
|
@ -11,7 +11,6 @@ readme = "README.md"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
yaml-rust2 = { path = "../.." }
|
|
||||||
rand = { version = "0.8.5", features = [ "small_rng" ] }
|
rand = { version = "0.8.5", features = [ "small_rng" ] }
|
||||||
lipsum = "0.9.0"
|
lipsum = "0.9.0"
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#![allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
|
#![allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
|
||||||
|
|
||||||
use std::{env, fs::File, io::prelude::*};
|
use std::{env, fs::File, io::prelude::*};
|
||||||
use yaml_rust2::{
|
use saphyr_parser::{
|
||||||
parser::{MarkedEventReceiver, Parser},
|
parser::{MarkedEventReceiver, Parser},
|
||||||
scanner::Marker,
|
scanner::Marker,
|
||||||
Event,
|
Event,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use yaml_rust2::{
|
use saphyr_parser::{
|
||||||
parser::{MarkedEventReceiver, Parser},
|
parser::{MarkedEventReceiver, Parser},
|
||||||
scanner::Marker,
|
scanner::Marker,
|
||||||
Event,
|
Event,
|
||||||
|
|
Loading…
Reference in a new issue