use crate::error::{Error, Result};
use crate::parser::{self};
use crate::prelude::*;
use crate::span_context;
use crate::value::{Number, Value};
use serde::de::{self, DeserializeSeed, IntoDeserializer, MapAccess, SeqAccess, Visitor};
use serde::Deserialize;
#[cfg(feature = "std")]
use std::io;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum YamlVersion {
#[default]
V1_2,
V1_1,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ParserConfig {
pub yaml_version: YamlVersion,
pub max_depth: usize,
pub max_document_length: usize,
pub max_alias_expansions: usize,
pub max_mapping_keys: usize,
pub max_sequence_length: usize,
pub max_events: usize,
pub max_nodes: usize,
pub max_total_scalar_bytes: usize,
pub max_documents: usize,
pub max_merge_keys: usize,
pub alias_anchor_ratio: Option<f64>,
pub duplicate_key_policy: DuplicateKeyPolicy,
pub strict_booleans: bool,
pub legacy_booleans: bool,
pub tag_registry: Option<Arc<crate::TagRegistry>>,
pub merge_key_policy: MergeKeyPolicy,
pub no_schema: bool,
pub legacy_octal_numbers: bool,
pub ignore_binary_tag_for_string: bool,
pub legacy_sexagesimal: bool,
pub require_indent: RequireIndent,
pub policies: Vec<Arc<dyn crate::policy::Policy>>,
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub properties: Option<Arc<std::collections::HashMap<String, String>>>,
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub strict_properties: bool,
#[cfg(feature = "include")]
#[cfg_attr(docsrs, doc(cfg(feature = "include")))]
pub include_resolver: Option<crate::include::IncludeResolver>,
#[cfg(feature = "include")]
#[cfg_attr(docsrs, doc(cfg(feature = "include")))]
pub max_include_depth: usize,
}
impl Default for ParserConfig {
fn default() -> Self {
ParserConfig {
yaml_version: YamlVersion::V1_2,
max_depth: 128,
max_document_length: 1024 * 1024 * 64, max_alias_expansions: 1024,
max_mapping_keys: 1024 * 64,
max_sequence_length: 1024 * 64,
max_events: 1_000_000,
max_nodes: 250_000,
max_total_scalar_bytes: 1024 * 1024 * 64, max_documents: 1_000,
max_merge_keys: 10_000,
alias_anchor_ratio: Some(10.0),
duplicate_key_policy: DuplicateKeyPolicy::default(),
strict_booleans: false,
legacy_booleans: false,
tag_registry: None,
merge_key_policy: MergeKeyPolicy::default(),
no_schema: false,
legacy_octal_numbers: false,
ignore_binary_tag_for_string: false,
legacy_sexagesimal: false,
require_indent: RequireIndent::Unchecked,
policies: Vec::new(),
#[cfg(feature = "std")]
properties: None,
#[cfg(feature = "std")]
strict_properties: false,
#[cfg(feature = "include")]
include_resolver: None,
#[cfg(feature = "include")]
max_include_depth: 24,
}
}
}
impl ParserConfig {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn strict() -> Self {
ParserConfig {
yaml_version: YamlVersion::V1_2,
max_depth: 64,
max_document_length: 1024 * 1024, max_alias_expansions: 100,
max_mapping_keys: 1024,
max_sequence_length: 1024,
max_events: 100_000,
max_nodes: 25_000,
max_total_scalar_bytes: 1024 * 1024, max_documents: 100,
max_merge_keys: 1_000,
alias_anchor_ratio: Some(5.0),
strict_booleans: true,
legacy_booleans: false,
duplicate_key_policy: DuplicateKeyPolicy::Error,
tag_registry: None,
merge_key_policy: MergeKeyPolicy::default(),
no_schema: false,
legacy_octal_numbers: false,
ignore_binary_tag_for_string: false,
legacy_sexagesimal: false,
require_indent: RequireIndent::Even,
policies: Vec::new(),
#[cfg(feature = "std")]
properties: None,
#[cfg(feature = "std")]
strict_properties: true,
#[cfg(feature = "include")]
include_resolver: None,
#[cfg(feature = "include")]
max_include_depth: 8,
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[must_use]
pub fn properties(
mut self,
properties: Arc<std::collections::HashMap<String, String>>,
) -> Self {
self.properties = Some(properties);
self
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[must_use]
pub fn strict_properties(mut self, strict: bool) -> Self {
self.strict_properties = strict;
self
}
#[cfg(feature = "include")]
#[cfg_attr(docsrs, doc(cfg(feature = "include")))]
#[must_use]
pub fn include_resolver(mut self, resolver: crate::include::IncludeResolver) -> Self {
self.include_resolver = Some(resolver);
self
}
#[cfg(feature = "include")]
#[cfg_attr(docsrs, doc(cfg(feature = "include")))]
#[must_use]
pub fn max_include_depth(mut self, depth: usize) -> Self {
self.max_include_depth = depth;
self
}
#[must_use]
pub fn version(mut self, version: YamlVersion) -> Self {
self.yaml_version = version;
match version {
YamlVersion::V1_1 => {
self.legacy_booleans = true;
self.legacy_octal_numbers = true;
self.legacy_sexagesimal = true;
}
YamlVersion::V1_2 => {
self.legacy_booleans = false;
self.legacy_octal_numbers = false;
self.legacy_sexagesimal = false;
}
}
self
}
#[must_use]
pub fn with_policy<P>(mut self, policy: P) -> Self
where
P: crate::policy::Policy + 'static,
{
self.policies.push(Arc::new(policy));
self
}
#[must_use]
pub fn max_depth(mut self, depth: usize) -> Self {
self.max_depth = depth;
self
}
#[must_use]
pub fn max_document_length(mut self, len: usize) -> Self {
self.max_document_length = len;
self
}
#[must_use]
pub fn max_alias_expansions(mut self, expansions: usize) -> Self {
self.max_alias_expansions = expansions;
self
}
#[must_use]
pub fn max_mapping_keys(mut self, max: usize) -> Self {
self.max_mapping_keys = max;
self
}
#[must_use]
pub fn max_sequence_length(mut self, max: usize) -> Self {
self.max_sequence_length = max;
self
}
#[must_use]
pub fn max_events(mut self, max: usize) -> Self {
self.max_events = max;
self
}
#[must_use]
pub fn max_nodes(mut self, max: usize) -> Self {
self.max_nodes = max;
self
}
#[must_use]
pub fn max_total_scalar_bytes(mut self, max: usize) -> Self {
self.max_total_scalar_bytes = max;
self
}
#[must_use]
pub fn max_documents(mut self, max: usize) -> Self {
self.max_documents = max;
self
}
#[must_use]
pub fn max_merge_keys(mut self, max: usize) -> Self {
self.max_merge_keys = max;
self
}
#[must_use]
pub fn require_indent(mut self, mode: RequireIndent) -> Self {
self.require_indent = mode;
self
}
#[must_use]
pub fn alias_anchor_ratio(mut self, ratio: Option<f64>) -> Self {
self.alias_anchor_ratio = ratio;
self
}
#[must_use]
pub fn duplicate_key_policy(mut self, policy: DuplicateKeyPolicy) -> Self {
self.duplicate_key_policy = policy;
self
}
#[must_use]
pub fn strict_booleans(mut self, strict: bool) -> Self {
self.strict_booleans = strict;
self
}
#[must_use]
pub fn legacy_booleans(mut self, legacy: bool) -> Self {
self.legacy_booleans = legacy;
self
}
#[must_use]
pub fn tag_registry(mut self, registry: Arc<crate::TagRegistry>) -> Self {
self.tag_registry = Some(registry);
self
}
#[must_use]
pub fn merge_key_policy(mut self, policy: MergeKeyPolicy) -> Self {
self.merge_key_policy = policy;
self
}
#[must_use]
pub fn no_schema(mut self, no_schema: bool) -> Self {
self.no_schema = no_schema;
self
}
#[must_use]
pub fn legacy_octal_numbers(mut self, on: bool) -> Self {
self.legacy_octal_numbers = on;
self
}
#[must_use]
pub fn ignore_binary_tag_for_string(mut self, on: bool) -> Self {
self.ignore_binary_tag_for_string = on;
self
}
#[must_use]
pub fn legacy_sexagesimal(mut self, on: bool) -> Self {
self.legacy_sexagesimal = on;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum RequireIndent {
#[default]
Unchecked,
Even,
Divisible(usize),
Uniform(Option<usize>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum MergeKeyPolicy {
#[default]
Auto,
AsOrdinary,
Error,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum DuplicateKeyPolicy {
First,
#[default]
Last,
Error,
}
pub fn from_str<T>(s: &str) -> Result<T>
where
T: for<'de> Deserialize<'de> + 'static,
{
from_str_with_config(s, &ParserConfig::default())
}
pub fn from_str_borrowing<'a, T>(s: &'a str) -> Result<T>
where
T: Deserialize<'a>,
{
from_str_borrowing_with_config(s, &ParserConfig::default())
}
pub fn from_str_borrowing_with_config<'a, T>(s: &'a str, config: &ParserConfig) -> Result<T>
where
T: Deserialize<'a>,
{
let parse_config = parser::ParseConfig::from(config);
if s.len() > parse_config.max_document_length {
return Err(Error::Parse(format!(
"document exceeds maximum length of {} bytes",
parse_config.max_document_length
)));
}
let mut de = crate::streaming::StreamingDeserializer::with_config(s, parse_config);
if let Some(registry) = config.tag_registry.as_ref() {
de = de.with_tag_registry(Arc::clone(registry));
}
T::deserialize(&mut de)
}
fn is_value_target<T: 'static + ?Sized>() -> bool {
use core::any::TypeId;
TypeId::of::<T>() == TypeId::of::<Value>()
}
#[cfg(all(feature = "std", feature = "figment"))]
pub(crate) fn from_str_typed_no_tag_preserve<T>(s: &str, config: &ParserConfig) -> Result<T>
where
T: for<'de> Deserialize<'de>,
{
let stream_eligible = config.merge_key_policy == MergeKeyPolicy::Auto
&& !config.ignore_binary_tag_for_string
&& config.policies.is_empty();
if stream_eligible {
if let Some(res) = crate::streaming::from_str_streaming(s, config) {
return res;
}
}
let parse_config = parser::ParseConfig::from(config);
let (value, span_tree) = parser::parse_one(s, &parse_config)?;
for p in &config.policies {
p.check_value(&value)?;
}
let spans = span_context::build_span_map(&value, &span_tree);
let ctx = span_context::SpanContext {
spans,
source: s.into(),
};
let _guard = span_context::set_span_context(ctx);
let de = Deserializer::with_options(
&value,
Some(_guard.as_ref()),
config.ignore_binary_tag_for_string,
);
T::deserialize(de)
}
#[cfg(all(feature = "std", feature = "strict-deserialise"))]
pub fn from_str_strict<T>(s: &str) -> Result<T>
where
T: for<'de> Deserialize<'de> + 'static,
{
let unknown = std::sync::Mutex::new(Vec::<String>::new());
let value: Value = from_str_with_config(s, &ParserConfig::default())?;
let result: Result<T> = serde_ignored::deserialize(&value, |path| {
unknown
.lock()
.expect("from_str_strict: ignored-paths lock poisoned")
.push(path.to_string());
});
let extras = unknown
.into_inner()
.expect("from_str_strict: ignored-paths lock poisoned");
let typed = result?;
if !extras.is_empty() {
let msg = if extras.len() == 1 {
format!("unknown field at `{}`", extras[0])
} else {
let joined = extras
.iter()
.map(|p| format!("`{p}`"))
.collect::<Vec<_>>()
.join(", ");
format!("unknown fields: {joined}")
};
return Err(Error::UnknownField(msg));
}
Ok(typed)
}
#[cfg(all(feature = "std", feature = "strict-deserialise"))]
pub fn from_slice_strict<T>(b: &[u8]) -> Result<T>
where
T: for<'de> Deserialize<'de> + 'static,
{
let s = core::str::from_utf8(b).map_err(|e| Error::Deserialize(e.to_string()))?;
from_str_strict(s)
}
#[cfg(all(feature = "std", feature = "strict-deserialise"))]
pub fn from_reader_strict<R, T>(mut reader: R) -> Result<T>
where
R: io::Read,
T: for<'de> Deserialize<'de> + 'static,
{
let mut s = String::new();
let _ = reader.read_to_string(&mut s).map_err(Error::Io)?;
from_str_strict(&s)
}
pub fn from_str_with_config<T>(s: &str, config: &ParserConfig) -> Result<T>
where
T: for<'de> Deserialize<'de> + 'static,
{
let stream_eligible = config.merge_key_policy == MergeKeyPolicy::Auto
&& !config.ignore_binary_tag_for_string
&& config.policies.is_empty()
&& properties_inactive(config)
&& includes_inactive(config);
if stream_eligible {
if let Some(res) = crate::streaming::from_str_streaming(s, config) {
return res;
}
}
let parse_config = parser::ParseConfig::from(config);
if is_value_target::<T>() {
let mut value = parser::parse_one_value(s, &parse_config)?;
apply_includes(&mut value, config)?;
apply_properties(&mut value, config)?;
for p in &config.policies {
p.check_value(&value)?;
}
let boxed: Box<dyn core::any::Any> = Box::new(value);
let downcast: Box<T> = boxed
.downcast::<T>()
.expect("is_value_target proved T == Value");
return Ok(*downcast);
}
#[cfg(feature = "std")]
{
let (mut value, span_tree) = parser::parse_one(s, &parse_config)?;
apply_includes(&mut value, config)?;
apply_properties(&mut value, config)?;
for p in &config.policies {
p.check_value(&value)?;
}
let spans = span_context::build_span_map(&value, &span_tree);
let ctx = span_context::SpanContext {
spans,
source: s.into(),
};
let _guard = span_context::set_span_context(ctx);
let de = Deserializer::with_options(
&value,
Some(_guard.as_ref()),
config.ignore_binary_tag_for_string,
);
T::deserialize(de)
}
#[cfg(not(feature = "std"))]
{
let value = parser::parse_one_value(s, &parse_config)?;
let de = Deserializer::with_options(&value, None, config.ignore_binary_tag_for_string);
T::deserialize(de)
}
}
#[cfg(feature = "std")]
#[inline]
fn properties_inactive(config: &ParserConfig) -> bool {
config.properties.is_none()
}
#[cfg(not(feature = "std"))]
#[inline]
fn properties_inactive(_config: &ParserConfig) -> bool {
true
}
#[cfg(feature = "std")]
fn apply_properties(value: &mut Value, config: &ParserConfig) -> Result<()> {
if let Some(props) = config.properties.as_ref() {
let action = if config.strict_properties {
crate::value::MissingAction::Error(false)
} else {
crate::value::MissingAction::Empty
};
value.interpolate_inner(
&|name| match props.get(name) {
Some(v) => crate::value::ResolveOutcome::Found(v.clone()),
None => crate::value::ResolveOutcome::Missing,
},
action,
)?;
}
Ok(())
}
#[cfg(not(feature = "std"))]
#[inline]
fn apply_properties(_value: &mut Value, _config: &ParserConfig) -> Result<()> {
Ok(())
}
#[cfg(feature = "include")]
#[inline]
fn includes_inactive(config: &ParserConfig) -> bool {
config.include_resolver.is_none()
}
#[cfg(not(feature = "include"))]
#[inline]
fn includes_inactive(_config: &ParserConfig) -> bool {
true
}
#[cfg(feature = "include")]
fn apply_includes(value: &mut Value, config: &ParserConfig) -> Result<()> {
if let Some(resolver) = config.include_resolver.as_ref() {
let parse_config = parser::ParseConfig::from(config);
let mut visited: std::collections::HashSet<String> = std::collections::HashSet::new();
let mut next_id: usize = 1;
resolve_includes_recursive(
value,
resolver,
&parse_config,
config.max_include_depth,
0,
0,
&mut visited,
&mut next_id,
)?;
}
Ok(())
}
#[cfg(not(feature = "include"))]
#[inline]
fn apply_includes(_value: &mut Value, _config: &ParserConfig) -> Result<()> {
Ok(())
}
#[cfg(feature = "include")]
#[allow(clippy::too_many_arguments)]
fn resolve_includes_recursive(
value: &mut Value,
resolver: &crate::include::IncludeResolver,
parse_config: &parser::ParseConfig,
max_depth: usize,
depth: usize,
from_id: usize,
visited: &mut std::collections::HashSet<String>,
next_id: &mut usize,
) -> Result<()> {
if depth > max_depth {
return Err(Error::RecursionLimitExceeded { depth });
}
match value {
Value::Tagged(boxed) => {
if boxed.tag().as_str() == "!include" {
let spec = match boxed.value().as_str() {
Some(s) => s.to_string(),
None => {
return Err(Error::Custom(
"!include directive expects a scalar string spec".into(),
));
}
};
if !visited.insert(spec.clone()) {
return Err(Error::Custom(format!(
"!include cycle detected: `{spec}` already in resolution chain"
)));
}
let (path, fragment) = crate::include::split_fragment(&spec);
let req = crate::include::IncludeRequest {
spec: &spec,
from_id,
depth,
};
let source = resolver.resolve(req)?;
let id = *next_id;
*next_id += 1;
let mut included = parser::parse_one_value(&source.bytes, parse_config)?;
resolve_includes_recursive(
&mut included,
resolver,
parse_config,
max_depth,
depth + 1,
id,
visited,
next_id,
)?;
if let Some(frag) = fragment {
if let Some(map) = included.as_mapping() {
match map.get(frag) {
Some(v) => *value = v.clone(),
None => {
return Err(Error::Custom(format!(
"!include fragment `#{frag}` not found in `{path}`"
)));
}
}
} else {
return Err(Error::Custom(format!(
"!include fragment `#{frag}` requires a mapping-shaped \
included document; `{path}` is not a mapping"
)));
}
} else {
*value = included;
}
let _ = visited.remove(&spec);
} else {
resolve_includes_recursive(
boxed.value_mut(),
resolver,
parse_config,
max_depth,
depth,
from_id,
visited,
next_id,
)?;
}
}
Value::Sequence(seq) => {
for v in seq {
resolve_includes_recursive(
v,
resolver,
parse_config,
max_depth,
depth,
from_id,
visited,
next_id,
)?;
}
}
Value::Mapping(map) => {
for v in map.values_mut() {
resolve_includes_recursive(
v,
resolver,
parse_config,
max_depth,
depth,
from_id,
visited,
next_id,
)?;
}
}
Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => {}
}
Ok(())
}
pub fn from_slice<T>(b: &[u8]) -> Result<T>
where
T: for<'de> Deserialize<'de> + 'static,
{
let s = core::str::from_utf8(b).map_err(|e| Error::Deserialize(e.to_string()))?;
from_str(s)
}
pub fn from_slice_with_config<T>(b: &[u8], config: &ParserConfig) -> Result<T>
where
T: for<'de> Deserialize<'de> + 'static,
{
let s = core::str::from_utf8(b).map_err(|e| Error::Deserialize(e.to_string()))?;
from_str_with_config(s, config)
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn from_reader<R, T>(reader: R) -> Result<T>
where
R: io::Read,
T: for<'de> Deserialize<'de> + 'static,
{
from_reader_with_config(reader, &ParserConfig::default())
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn from_reader_with_config<R, T>(mut reader: R, config: &ParserConfig) -> Result<T>
where
R: io::Read,
T: for<'de> Deserialize<'de> + 'static,
{
let mut s = String::new();
let _ = reader.read_to_string(&mut s).map_err(Error::Io)?;
from_str_with_config(&s, config)
}
pub fn from_value<T>(value: &Value) -> Result<T>
where
T: for<'de> Deserialize<'de> + 'static,
{
if is_value_target::<T>() {
let cloned = value.clone();
let boxed: Box<dyn core::any::Any> = Box::new(cloned);
let downcast: Box<T> = boxed
.downcast::<T>()
.expect("is_value_target proved T == Value");
return Ok(*downcast);
}
T::deserialize(Deserializer::new(value))
}
#[derive(Debug, Clone, Copy)]
pub struct Deserializer<'de> {
pub(crate) value: &'de Value,
pub(crate) span_ctx: Option<&'de span_context::SpanContext>,
pub(crate) ignore_binary_tag_for_string: bool,
pub(crate) preserve_tags: bool,
}
impl<'de> Deserializer<'de> {
#[must_use]
pub fn new(value: &'de Value) -> Self {
Deserializer {
value,
span_ctx: None,
ignore_binary_tag_for_string: false,
preserve_tags: false,
}
}
#[must_use]
pub fn with_span_context(value: &'de Value, span_ctx: &'de span_context::SpanContext) -> Self {
Deserializer {
value,
span_ctx: Some(span_ctx),
ignore_binary_tag_for_string: false,
preserve_tags: false,
}
}
pub(crate) fn with_options(
value: &'de Value,
span_ctx: Option<&'de span_context::SpanContext>,
ignore_binary_tag_for_string: bool,
) -> Self {
Deserializer {
value,
span_ctx,
ignore_binary_tag_for_string,
preserve_tags: false,
}
}
pub(crate) fn with_options_preserving_tags(
value: &'de Value,
span_ctx: Option<&'de span_context::SpanContext>,
ignore_binary_tag_for_string: bool,
) -> Self {
Deserializer {
value,
span_ctx,
ignore_binary_tag_for_string,
preserve_tags: true,
}
}
pub(crate) fn descend(&self, value: &'de Value) -> Self {
Deserializer {
value,
span_ctx: self.span_ctx,
ignore_binary_tag_for_string: self.ignore_binary_tag_for_string,
preserve_tags: self.preserve_tags,
}
}
fn wrap_err<T>(&self, res: Result<T>) -> Result<T> {
match res {
Err(Error::Deserialize(msg)) => {
if let Some(ctx) = self.span_ctx {
let ptr: *const Value = self.value;
let addr = ptr as usize;
if let Some(span) = ctx.spans.get(&addr) {
return Err(Error::deserialize_at(msg, &ctx.source, span.0));
}
}
Err(Error::Deserialize(msg))
}
_ => res,
}
}
}
impl<'de> de::Deserializer<'de> for Deserializer<'de> {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.value {
Value::Null => self.wrap_err(visitor.visit_none()),
Value::Bool(b) => self.wrap_err(visitor.visit_bool(*b)),
Value::Number(Number::Integer(n)) => self.wrap_err(visitor.visit_i64(*n)),
Value::Number(Number::Float(n)) => self.wrap_err(visitor.visit_f64(*n)),
Value::String(s) => self.wrap_err(visitor.visit_str(s)),
Value::Sequence(_) => self.deserialize_seq(visitor),
Value::Mapping(_) => self.deserialize_map(visitor),
Value::Tagged(tagged) => {
if self.preserve_tags {
self.wrap_err(visitor.visit_map(crate::value::TagPreservingMapAccess::new(
tagged.tag().as_str(),
tagged.value(),
)))
} else {
let de = self.descend(tagged.value());
de.deserialize_any(visitor)
}
}
}
}
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.value {
Value::Bool(b) => self.wrap_err(visitor.visit_bool(*b)),
_ => self.wrap_err(Err(Error::TypeMismatch {
expected: "bool",
found: type_name(self.value),
})),
}
}
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_i64(visitor)
}
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_i64(visitor)
}
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_i64(visitor)
}
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.value {
Value::Number(Number::Integer(n)) => self.wrap_err(visitor.visit_i64(*n)),
Value::Number(Number::Float(n))
if n.fract() == 0.0
&& *n >= i64::MIN as f64
&& *n <= i64::MAX as f64
&& !n.is_nan() =>
{
self.wrap_err(visitor.visit_i64(*n as i64))
}
_ => self.wrap_err(Err(Error::TypeMismatch {
expected: "integer",
found: type_name(self.value),
})),
}
}
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_u64(visitor)
}
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_u64(visitor)
}
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_u64(visitor)
}
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.value {
Value::Number(Number::Integer(n)) if *n >= 0 => {
self.wrap_err(visitor.visit_u64(*n as u64))
}
Value::Number(Number::Float(n))
if n.fract() == 0.0 && *n >= 0.0 && *n <= u64::MAX as f64 && !n.is_nan() =>
{
self.wrap_err(visitor.visit_u64(*n as u64))
}
_ => self.wrap_err(Err(Error::TypeMismatch {
expected: "unsigned integer",
found: type_name(self.value),
})),
}
}
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_f64(visitor)
}
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.value {
Value::Number(Number::Float(n)) => self.wrap_err(visitor.visit_f64(*n)),
Value::Number(Number::Integer(n)) => self.wrap_err(visitor.visit_f64(*n as f64)),
_ => self.wrap_err(Err(Error::TypeMismatch {
expected: "float",
found: type_name(self.value),
})),
}
}
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.value {
Value::String(s) if s.chars().count() == 1 => {
self.wrap_err(visitor.visit_char(s.chars().next().unwrap()))
}
_ => self.wrap_err(Err(Error::TypeMismatch {
expected: "char",
found: type_name(self.value),
})),
}
}
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.value {
Value::String(s) => self.wrap_err(visitor.visit_str(s)),
Value::Tagged(boxed)
if self.ignore_binary_tag_for_string && is_binary_tag(boxed.tag().as_str()) =>
{
match boxed.value() {
Value::String(s) => self.wrap_err(visitor.visit_str(s)),
other => self.wrap_err(Err(Error::TypeMismatch {
expected: "string-shaped !!binary content",
found: type_name(other),
})),
}
}
_ => self.wrap_err(Err(Error::TypeMismatch {
expected: "string",
found: type_name(self.value),
})),
}
}
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_str(visitor)
}
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.value {
Value::String(s) => self.wrap_err(visitor.visit_bytes(s.as_bytes())),
Value::Tagged(boxed) if is_binary_tag(boxed.tag().as_str()) => match boxed.value() {
Value::String(s) => match crate::base64::decode(s) {
Ok(bytes) => self.wrap_err(visitor.visit_byte_buf(bytes)),
Err(why) => self.wrap_err(Err(Error::Deserialize(format!("!!binary: {why}")))),
},
other => self.wrap_err(Err(Error::TypeMismatch {
expected: "string-shaped !!binary content",
found: type_name(other),
})),
},
_ => self.wrap_err(Err(Error::TypeMismatch {
expected: "bytes",
found: type_name(self.value),
})),
}
}
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_bytes(visitor)
}
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.value {
Value::Null => self.wrap_err(visitor.visit_none()),
_ => self.wrap_err(visitor.visit_some(self)),
}
}
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.value {
Value::Null => self.wrap_err(visitor.visit_unit()),
_ => self.wrap_err(Err(Error::TypeMismatch {
expected: "null",
found: type_name(self.value),
})),
}
}
fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_unit(visitor)
}
fn deserialize_newtype_struct<V>(self, name: &'static str, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
if name == crate::spanned::SPANNED_TYPE_NAME {
return visitor.visit_map(SpannedMapAccess::new(self.value, self.span_ctx));
}
self.wrap_err(visitor.visit_newtype_struct(self))
}
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.value {
Value::Sequence(seq) => {
self.wrap_err(visitor.visit_seq(ValueSeqAccess::from_de(&self, seq)))
}
Value::Tagged(tagged) => self.descend(tagged.value()).deserialize_seq(visitor),
_ => self.wrap_err(Err(Error::TypeMismatch {
expected: "sequence",
found: type_name(self.value),
})),
}
}
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_seq(visitor)
}
fn deserialize_tuple_struct<V>(
self,
_name: &'static str,
_len: usize,
visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_seq(visitor)
}
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.value {
Value::Mapping(map) => {
self.wrap_err(visitor.visit_map(ValueMapAccess::from_de(&self, map)))
}
Value::Tagged(tagged) => self.descend(tagged.value()).deserialize_map(visitor),
_ => self.wrap_err(Err(Error::TypeMismatch {
expected: "mapping",
found: type_name(self.value),
})),
}
}
fn deserialize_struct<V>(
self,
name: &'static str,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
if name == crate::spanned::SPANNED_TYPE_NAME {
return visitor.visit_map(SpannedMapAccess::new(self.value, self.span_ctx));
}
self.deserialize_map(visitor)
}
fn deserialize_enum<V>(
self,
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.value {
Value::String(variant) => {
let de: de::value::StrDeserializer<'de, Error> =
variant.as_str().into_deserializer();
self.wrap_err(visitor.visit_enum(de))
}
Value::Mapping(map) if map.len() == 1 => {
let (variant, value) = map.iter().next().unwrap();
self.wrap_err(visitor.visit_enum(EnumAccess {
variant,
value,
span_ctx: self.span_ctx,
}))
}
_ => self.wrap_err(Err(Error::TypeMismatch {
expected: "string or single-key mapping",
found: type_name(self.value),
})),
}
}
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.value {
Value::String(s) => self.wrap_err(visitor.visit_str(s)),
_ => self.deserialize_any(visitor),
}
}
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.wrap_err(visitor.visit_unit())
}
}
pub(crate) struct ValueSeqAccess<'de> {
iter: core::slice::Iter<'de, Value>,
span_ctx: Option<&'de span_context::SpanContext>,
ignore_binary_tag_for_string: bool,
preserve_tags: bool,
}
impl<'de> ValueSeqAccess<'de> {
pub(crate) fn from_de(de: &Deserializer<'de>, seq: &'de [Value]) -> Self {
ValueSeqAccess {
iter: seq.iter(),
span_ctx: de.span_ctx,
ignore_binary_tag_for_string: de.ignore_binary_tag_for_string,
preserve_tags: de.preserve_tags,
}
}
}
impl<'de> SeqAccess<'de> for ValueSeqAccess<'de> {
type Error = Error;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
where
T: DeserializeSeed<'de>,
{
match self.iter.next() {
Some(value) => {
let de = Deserializer {
value,
span_ctx: self.span_ctx,
ignore_binary_tag_for_string: self.ignore_binary_tag_for_string,
preserve_tags: self.preserve_tags,
};
seed.deserialize(de).map(Some)
}
None => Ok(None),
}
}
}
pub(crate) struct ValueMapAccess<'de> {
iter: indexmap::map::Iter<'de, String, Value>,
value: Option<&'de Value>,
span_ctx: Option<&'de span_context::SpanContext>,
ignore_binary_tag_for_string: bool,
preserve_tags: bool,
}
impl<'de> ValueMapAccess<'de> {
pub(crate) fn from_de(de: &Deserializer<'de>, map: &'de crate::value::Mapping) -> Self {
ValueMapAccess {
iter: map.iter(),
value: None,
span_ctx: de.span_ctx,
ignore_binary_tag_for_string: de.ignore_binary_tag_for_string,
preserve_tags: de.preserve_tags,
}
}
fn child_de(&self, value: &'de Value) -> Deserializer<'de> {
Deserializer {
value,
span_ctx: self.span_ctx,
ignore_binary_tag_for_string: self.ignore_binary_tag_for_string,
preserve_tags: self.preserve_tags,
}
}
}
impl<'de> MapAccess<'de> for ValueMapAccess<'de> {
type Error = Error;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
where
K: DeserializeSeed<'de>,
{
match self.iter.next() {
Some((key, value)) => {
self.value = Some(value);
let de = self.child_de(value);
let key_de: de::value::StrDeserializer<'de, Error> =
key.as_str().into_deserializer();
de.wrap_err(seed.deserialize(key_de).map(Some))
}
None => Ok(None),
}
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
where
V: DeserializeSeed<'de>,
{
match self.value.take() {
Some(value) => {
let de = self.child_de(value);
let res = seed.deserialize(de);
de.wrap_err(res)
}
None => Err(de::Error::custom("value is missing")),
}
}
}
struct EnumAccess<'de> {
variant: &'de str,
value: &'de Value,
span_ctx: Option<&'de span_context::SpanContext>,
}
impl<'de> de::EnumAccess<'de> for EnumAccess<'de> {
type Error = Error;
type Variant = VariantAccess<'de>;
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
where
V: DeserializeSeed<'de>,
{
let de: de::value::StrDeserializer<'de, Error> = self.variant.into_deserializer();
let variant = seed.deserialize(de)?;
let visitor = VariantAccess {
value: self.value,
span_ctx: self.span_ctx,
};
Ok((variant, visitor))
}
}
struct VariantAccess<'de> {
value: &'de Value,
span_ctx: Option<&'de span_context::SpanContext>,
}
impl<'de> de::VariantAccess<'de> for VariantAccess<'de> {
type Error = Error;
fn unit_variant(self) -> Result<()> {
let de = if let Some(ctx) = self.span_ctx {
Deserializer::with_span_context(self.value, ctx)
} else {
Deserializer::new(self.value)
};
Deserialize::deserialize(de)
}
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
where
T: DeserializeSeed<'de>,
{
let de = if let Some(ctx) = self.span_ctx {
Deserializer::with_span_context(self.value, ctx)
} else {
Deserializer::new(self.value)
};
seed.deserialize(de)
}
fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let de = if let Some(ctx) = self.span_ctx {
Deserializer::with_span_context(self.value, ctx)
} else {
Deserializer::new(self.value)
};
de::Deserializer::deserialize_seq(de, visitor)
}
fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let de = if let Some(ctx) = self.span_ctx {
Deserializer::with_span_context(self.value, ctx)
} else {
Deserializer::new(self.value)
};
de::Deserializer::deserialize_map(de, visitor)
}
}
pub(crate) struct SpannedMapAccess<'de> {
value: &'de Value,
span_ctx: Option<&'de span_context::SpanContext>,
fields: core::slice::Iter<'static, &'static str>,
}
impl<'de> SpannedMapAccess<'de> {
pub(crate) fn new(value: &'de Value, span_ctx: Option<&'de span_context::SpanContext>) -> Self {
SpannedMapAccess {
value,
span_ctx,
fields: crate::spanned::SPANNED_FIELDS.iter(),
}
}
}
impl<'de> MapAccess<'de> for SpannedMapAccess<'de> {
type Error = Error;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
where
K: DeserializeSeed<'de>,
{
match self.fields.next() {
Some(field) => {
use serde::de::value::BorrowedStrDeserializer;
let de: BorrowedStrDeserializer<'_, Error> = BorrowedStrDeserializer::new(field);
seed.deserialize(de).map(Some)
}
None => Ok(None),
}
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
where
V: DeserializeSeed<'de>,
{
use crate::spanned::*;
let last_field = SPANNED_FIELDS[SPANNED_FIELDS.len() - 1 - (self.fields.len())];
if last_field == SPANNED_FIELD_VALUE {
let de = if let Some(ctx) = self.span_ctx {
Deserializer::with_span_context(self.value, ctx)
} else {
Deserializer::new(self.value)
};
return de.wrap_err(seed.deserialize(de));
}
let ptr: *const Value = self.value;
let addr = ptr as usize;
let span = self.span_ctx.and_then(|ctx| ctx.spans.get(&addr));
let loc = if let Some(s) = span {
crate::error::Location::from_index(&self.span_ctx.unwrap().source, s.0)
} else {
crate::error::Location::default()
};
let end_loc = if let Some(s) = span {
crate::error::Location::from_index(&self.span_ctx.unwrap().source, s.1)
} else {
crate::error::Location::default()
};
let val = match last_field {
SPANNED_FIELD_START_LINE => loc.line(),
SPANNED_FIELD_START_COLUMN => loc.column(),
SPANNED_FIELD_START_INDEX => loc.index(),
SPANNED_FIELD_END_LINE => end_loc.line(),
SPANNED_FIELD_END_COLUMN => end_loc.column(),
SPANNED_FIELD_END_INDEX => end_loc.index(),
_ => crate::error::invariant_violated(
"spanned-field index outside the SPANNED_FIELDS array",
),
};
seed.deserialize(val.into_deserializer())
}
}
pub(crate) fn is_binary_tag(tag: &str) -> bool {
matches!(
tag,
"!!binary" | "binary" | "tag:yaml.org,2002:binary" | "!<tag:yaml.org,2002:binary>"
)
}
fn type_name(value: &Value) -> String {
match value {
Value::Null => "null".to_owned(),
Value::Bool(_) => "bool".to_owned(),
Value::Number(Number::Integer(_)) => "integer".to_owned(),
Value::Number(Number::Float(_)) => "float".to_owned(),
Value::String(_) => "string".to_owned(),
Value::Sequence(_) => "sequence".to_owned(),
Value::Mapping(_) => "mapping".to_owned(),
Value::Tagged(tagged) => format!("tagged value (!{})", tagged.tag().as_str()),
}
}