deser_yaml/lib.rs
1//! Parse and write YAML compatible with deser.
2//!
3//! ```rust
4//! use deser::{Deserialize, Serialize};
5//!
6//! #[derive(Deserialize, Serialize, Debug)]
7//! struct Config {
8//! name: String,
9//! ports: Vec<u16>,
10//! }
11//!
12//! let config: Config = deser_yaml::from_str("
13//! name: web
14//! ports: [80, 443]
15//! ").unwrap();
16//! assert_eq!(config.name, "web");
17//! assert_eq!(config.ports, [80, 443]);
18//!
19//! assert_eq!(
20//! deser_yaml::to_string(&config).unwrap(),
21//! "name: web\nports:\n - 80\n - 443\n"
22//! );
23//! ```
24//!
25//! # Data Model
26//!
27//! The YAML parser passes the complete official
28//! [YAML test suite](https://github.com/yaml/yaml-test-suite). YAML maps
29//! onto the deser data model as follows:
30//!
31//! | YAML | deser |
32//! |---------------------------------------|-----------------------------------------|
33//! | `null`, `~`, empty | `Null` |
34//! | `true`, `false` | `Bool` |
35//! | integers | `U64`, `I64` (`u128` / `i128` if wider) |
36//! | floats | `F64` |
37//! | other scalars | `Str` |
38//! | `!!binary` | `Bytes` |
39//! | `!!timestamp` | [`Datetime`](deser_core::ext::Datetime) |
40//! | mappings and sequences | maps and sequences |
41//!
42//! Which plain (unquoted) scalars are null, booleans or numbers depends on
43//! the YAML version, see [`Version`]. As their type is inferred from
44//! their text, they are passed on as
45//! [`Implicit`](deser_core::Atom::Implicit) atoms: types that expect strings
46//! receive the text, all others the value. `version: 1.10` is `1.1` for an
47//! `f64` and `"1.10"` for a `String`, `~` is `None` for an `Option<String>`
48//! and `"~"` for a `String`, and keys like `200` work for maps with string
49//! keys. Quoted scalars are always strings and scalars with a standard tag
50//! (like `!!int 42`) are always of the type of their tag.
51//!
52//! ```rust
53//! #[derive(deser::Deserialize)]
54//! struct Package {
55//! version: String,
56//! port: u16,
57//! }
58//!
59//! let package: Package =
60//! deser_yaml::from_str("version: 1.10\nport: 0x1F").unwrap();
61//! assert_eq!(package.version, "1.10");
62//! assert_eq!(package.port, 31);
63//! ```
64//! The standard tags (`!!str`, `!!int`, `!!float`, `!!bool`, `!!null`,
65//! `!!binary`, `!!timestamp`, `!!seq` and `!!map`) determine the type of a
66//! value, all other tags are passed on out of band (see [Tags](#tags)).
67//! Timestamps are only recognized with an explicit `!!timestamp` tag. They
68//! are passed on as the well-known [`Datetime`](deser_core::ext::Datetime) type
69//! (a date or an offset date-time, timestamps without time zone are in UTC)
70//! which falls back to a string. Map keys can be of any
71//! type.
72//!
73//! Aliases are expanded: every alias produces the events of the node it
74//! refers to (see [`DeserializerConfig::set_alias_limit`]).
75//!
76//! # Serialization
77//!
78//! [`to_string`] writes values as block collections: sequences with `-`,
79//! mappings with `key: value`, empty collections as `{}` and `[]`. How the
80//! output looks can be configured with [`SerializerConfig`] (indentation,
81//! quoting, null, bytes, ...). Values are always written so that they read
82//! back as the same values, also by readers of YAML 1.1 (such as PyYAML)
83//! unless configured otherwise with [`SerializerConfig::set_compat`]:
84//!
85//! | deser | YAML |
86//! |-----------------------------------------|-----------------------------------------------|
87//! | `Null` | `null` (see [`NullStyle`]) |
88//! | `Bool`, integers | `true`, `false`, `42` |
89//! | `F32`, `F64` | `1.5`, `1.0e+20`, `.inf`, `.nan` (the shortest text for the precision) |
90//! | `Str` | plain if possible, otherwise quoted (see [`QuoteStyle`]), with line breaks as literal block scalar (see [`MultilineStyle`]) |
91//! | [`Implicit`](deser_core::Atom::Implicit) | its text if readers read it as the same value (`1.10`, `0x1F`, `~`), otherwise the value |
92//! | `Bytes` | `!!binary` (see [`SerializerConfig::set_binary`]) |
93//! | [`Datetime`](deser_core::ext::Datetime) | timestamp (see [`SerializerConfig::set_timestamp_tag`]) |
94//! | maps and sequences | block mappings and sequences, flow style (`[a, b]`, `{a: 1}`) if compact (see [`FlowPolicy`]), keys that are collections or long use `? key` |
95//!
96//! The style of individual values can be requested with hints: the
97//! well-known [`Layout`](deser_core::hints::Layout) for collections (flow or
98//! block) and [`ScalarStyle`](style::ScalarStyle) for strings (see
99//! [`style`]). Values set them with adapters, layers can set them for
100//! instance by path. Hints are preferences: a value is written in another
101//! style if the requested one cannot represent it. When reading, flow
102//! collections are reported as compact so that they stay flow collections
103//! through a [`Recording`](deser_core::de::Recording). Plain scalars keep
104//! their text the same way, `version: 1.10` read into a `deser_value::Value` or a
105//! recording is written as `version: 1.10` again.
106//!
107//! Tags are written with [`Tagged`] or [`set_tag`] (see [Tags](#tags)). Streams of
108//! multiple documents are written with [`Serializer`].
109//!
110//! # Tags
111//!
112//! Tags are not part of the deser data model. The standard tags determine
113//! the type of a value (see [Data Model](#data-model)), all
114//! other tags are exchanged out of band through the
115//! [`State`](deser_core::State):
116//!
117//! * When deserializing, the tag of a node is published into the state for
118//! the first event of the node (the atom or the start of the map or
119//! sequence). Types can pick it up with [`take_tag`].
120//! Types which do not care about tags never see them, which means that
121//! unknown tags are transparent: the value of `!color red` is the string
122//! `red`.
123//! * When serializing, [`set_tag`] registers the tag of a value in the state
124//! and the serializer writes it in front of the node.
125//! * [`Tagged`] captures the tag of a value and writes it.
126//!
127//! Both directions use the same event data, so values which capture event
128//! data (such as [`Recording`](deser_core::de::Recording)) keep the tags.
129//!
130//! Tags are reported fully resolved: `!foo` stays `!foo` but `!!set`
131//! becomes `tag:yaml.org,2002:set` and tag handles declared with `%TAG`
132//! directives are expanded.
133//!
134//! # Documents
135//!
136//! A YAML stream can contain multiple documents. [`from_str`] expects at
137//! most one document, [`Deserializer`] can read them one by one:
138//!
139//! ```rust
140//! let mut de = deser_yaml::Deserializer::from_str("--- a\n--- b\n");
141//! let docs = de.iter::<String>().collect::<Result<Vec<_>, _>>().unwrap();
142//! assert_eq!(docs, ["a", "b"]);
143//! ```
144//!
145//! # Streams
146//!
147//! Values are read from a [`Read`](std::io::Read) with [`from_reader`]
148//! and written to a [`Write`](std::io::Write) with [`to_writer`]. To read
149//! or write streams of documents the configurations create readers and
150//! writers of [`deser::io`](deser_core::io) ([`DeserializerConfig::reader`]
151//! and [`SerializerConfig::writer`]). The reader only buffers until a
152//! document is complete:
153//!
154//! ```rust
155//! # #[cfg(feature = "io")] {
156//! use deser_yaml::{DeserializerConfig, SerializerConfig};
157//!
158//! const ENDED: SerializerConfig =
159//! SerializerConfig::builder().end_documents(true).build();
160//! let mut writer = ENDED.writer(Vec::new());
161//! writer.write(&vec![1, 2]).unwrap();
162//! writer.write(&"done").unwrap();
163//! let output = writer.into_inner();
164//! assert_eq!(output, b"- 1\n- 2\n...\n---\ndone\n...\n");
165//!
166//! let mut reader = DeserializerConfig::new().reader(&output[..]);
167//! assert_eq!(reader.read::<Vec<u32>>().unwrap(), Some(vec![1, 2]));
168//! assert_eq!(reader.read::<String>().unwrap().as_deref(), Some("done"));
169//! assert_eq!(reader.read::<String>().unwrap(), None);
170//! # }
171//! ```
172//!
173//! The stream serializer ([`Serializer`]) and the stream deserializer
174//! ([`StreamDeserializer`]) do not do IO themselves (see
175//! [`deser::stream`](deser_core::stream)), they also work with other kinds
176//! of IO (for instance async runtimes with `deser-tokio`).
177//!
178//! # Features
179//!
180//! * `io` (enabled by default): reading and writing streams of the
181//! standard library, see [streams](#streams).
182//! * `speedups` (enabled by default): validates UTF-8 with
183//! [`simdutf8`](https://docs.rs/simdutf8).
184#![doc(html_logo_url = "https://raw.githubusercontent.com/mitsuhiko/deser/main/artwork/logo.svg")]
185
186// `copy.rs` is shared with other formats which only need `alloc`
187extern crate alloc;
188
189mod copy;
190mod de;
191mod emit;
192mod event;
193mod parser;
194mod quote;
195mod resolve;
196mod scanner;
197mod ser;
198mod stream;
199pub mod style;
200mod tag;
201
202pub use self::de::{
203 Deserializer, DeserializerConfig, DeserializerConfigBuilder, Iter, from_slice, from_str,
204};
205pub use self::resolve::Version;
206#[cfg(feature = "io")]
207pub use self::ser::to_writer;
208pub use self::ser::{
209 FlowPolicy, Indent, MultilineStyle, NullStyle, QuoteStyle, Serializer, SerializerConfig,
210 SerializerConfigBuilder, to_string,
211};
212pub use self::stream::StreamDeserializer;
213#[cfg(feature = "io")]
214pub use self::stream::from_reader;
215pub use self::tag::{Tagged, set_tag, take_tag};
216
217#[doc(hidden)]
218#[path = "private.rs"]
219pub mod __private;