#![forbid(unsafe_code)]
#![allow(deprecated)]
#[cfg(not(any(feature = "serialize", feature = "deserialize")))]
compile_error!(
"Invalid feature configuration: enable at least one of \
\"serialize\" or \"deserialize\"."
);
pub use anchors::{
ArcAnchor, ArcRecursion, ArcRecursive, ArcWeakAnchor, RcAnchor, RcRecursion, RcRecursive,
RcWeakAnchor,
};
#[cfg(any(feature = "serialize", feature = "deserialize"))]
pub use wrappers::{Commented, FlowMap, FlowSeq, SpaceAfter};
#[cfg(feature = "deserialize")]
pub use self::{
de::{budget, localizer, options, Budget, DuplicateKeyPolicy, Error, Options},
de_error::{
CroppedRegion, MessageFormatter, RenderOptions, SnippetMode, TransformReason,
UserMessageFormatter,
},
indentation::RequireIndent,
input_source::{
IncludeRequest, IncludeResolveError, IncludeResolver, InputSource, ResolveProblem,
ResolvedInclude,
},
localizer::{
DEFAULT_ENGLISH_LOCALIZER, DefaultEnglishLocalizer, ExternalMessage,
ExternalMessageSource, Localizer,
},
message_formatters::{DefaultMessageFormatter, DeveloperMessageFormatter},
};
pub use location::{Location, Locations, Span};
pub use long_strings::{FoldStr, FoldString, LitStr, LitString};
#[cfg(feature = "figment")]
pub use de::figment;
#[cfg(feature = "miette")]
pub use de::miette;
#[cfg(any(feature = "garde", feature = "validator"))]
pub use de::path_map;
#[cfg(feature = "properties")]
pub use de::properties;
#[cfg(feature = "robotics")]
pub use de::robotics;
#[cfg(all(feature = "deserialize", feature = "include_fs"))]
pub use de::safe_resolver::{SafeFileReadMode, SafeFileResolver, SymlinkPolicy};
#[cfg(feature = "serialize")]
pub use self::{ser::{error as ser_error, options::SerializerOptions}};
pub use spanned::Spanned;
#[cfg(all(feature = "deserialize", feature = "include"))]
pub(crate) fn resolver_from_options<'a>(
options: Options,
) -> Option<Box<crate::input_source::IncludeResolver<'a>>> {
options.include_resolver.clone().map(|rc_refcell| {
Box::new(move |req: crate::input_source::IncludeRequest<'_>| rc_refcell.borrow_mut()(req))
as Box<crate::input_source::IncludeResolver<'a>>
})
}
#[cfg(feature = "deserialize")]
use crate::budget::EnforcingPolicy;
#[cfg(feature = "deserialize")]
use crate::de::{Ev, Events};
#[cfg(feature = "deserialize")]
use crate::live_events::LiveEvents;
#[cfg(feature = "deserialize")]
use crate::parse_scalars::scalar_is_nullish;
#[cfg(feature = "deserialize")]
use crate::properties_redaction::with_interp_redaction_scope;
#[cfg(feature = "deserialize")]
use serde::de::DeserializeOwned;
#[cfg(feature = "deserialize")]
use std::io::Read;
#[cfg(feature = "deserialize")]
#[path = "de/anchor_store.rs"]
mod anchor_store;
mod anchors;
#[cfg(feature = "deserialize")]
#[path = "de/mod.rs"]
mod de;
mod long_strings;
#[cfg(any(feature = "serialize", feature = "deserialize"))]
mod wrappers;
#[path = "de/parse_scalars.rs"]
mod parse_scalars;
#[cfg(feature = "serialize")]
#[path = "ser/mod.rs"]
pub mod ser;
mod spanned;
#[cfg(feature = "deserialize")]
pub(crate) use de::{
buffered_input, error as de_error, include, indentation, input_source, live_events,
message_formatters, properties_redaction, ring_reader, snippet as de_snippet, tags,
};
#[cfg(all(feature = "deserialize", feature = "include"))]
pub(crate) use de::include_stack;
#[cfg(any(feature = "garde", feature = "validator"))]
use de::lib_validate;
#[cfg(feature = "deserialize")]
pub use de::YamlDeserializer as Deserializer;
#[cfg(any(feature = "garde", feature = "validator"))]
pub use lib_validate::*;
#[cfg(feature = "serialize")]
pub use ser::YamlSerializer as Serializer;
#[cfg(feature = "deserialize")]
pub use de::{
with_deserializer_from_reader, with_deserializer_from_reader_with_options,
with_deserializer_from_slice, with_deserializer_from_slice_with_options,
with_deserializer_from_str, with_deserializer_from_str_with_options,
};
mod macros;
#[path = "de/location.rs"]
mod location;
#[cfg(feature = "serialize")]
pub fn to_string<T: serde::Serialize>(value: &T) -> std::result::Result<String, crate::ser::Error> {
let mut out = String::new();
to_fmt_writer(&mut out, value)?;
Ok(out)
}
#[cfg(feature = "serialize")]
pub fn to_string_with_options<T: serde::Serialize>(
value: &T,
options: SerializerOptions,
) -> std::result::Result<String, crate::ser::Error> {
let mut out = String::new();
to_fmt_writer_with_options(&mut out, value, options)?;
Ok(out)
}
#[deprecated(
since = "0.0.7",
note = "Use `to_fmt_writer` for `fmt::Write` (String, fmt::Formatter) or `to_io_writer` for files/sockets."
)]
#[cfg(feature = "serialize")]
pub fn to_writer<W: std::fmt::Write, T: serde::Serialize>(
output: &mut W,
value: &T,
) -> std::result::Result<(), crate::ser::Error> {
let mut ser = crate::ser::YamlSerializer::new(output);
value.serialize(&mut ser)
}
#[cfg(feature = "serialize")]
pub fn to_fmt_writer<W: std::fmt::Write, T: serde::Serialize>(
output: &mut W,
value: &T,
) -> std::result::Result<(), crate::ser::Error> {
to_fmt_writer_with_options(output, value, SerializerOptions::default())
}
#[cfg(feature = "serialize")]
pub fn to_io_writer<W: std::io::Write, T: serde::Serialize>(
output: &mut W,
value: &T,
) -> std::result::Result<(), crate::ser::Error> {
to_io_writer_with_options(output, value, SerializerOptions::default())
}
#[cfg(feature = "serialize")]
pub fn to_fmt_writer_with_options<W: std::fmt::Write, T: serde::Serialize>(
output: &mut W,
value: &T,
mut options: SerializerOptions,
) -> std::result::Result<(), crate::ser::Error> {
options.consistent()?;
let mut ser = crate::ser::YamlSerializer::with_options(output, &mut options);
value.serialize(&mut ser)
}
#[cfg(feature = "serialize")]
pub fn to_io_writer_with_options<W: std::io::Write, T: serde::Serialize>(
output: &mut W,
value: &T,
mut options: SerializerOptions,
) -> std::result::Result<(), crate::ser::Error> {
options.consistent()?;
struct Adapter<'a, W: std::io::Write> {
output: &'a mut W,
last_err: Option<std::io::Error>,
}
impl<'a, W: std::io::Write> std::fmt::Write for Adapter<'a, W> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
if let Err(e) = self.output.write_all(s.as_bytes()) {
self.last_err = Some(e);
return Err(std::fmt::Error);
}
Ok(())
}
fn write_char(&mut self, c: char) -> std::fmt::Result {
let mut buf = [0u8; 4];
let s = c.encode_utf8(&mut buf);
self.write_str(s)
}
}
let mut adapter = Adapter {
output,
last_err: None,
};
let mut ser = crate::ser::YamlSerializer::with_options(&mut adapter, &mut options);
match value.serialize(&mut ser) {
Ok(()) => Ok(()),
Err(e) => {
if let Some(io_error) = adapter.last_err.take() {
return Err(crate::ser::Error::from(io_error));
}
Err(e)
}
}
}
#[deprecated(
since = "0.0.7",
note = "Use `to_fmt_writer_with_options` for fmt::Write or `to_io_writer_with_options` for io::Write."
)]
#[cfg(feature = "serialize")]
pub fn to_writer_with_options<W: std::fmt::Write, T: serde::Serialize>(
output: &mut W,
value: &T,
options: SerializerOptions,
) -> std::result::Result<(), crate::ser::Error> {
to_fmt_writer_with_options(output, value, options)
}
#[cfg(feature = "deserialize")]
pub fn from_str<'de, T>(input: &'de str) -> Result<T, Error>
where
T: serde::de::Deserialize<'de>,
{
from_str_with_options(input, Options::default())
}
#[allow(deprecated)]
#[cfg(feature = "deserialize")]
fn from_str_with_options_impl<'de, T>(input: &'de str, options: Options) -> Result<T, Error>
where
T: serde::de::Deserialize<'de>,
{
let input = if let Some(rest) = input.strip_prefix('\u{FEFF}') {
rest
} else {
input
};
let with_snippet = options.with_snippet;
let crop_radius = options.crop_radius;
let cfg = crate::de::Cfg::from_options(&options);
let mut src = LiveEvents::from_str(input, options, false);
let value_res = crate::anchor_store::with_document_scope(|| {
with_interp_redaction_scope(|| {
crate::de::with_root_redaction(crate::de::YamlDeserializer::new(&mut src, cfg), |de| {
T::deserialize(de)
})
})
});
let value = match value_res {
Ok(v) => v,
Err(e) => {
if src.synthesized_null_emitted() {
let err = Error::eof().with_location(src.last_location());
return Err(maybe_with_snippet_from_events(
err,
input,
&src,
with_snippet,
crop_radius,
));
} else {
return Err(maybe_with_snippet_from_events(
e,
input,
&src,
with_snippet,
crop_radius,
));
}
}
};
match src.peek() {
Ok(Some(_)) => {
let err = Error::multiple_documents("use from_multiple or from_multiple_with_options")
.with_location(src.last_location());
return Err(maybe_with_snippet_from_events(
err,
input,
&src,
with_snippet,
crop_radius,
));
}
Ok(None) => {}
Err(e) => {
if src.seen_doc_end() {
} else {
return Err(maybe_with_snippet_from_events(
e,
input,
&src,
with_snippet,
crop_radius,
));
}
}
}
if let Err(e) = src.finish() {
return Err(maybe_with_snippet_from_events(
e,
input,
&src,
with_snippet,
crop_radius,
));
}
Ok(value)
}
#[allow(deprecated)]
#[cfg(feature = "deserialize")]
pub fn from_str_with_options<'de, T>(input: &'de str, options: Options) -> Result<T, Error>
where
T: serde::de::Deserialize<'de>,
{
from_str_with_options_impl(input, options)
}
#[cfg(feature = "deserialize")]
pub(crate) fn maybe_with_snippet(
err: Error,
input: &str,
with_snippet: bool,
crop_radius: usize,
) -> Error {
if !(with_snippet && crop_radius > 0 && err.location().is_some()) {
return err;
}
err.with_snippet(input, crop_radius)
}
#[cfg(feature = "deserialize")]
pub(crate) struct RootFragment<'a> {
pub text: &'a str,
pub start_line: usize,
pub source_name: &'a str,
}
#[cfg(feature = "deserialize")]
struct ReaderSnippetContext<R> {
shared_ring: ring_reader::SharedRingReader<R>,
with_snippet: bool,
crop_radius: usize,
}
#[cfg(feature = "deserialize")]
impl<R: Read> ReaderSnippetContext<R> {
fn new(
reader: R,
with_snippet: bool,
crop_radius: usize,
) -> (Self, ring_reader::SharedRingReaderHandle<R>) {
let shared_ring = ring_reader::SharedRingReader::new(reader);
let ring_handle = ring_reader::SharedRingReaderHandle::new(&shared_ring);
(
Self {
shared_ring,
with_snippet,
crop_radius,
},
ring_handle,
)
}
fn attach_snippet(&self, err: Error, src: &LiveEvents<'_>) -> Error {
if !self.with_snippet || self.crop_radius == 0 {
return err;
}
match self.shared_ring.get_recent() {
Ok(snapshot) => {
let text = String::from_utf8_lossy(&snapshot.bytes);
let root = RootFragment {
text: text.as_ref(),
start_line: snapshot.start_line,
source_name: "input",
};
maybe_with_snippet_from_events_and_root_fragment(
err,
Some(&root),
text.as_ref(),
src,
self.with_snippet,
self.crop_radius,
)
}
Err(_) => err,
}
}
}
#[cfg(all(feature = "deserialize", feature = "include"))]
fn with_root_additional_snippet(
err: Error,
root: Option<&RootFragment<'_>>,
input: &str,
location: &crate::Location,
crop_radius: usize,
) -> Error {
match root {
Some(root) => err.with_additional_snippet_offset_named(
root.text,
root.start_line,
root.source_name,
location,
crop_radius,
),
None => err.with_additional_snippet_named(input, "input", location, crop_radius),
}
}
#[cfg(all(feature = "deserialize", feature = "include"))]
fn recorded_source_snippet_chain<'a>(
events: &'a crate::live_events::LiveEvents<'_>,
location: &crate::Location,
) -> Option<Vec<&'a crate::include_stack::RecordedSource>> {
let chain = events.recorded_source_chain(location.source_id());
chain.first()?.text.as_deref()?;
Some(chain)
}
#[cfg(all(feature = "deserialize", feature = "include"))]
fn with_recorded_source_snippets(
err: Error,
root: Option<&RootFragment<'_>>,
input: &str,
chain: &[&crate::include_stack::RecordedSource],
crop_radius: usize,
) -> Error {
let current = chain[0];
let source_text = current
.text
.as_deref()
.expect("recorded source snippet chain must start with text-backed source");
let mut err_with_snippet =
err.with_snippet_named(source_text, current.name.as_str(), crop_radius);
for window in chain.windows(2) {
let child = window[0];
let parent = window[1];
if child.include_location == crate::Location::UNKNOWN {
continue;
}
match parent.text.as_deref() {
Some(parent_text) => {
err_with_snippet = err_with_snippet.with_additional_snippet_named(
parent_text,
parent.name.as_str(),
&child.include_location,
crop_radius,
);
}
None if parent.parent_source_id.is_none() => {
err_with_snippet = with_root_additional_snippet(
err_with_snippet,
root,
input,
&child.include_location,
crop_radius,
);
}
None => {}
}
}
err_with_snippet
}
#[cfg(feature = "deserialize")]
pub(crate) fn maybe_with_snippet_from_events_and_root_fragment(
err: Error,
root: Option<&RootFragment<'_>>,
input: &str,
#[allow(unused_variables)] events: &crate::live_events::LiveEvents<'_>,
with_snippet: bool,
crop_radius: usize,
) -> Error {
if !(with_snippet && crop_radius > 0 && err.location().is_some()) {
return err;
}
#[cfg(feature = "include")]
if let Some(loc) = err.location()
&& let Some(chain) = recorded_source_snippet_chain(events, &loc)
{
return with_recorded_source_snippets(err, root, input, &chain, crop_radius);
}
match root {
Some(root) => {
err.with_snippet_offset_named(root.text, root.start_line, root.source_name, crop_radius)
}
None => maybe_with_snippet(err, input, with_snippet, crop_radius),
}
}
#[cfg(feature = "deserialize")]
pub(crate) fn maybe_with_snippet_from_events(
err: Error,
input: &str,
#[allow(unused_variables)] events: &crate::live_events::LiveEvents<'_>,
with_snippet: bool,
crop_radius: usize,
) -> Error {
maybe_with_snippet_from_events_and_root_fragment(
err,
None,
input,
events,
with_snippet,
crop_radius,
)
}
#[cfg(feature = "deserialize")]
pub fn from_multiple<T: DeserializeOwned>(input: &str) -> Result<Vec<T>, Error> {
from_multiple_with_options(input, Options::default())
}
#[allow(deprecated)]
#[cfg(feature = "deserialize")]
pub fn from_multiple_with_options<T: DeserializeOwned>(
input: &str,
options: Options,
) -> Result<Vec<T>, Error> {
let input = if let Some(rest) = input.strip_prefix('\u{FEFF}') {
rest
} else {
input
};
let with_snippet = options.with_snippet;
let crop_radius = options.crop_radius;
let cfg = crate::de::Cfg::from_options(&options);
let mut src = LiveEvents::from_str(input, options, false);
let mut values = Vec::new();
loop {
match src.peek()? {
Some(Ev::Scalar {
value: s,
style,
tag,
..
}) if *tag == crate::tags::SfTag::Null
|| (*tag != crate::tags::SfTag::String && scalar_is_nullish(s, style)) =>
{
let _ = src.next()?; continue;
}
Some(_) => {
let value_res = crate::anchor_store::with_document_scope(|| {
with_interp_redaction_scope(|| {
crate::de::with_root_redaction(
crate::de::YamlDeserializer::new(&mut src, cfg),
|de| T::deserialize(de),
)
})
});
let value = match value_res {
Ok(v) => v,
Err(e) => {
return Err(maybe_with_snippet_from_events(
e,
input,
&src,
with_snippet,
crop_radius,
));
}
};
values.push(value);
}
None => break,
}
}
if let Err(e) = src.finish() {
return Err(maybe_with_snippet_from_events(
e,
input,
&src,
with_snippet,
crop_radius,
));
}
Ok(values)
}
#[cfg(feature = "deserialize")]
pub fn from_slice<T: DeserializeOwned>(bytes: &[u8]) -> Result<T, Error> {
from_slice_with_options(bytes, Options::default())
}
#[cfg(feature = "deserialize")]
pub fn from_slice_with_options<'de, T>(bytes: &'de [u8], options: Options) -> Result<T, Error>
where
T: serde::Deserialize<'de>,
{
let s = std::str::from_utf8(bytes).map_err(|_| Error::InvalidUtf8Input)?;
from_str_with_options(s, options)
}
#[cfg(feature = "deserialize")]
pub fn from_slice_multiple<T: DeserializeOwned>(bytes: &[u8]) -> Result<Vec<T>, Error> {
from_slice_multiple_with_options(bytes, Options::default())
}
#[cfg(feature = "deserialize")]
pub fn from_slice_multiple_with_options<T: DeserializeOwned>(
bytes: &[u8],
options: Options,
) -> Result<Vec<T>, Error> {
let s = std::str::from_utf8(bytes).map_err(|_| Error::InvalidUtf8Input)?;
from_multiple_with_options(s, options)
}
#[cfg(feature = "serialize")]
pub fn to_string_multiple<T: serde::Serialize>(
values: &[T],
) -> std::result::Result<String, crate::ser::Error> {
to_string_multiple_with_options(values, SerializerOptions::default())
}
#[cfg(feature = "serialize")]
pub fn to_string_multiple_with_options<T: serde::Serialize>(
values: &[T],
options: SerializerOptions,
) -> std::result::Result<String, crate::ser::Error> {
let mut out = String::new();
let mut first = true;
for v in values {
if !first {
out.push_str("---\n");
}
first = false;
to_fmt_writer_with_options(&mut out, v, options)?;
}
Ok(out)
}
#[cfg(feature = "deserialize")]
pub fn from_reader<'a, R: std::io::Read + 'a, T: DeserializeOwned>(reader: R) -> Result<T, Error> {
from_reader_with_options(reader, Options::default())
}
#[allow(deprecated)]
#[cfg(feature = "deserialize")]
pub fn from_reader_with_options<'a, R: std::io::Read + 'a, T: DeserializeOwned>(
reader: R,
options: Options,
) -> Result<T, Error> {
let cfg = crate::de::Cfg::from_options(&options);
let (snippet_ctx, ring_handle) =
ReaderSnippetContext::new(reader, options.with_snippet, options.crop_radius);
let mut src = LiveEvents::from_reader(ring_handle, options, false, EnforcingPolicy::AllContent);
let value_res = crate::anchor_store::with_document_scope(|| {
with_interp_redaction_scope(|| {
crate::de::with_root_redaction(crate::de::YamlDeserializer::new(&mut src, cfg), |de| {
T::deserialize(de)
})
})
});
let value = match value_res {
Ok(v) => v,
Err(e) => {
if src.synthesized_null_emitted() {
return Err(snippet_ctx
.attach_snippet(Error::eof().with_location(src.last_location()), &src));
} else {
return Err(snippet_ctx.attach_snippet(e, &src));
}
}
};
match src.peek() {
Ok(Some(_)) => {
return Err(snippet_ctx.attach_snippet(
Error::multiple_documents("use read or read_with_options to obtain the iterator")
.with_location(src.last_location()),
&src,
));
}
Ok(None) => {}
Err(e) => {
if src.seen_doc_end() {
} else {
return Err(snippet_ctx.attach_snippet(e, &src));
}
}
}
if let Err(e) = src.finish() {
return Err(snippet_ctx.attach_snippet(e, &src));
}
Ok(value)
}
#[cfg(feature = "deserialize")]
pub fn read<'a, R, T>(reader: &'a mut R) -> Box<dyn Iterator<Item = Result<T, Error>> + 'a>
where
R: Read + 'a,
T: DeserializeOwned + 'a,
{
Box::new(read_with_options(
reader,
crate::options! {
budget: crate::budget! {
max_reader_input_bytes: None,
},
},
))
}
#[allow(deprecated)]
#[cfg(feature = "deserialize")]
pub fn read_with_options<'a, R, T>(
reader: &'a mut R, options: Options,
) -> impl Iterator<Item = Result<T, Error>> + 'a
where
R: Read + 'a,
T: DeserializeOwned + 'a,
{
struct ReadIter<'a, T> {
src: LiveEvents<'a>, cfg: crate::de::Cfg,
finished: bool,
_marker: std::marker::PhantomData<T>,
}
impl<'a, T> Iterator for ReadIter<'a, T>
where
T: DeserializeOwned + 'a,
{
type Item = Result<T, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
loop {
match self.src.peek() {
Ok(Some(Ev::Scalar { value, style, .. }))
if scalar_is_nullish(value, style) =>
{
let _ = self.src.next();
continue;
}
Ok(Some(_)) => {
let res = crate::anchor_store::with_document_scope(|| {
with_interp_redaction_scope(|| {
crate::de::with_root_redaction(
crate::de::YamlDeserializer::new(&mut self.src, self.cfg),
|de| T::deserialize(de),
)
})
});
if res.is_err() {
if !self.src.skip_to_next_document() {
self.finished = true;
}
}
return Some(res);
}
Ok(None) => {
self.finished = true;
if let Err(e) = self.src.finish() {
return Some(Err(e));
}
return None;
}
Err(e) => {
self.finished = true;
let _ = self.src.finish();
return Some(Err(e));
}
}
}
}
}
let cfg = crate::de::Cfg::from_options(&options);
let src = LiveEvents::from_reader(reader, options, false, EnforcingPolicy::PerDocument);
ReadIter::<T> {
src,
cfg,
finished: false,
_marker: std::marker::PhantomData,
}
}