Skip to main content

Crate deser_yaml

Crate deser_yaml 

Source
Expand description

Parse and write YAML compatible with deser.

use deser::{Deserialize, Serialize};

#[derive(Deserialize, Serialize, Debug)]
struct Config {
    name: String,
    ports: Vec<u16>,
}

let config: Config = deser_yaml::from_str("
name: web
ports: [80, 443]
").unwrap();
assert_eq!(config.name, "web");
assert_eq!(config.ports, [80, 443]);

assert_eq!(
    deser_yaml::to_string(&config).unwrap(),
    "name: web\nports:\n  - 80\n  - 443\n"
);

§Data Model

The YAML parser passes the complete official YAML test suite. YAML maps onto the deser data model as follows:

YAMLdeser
null, ~, emptyNull
true, falseBool
integersU64, I64 (u128 / i128 if wider)
floatsF64
other scalarsStr
!!binaryBytes
!!timestampDatetime
mappings and sequencesmaps and sequences

Which plain (unquoted) scalars are null, booleans or numbers depends on the YAML version, see Version. As their type is inferred from their text, they are passed on as Implicit atoms: types that expect strings receive the text, all others the value. version: 1.10 is 1.1 for an f64 and "1.10" for a String, ~ is None for an Option<String> and "~" for a String, and keys like 200 work for maps with string keys. Quoted scalars are always strings and scalars with a standard tag (like !!int 42) are always of the type of their tag.

#[derive(deser::Deserialize)]
struct Package {
    version: String,
    port: u16,
}

let package: Package =
    deser_yaml::from_str("version: 1.10\nport: 0x1F").unwrap();
assert_eq!(package.version, "1.10");
assert_eq!(package.port, 31);

The standard tags (!!str, !!int, !!float, !!bool, !!null, !!binary, !!timestamp, !!seq and !!map) determine the type of a value, all other tags are passed on out of band (see Tags). Timestamps are only recognized with an explicit !!timestamp tag. They are passed on as the well-known Datetime type (a date or an offset date-time, timestamps without time zone are in UTC) which falls back to a string. Map keys can be of any type.

Aliases are expanded: every alias produces the events of the node it refers to (see DeserializerConfig::set_alias_limit).

§Serialization

to_string writes values as block collections: sequences with -, mappings with key: value, empty collections as {} and []. How the output looks can be configured with SerializerConfig (indentation, quoting, null, bytes, …). Values are always written so that they read back as the same values, also by readers of YAML 1.1 (such as PyYAML) unless configured otherwise with SerializerConfig::set_compat:

deserYAML
Nullnull (see NullStyle)
Bool, integerstrue, false, 42
F32, F641.5, 1.0e+20, .inf, .nan (the shortest text for the precision)
Strplain if possible, otherwise quoted (see QuoteStyle), with line breaks as literal block scalar (see MultilineStyle)
Implicitits text if readers read it as the same value (1.10, 0x1F, ~), otherwise the value
Bytes!!binary (see SerializerConfig::set_binary)
Datetimetimestamp (see SerializerConfig::set_timestamp_tag)
maps and sequencesblock mappings and sequences, flow style ([a, b], {a: 1}) if compact (see FlowPolicy), keys that are collections or long use ? key

The style of individual values can be requested with hints: the well-known Layout for collections (flow or block) and ScalarStyle for strings (see style). Values set them with adapters, layers can set them for instance by path. Hints are preferences: a value is written in another style if the requested one cannot represent it. When reading, flow collections are reported as compact so that they stay flow collections through a Recording. Plain scalars keep their text the same way, version: 1.10 read into a deser_value::Value or a recording is written as version: 1.10 again.

Tags are written with Tagged or set_tag (see Tags). Streams of multiple documents are written with Serializer.

§Tags

Tags are not part of the deser data model. The standard tags determine the type of a value (see Data Model), all other tags are exchanged out of band through the State:

  • When deserializing, the tag of a node is published into the state for the first event of the node (the atom or the start of the map or sequence). Types can pick it up with take_tag. Types which do not care about tags never see them, which means that unknown tags are transparent: the value of !color red is the string red.
  • When serializing, set_tag registers the tag of a value in the state and the serializer writes it in front of the node.
  • Tagged captures the tag of a value and writes it.

Both directions use the same event data, so values which capture event data (such as Recording) keep the tags.

Tags are reported fully resolved: !foo stays !foo but !!set becomes tag:yaml.org,2002:set and tag handles declared with %TAG directives are expanded.

§Documents

A YAML stream can contain multiple documents. from_str expects at most one document, Deserializer can read them one by one:

let mut de = deser_yaml::Deserializer::from_str("--- a\n--- b\n");
let docs = de.iter::<String>().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(docs, ["a", "b"]);

§Streams

Values are read from a Read with from_reader and written to a Write with to_writer. To read or write streams of documents the configurations create readers and writers of deser::io (DeserializerConfig::reader and SerializerConfig::writer). The reader only buffers until a document is complete:

use deser_yaml::{DeserializerConfig, SerializerConfig};

const ENDED: SerializerConfig =
    SerializerConfig::builder().end_documents(true).build();
let mut writer = ENDED.writer(Vec::new());
writer.write(&vec![1, 2]).unwrap();
writer.write(&"done").unwrap();
let output = writer.into_inner();
assert_eq!(output, b"- 1\n- 2\n...\n---\ndone\n...\n");

let mut reader = DeserializerConfig::new().reader(&output[..]);
assert_eq!(reader.read::<Vec<u32>>().unwrap(), Some(vec![1, 2]));
assert_eq!(reader.read::<String>().unwrap().as_deref(), Some("done"));
assert_eq!(reader.read::<String>().unwrap(), None);

The stream serializer (Serializer) and the stream deserializer (StreamDeserializer) do not do IO themselves (see deser::stream), they also work with other kinds of IO (for instance async runtimes with deser-tokio).

§Features

  • io (enabled by default): reading and writing streams of the standard library, see streams.
  • speedups (enabled by default): validates UTF-8 with simdutf8.

Modules§

style
Hints for the style of YAML scalars.

Structs§

Deserializer
Deserializes YAML.
DeserializerConfig
Configures how YAML is deserialized.
DeserializerConfigBuilder
Builds a DeserializerConfig.
Iter
An iterator over the documents of a YAML stream.
Serializer
Serializes values into YAML documents.
SerializerConfig
Configures how values are serialized to YAML.
SerializerConfigBuilder
Builds a SerializerConfig.
StreamDeserializer
Reads a stream of YAML documents (see deser::stream).
Tagged
A value with an optional YAML tag.

Enums§

FlowPolicy
When collections are written in flow style ([a, b], {a: 1}).
Indent
How the output is indented.
MultilineStyle
How strings with line breaks are written.
NullStyle
How null is written.
QuoteStyle
How strings are quoted when they cannot be written plain.
Version
The YAML version that determines how plain scalars are resolved.

Functions§

from_reader
Deserializes a value from a reader.
from_slice
Deserializes a value from YAML in a byte slice.
from_str
Deserializes a value from YAML.
set_tag
Sets the tag of the node that is serialized.
take_tag
Takes the tag of the current node from the state.
to_string
Serializes a value to YAML.
to_writer
Serializes a value to a writer.