use std::borrow::Cow;
use std::marker::PhantomData;
use oneyaml_core::{Limits, Settings, Span};
use serde::de::value::StrDeserializer;
use serde::de::{self, DeserializeOwned, DeserializeSeed, IntoDeserializer, Unexpected, Visitor};
use std::cell::{Cell, RefCell};
use super::Hint;
use super::core::typed_depth_exceeded;
use super::scalar::{IntTy, Kind, Sc, ScalarDe, VariantMemo, Variants, variant_name};
use crate::construct::keys::key_text;
use crate::construct::scalar::resolve_error;
use crate::error::Error;
use crate::resolve::{Resolver, Style};
use crate::value::{
Mapping, MappingIter, Sequence, TagStr, TaggedValue, VALUE_MAGIC, Value, hand_off,
};
pub fn from_value<T: DeserializeOwned>(value: Value) -> Result<T, Error> {
T::deserialize(OwnedRoot {
value,
limits: Limits::default(),
resolver: Resolver::new(&Settings::new()),
})
}
pub fn from_value_with<T: DeserializeOwned>(value: Value, settings: &Settings) -> Result<T, Error> {
T::deserialize(OwnedRoot {
value,
limits: settings.get_limits().clone(),
resolver: Resolver::new(settings),
})
}
pub(crate) trait Text<'a, 'de>: Copy {
fn text(s: &'a str) -> Cow<'de, str>;
}
#[derive(Clone, Copy)]
pub(crate) struct Borrowed;
impl<'de> Text<'de, 'de> for Borrowed {
fn text(s: &'de str) -> Cow<'de, str> {
Cow::Borrowed(s)
}
}
#[derive(Clone, Copy)]
pub(crate) struct Copied;
impl<'de> Text<'_, 'de> for Copied {
fn text(s: &str) -> Cow<'de, str> {
Cow::Owned(s.to_owned())
}
}
pub(crate) struct ValueDe<'a, 'm, 'de, M> {
value: &'a Value,
depth: usize,
max: usize,
prepaid: bool,
resolver: Resolver,
memo: &'m RefCell<VariantMemo>,
poisoned: &'m Cell<bool>,
mode: PhantomData<fn() -> (M, &'de ())>,
}
impl<M> Clone for ValueDe<'_, '_, '_, M> {
fn clone(&self) -> Self {
*self
}
}
impl<M> Copy for ValueDe<'_, '_, '_, M> {}
fn run<'a, 'de, M: Text<'a, 'de>, R>(
value: &'a Value,
limits: &Limits,
resolver: Resolver,
f: impl for<'m> FnOnce(ValueDe<'a, 'm, 'de, M>) -> Result<R, Error>,
) -> Result<R, Error> {
if let Err(e) = value.check_depth(limits) {
return Err(e.into_error().finish(None, Vec::new()));
}
let memo = RefCell::default();
let poisoned = Cell::new(false);
let read = f(ValueDe::new(
value,
0,
limits.get_max_typed_depth(),
resolver,
&memo,
&poisoned,
));
match read {
Ok(_) if poisoned.get() => Err(finish(super::read_after_ignored_error(None))),
other => other.map_err(finish),
}
}
fn finish(e: Error) -> Error {
e.without_span().finish(None, Vec::new())
}
fn key_name(k: &Value) -> String {
match k {
Value::String(s) => crate::error::segment(s),
Value::Tagged(t) => match &t.value {
Value::String(s) => crate::error::segment(s),
_ => key_text(k),
},
_ => key_text(k),
}
}
fn tagged_what(content: &Value) -> &'static str {
match content {
Value::Sequence(_) => "tagged sequence",
Value::Mapping(_) => "tagged mapping",
Value::Tagged(_) => "tagged value",
_ => "tagged scalar",
}
}
impl<'a, 'm, 'de, M: Text<'a, 'de>> ValueDe<'a, 'm, 'de, M> {
fn new(
value: &'a Value,
depth: usize,
max: usize,
resolver: Resolver,
memo: &'m RefCell<VariantMemo>,
poisoned: &'m Cell<bool>,
) -> Self {
Self {
value,
depth,
max,
prepaid: false,
resolver,
memo,
poisoned,
mode: PhantomData,
}
}
fn at(self, value: &'a Value, depth: usize) -> Self {
Self::new(
value,
depth,
self.max,
self.resolver,
self.memo,
self.poisoned,
)
}
fn enter(self) -> Result<usize, Error> {
if self.depth >= self.max {
Err(typed_depth_exceeded(self.max, None))
} else {
Ok(self.depth.saturating_add(1))
}
}
fn reentered(self) -> Result<Self, Error> {
let mut again = self.at(self.value, self.enter()?);
again.prepaid = true;
Ok(again)
}
fn visit_level(self) -> Result<usize, Error> {
if self.prepaid {
Ok(self.depth)
} else {
self.enter()
}
}
fn check_unpoisoned(self) -> Result<(), Error> {
if self.poisoned.get() {
Err(super::read_after_ignored_error(None))
} else {
Ok(())
}
}
fn node<V: Visitor<'de>>(self, hint: Hint, v: V) -> Result<V::Value, Error> {
self.check_unpoisoned()?;
let result = self.node_unchecked(hint, v);
if let Err(e) = &result {
if !super::is_target_error(e) {
self.poisoned.set(true);
}
}
result
}
fn node_unchecked<V: Visitor<'de>>(self, hint: Hint, v: V) -> Result<V::Value, Error> {
if hint == Hint::Ignored {
return v.visit_unit();
}
match self.value {
Value::Null => self.scalar(Kind::Null, Cow::Borrowed(""), hint, v),
Value::Bool(b) => self.scalar(Kind::Bool(*b), Cow::Borrowed(""), hint, v),
Value::Number(n) => {
let text = if matches!(hint, Hint::Int(_) | Hint::Float | Hint::Any) {
Cow::Owned(n.to_string())
} else {
Cow::Borrowed("")
};
self.scalar(Kind::Number(n.clone()), text, hint, v)
}
Value::String(s) => self.scalar(Kind::Str, M::text(s), hint, v),
Value::Sequence(q) => self.sequence(q, hint, v),
Value::Mapping(m) => self.mapping(m, hint, v),
Value::Tagged(t) => self.tagged(t, hint, v),
}
}
fn scalar<V: Visitor<'de>>(
self,
kind: Kind<'de>,
text: Cow<'de, str>,
hint: Hint,
v: V,
) -> Result<V::Value, Error> {
match hint {
Hint::Option => match kind {
Kind::Null => v.visit_none(),
_ => v.visit_some(self.reentered()?),
},
Hint::Newtype => v.visit_newtype_struct(self.reentered()?),
_ => Sc {
kind,
value: text,
lenient: false,
yaml11_hint: false,
variant_content: None,
span: Span::empty(0),
depth: self.depth,
max: self.max,
memo: None,
}
.visit(hint, v),
}
}
fn sequence<V: Visitor<'de>>(
self,
q: &'a Sequence,
hint: Hint,
v: V,
) -> Result<V::Value, Error> {
match hint {
Hint::Option => v.visit_some(self.reentered()?),
Hint::Newtype => v.visit_newtype_struct(self.reentered()?),
Hint::Any | Hint::Seq => {
let depth = self.visit_level()?;
let mut access = SeqDe {
de: self,
it: q.iter(),
taken: 0,
depth,
};
let value = v.visit_seq(&mut access)?;
let extra = access.it.len();
if extra == 0 {
Ok(value)
} else {
let taken = access.taken;
Err(de::Error::invalid_length(
taken.saturating_add(extra),
&format!("{taken} elements").as_str(),
))
}
}
_ => Err(de::Error::invalid_type(Unexpected::Seq, &v)),
}
}
fn mapping<V: Visitor<'de>>(self, m: &'a Mapping, hint: Hint, v: V) -> Result<V::Value, Error> {
match hint {
Hint::Option => v.visit_some(self.reentered()?),
Hint::Newtype => v.visit_newtype_struct(self.reentered()?),
Hint::Any | Hint::Map => v.visit_map(MapDe {
de: self,
it: m.iter(),
entry: None,
depth: self.visit_level()?,
}),
Hint::Enum(_) => {
let depth = self.visit_level()?;
let value = v.visit_enum(MapEnum {
de: self,
map: m,
depth,
})?;
if m.len() > 1 {
return Err(de::Error::invalid_length(
2,
&"a mapping with a single key (the variant)",
));
}
Ok(value)
}
_ => Err(de::Error::invalid_type(Unexpected::Map, &v)),
}
}
fn tagged<V: Visitor<'de>>(
self,
t: &'a TaggedValue,
hint: Hint,
v: V,
) -> Result<V::Value, Error> {
match hint {
Hint::Option => return v.visit_some(self.reentered()?),
Hint::Newtype => return v.visit_newtype_struct(self.reentered()?),
Hint::Any | Hint::Enum(_) => {}
_ => {
return Err(de::Error::invalid_type(
Unexpected::Other(tagged_what(&t.value)),
&v,
));
}
}
let depth = match t.value {
Value::Tagged(_) => self.enter()?,
_ => self.depth,
};
let variants = match hint {
Hint::Enum(names) => Some(Variants {
names,
memo: self.memo,
}),
_ => None,
};
let name = variant_name(&t.tag, variants, None, Span::empty(0))?;
let content = self.at(&t.value, depth);
match &t.value {
Value::String(s) => {
let resolve = matches!(hint, Hint::Enum(_))
&& self
.resolver
.resolves_variant_content(t.tag.parts(), Style::Plain);
v.visit_enum(TaggedScalar {
name,
content,
text: s,
tag: t.tag.parts(),
resolve,
})
}
_ => v.visit_enum(TaggedContent { name, content }),
}
}
}
macro_rules! hinted {
($($method:ident => $hint:expr),* $(,)?) => {$(
fn $method<V: Visitor<'de>>(self, v: V) -> Result<V::Value, Error> {
self.node($hint, v)
}
)*};
}
impl<'a, 'm, 'de, M: Text<'a, 'de>> de::Deserializer<'de> for ValueDe<'a, 'm, 'de, M> {
type Error = Error;
hinted! {
deserialize_any => Hint::Any,
deserialize_bool => Hint::Bool,
deserialize_i8 => Hint::Int(IntTy::I8),
deserialize_i16 => Hint::Int(IntTy::I16),
deserialize_i32 => Hint::Int(IntTy::I32),
deserialize_i64 => Hint::Int(IntTy::I64),
deserialize_i128 => Hint::Int(IntTy::I128),
deserialize_u8 => Hint::Int(IntTy::U8),
deserialize_u16 => Hint::Int(IntTy::U16),
deserialize_u32 => Hint::Int(IntTy::U32),
deserialize_u64 => Hint::Int(IntTy::U64),
deserialize_u128 => Hint::Int(IntTy::U128),
deserialize_f32 => Hint::Float,
deserialize_f64 => Hint::Float,
deserialize_char => Hint::Str,
deserialize_str => Hint::Str,
deserialize_string => Hint::Str,
deserialize_bytes => Hint::Str,
deserialize_byte_buf => Hint::Str,
deserialize_option => Hint::Option,
deserialize_unit => Hint::Unit,
deserialize_seq => Hint::Seq,
deserialize_map => Hint::Map,
deserialize_identifier => Hint::Str,
deserialize_ignored_any => Hint::Ignored,
}
fn deserialize_unit_struct<V: Visitor<'de>>(
self,
_: &'static str,
v: V,
) -> Result<V::Value, Error> {
self.node(Hint::Unit, v)
}
fn deserialize_newtype_struct<V: Visitor<'de>>(
self,
name: &'static str,
v: V,
) -> Result<V::Value, Error> {
if name == VALUE_MAGIC {
return hand_off(self.value.clone(), v);
}
self.node(Hint::Newtype, v)
}
fn deserialize_tuple<V: Visitor<'de>>(self, _: usize, v: V) -> Result<V::Value, Error> {
self.node(Hint::Seq, v)
}
fn deserialize_tuple_struct<V: Visitor<'de>>(
self,
_: &'static str,
_: usize,
v: V,
) -> Result<V::Value, Error> {
self.node(Hint::Seq, v)
}
fn deserialize_struct<V: Visitor<'de>>(
self,
name: &'static str,
fields: &'static [&'static str],
v: V,
) -> Result<V::Value, Error> {
if crate::spanned::is_spanned(name, fields) {
return Err(de::Error::invalid_type(
Unexpected::Other("a Value (it has no source spans)"),
&"a node read from YAML text (`Spanned<T>` needs `from_str` or \
`NodeRef::deserialize`)",
));
}
self.node(Hint::Map, v)
}
fn deserialize_enum<V: Visitor<'de>>(
self,
_: &'static str,
variants: &'static [&'static str],
v: V,
) -> Result<V::Value, Error> {
self.node(Hint::Enum(variants), v)
}
}
struct SeqDe<'a, 'm, 'de, M> {
de: ValueDe<'a, 'm, 'de, M>,
it: std::slice::Iter<'a, Value>,
taken: usize,
depth: usize,
}
impl<'a, 'm, 'de, M: Text<'a, 'de>> de::SeqAccess<'de> for SeqDe<'a, 'm, 'de, M> {
type Error = Error;
fn next_element_seed<T: DeserializeSeed<'de>>(
&mut self,
seed: T,
) -> Result<Option<T::Value>, Error> {
self.de.check_unpoisoned()?;
let Some(item) = self.it.next() else {
return Ok(None);
};
let i = self.taken;
self.taken = i.saturating_add(1);
seed.deserialize(self.de.at(item, self.depth))
.map(Some)
.map_err(|e| e.in_index(i))
}
fn size_hint(&self) -> Option<usize> {
Some(self.it.len())
}
}
struct MapDe<'a, 'm, 'de, M> {
de: ValueDe<'a, 'm, 'de, M>,
it: MappingIter<'a>,
entry: Option<(&'a Value, &'a Value)>,
depth: usize,
}
impl<'a, 'm, 'de, M: Text<'a, 'de>> de::MapAccess<'de> for MapDe<'a, 'm, 'de, M> {
type Error = Error;
fn next_key_seed<K: DeserializeSeed<'de>>(
&mut self,
seed: K,
) -> Result<Option<K::Value>, Error> {
self.de.check_unpoisoned()?;
let Some((k, v)) = self.it.next() else {
return Ok(None);
};
self.entry = Some((k, v));
seed.deserialize(self.de.at(k, self.depth)).map(Some)
}
fn next_value_seed<V: DeserializeSeed<'de>>(&mut self, seed: V) -> Result<V::Value, Error> {
self.de.check_unpoisoned()?;
let Some((k, v)) = self.entry.take() else {
debug_assert!(false, "next_value_seed before next_key_seed");
return Err(Error::internal("mapping value read before its key", None));
};
seed.deserialize(self.de.at(v, self.depth))
.map_err(|e| e.in_key(&key_name(k)))
}
fn size_hint(&self) -> Option<usize> {
Some(self.it.len())
}
}
struct MapEnum<'a, 'm, 'de, M> {
de: ValueDe<'a, 'm, 'de, M>,
map: &'a Mapping,
depth: usize,
}
impl<'a, 'm, 'de, M: Text<'a, 'de>> de::EnumAccess<'de> for MapEnum<'a, 'm, 'de, M> {
type Error = Error;
type Variant = Variant<'a, 'm, 'de, M>;
fn variant_seed<S: DeserializeSeed<'de>>(
self,
seed: S,
) -> Result<(S::Value, Self::Variant), Error> {
let Some((k, content)) = self.map.get_index(0) else {
return Err(de::Error::invalid_length(
0,
&"a mapping with a single key (the variant)",
));
};
if matches!(k.untag_ref(), Value::Sequence(_) | Value::Mapping(_)) {
return Err(de::Error::invalid_type(
Unexpected::Other("a collection as variant name"),
&"a variant name",
));
}
let variant = seed.deserialize(self.de.at(k, self.depth))?;
Ok((
variant,
Variant {
content: self.de.at(content, self.depth),
name: Cow::Owned(key_name(k)),
},
))
}
}
struct Variant<'a, 'm, 'de, M> {
content: ValueDe<'a, 'm, 'de, M>,
name: Cow<'static, str>,
}
impl<'a, 'm, 'de, M: Text<'a, 'de>> de::VariantAccess<'de> for Variant<'a, 'm, 'de, M> {
type Error = Error;
fn unit_variant(self) -> Result<(), Error> {
let name = self.name;
de::Deserialize::deserialize(self.content).map_err(|e: Error| e.in_key(&name))
}
fn newtype_variant_seed<T: DeserializeSeed<'de>>(self, seed: T) -> Result<T::Value, Error> {
let name = self.name;
seed.deserialize(self.content).map_err(|e| e.in_key(&name))
}
fn tuple_variant<V: Visitor<'de>>(self, _len: usize, v: V) -> Result<V::Value, Error> {
let name = self.name;
self.content.node(Hint::Seq, v).map_err(|e| e.in_key(&name))
}
fn struct_variant<V: Visitor<'de>>(
self,
_fields: &'static [&'static str],
v: V,
) -> Result<V::Value, Error> {
let name = self.name;
self.content.node(Hint::Map, v).map_err(|e| e.in_key(&name))
}
}
struct TaggedScalar<'a, 'm, 'de, M> {
name: Cow<'static, str>,
content: ValueDe<'a, 'm, 'de, M>,
text: &'a str,
tag: TagStr<'a>,
resolve: bool,
}
impl<'a, 'm, 'de, M: Text<'a, 'de>> de::EnumAccess<'de> for TaggedScalar<'a, 'm, 'de, M> {
type Error = Error;
type Variant = Self;
fn variant_seed<S: DeserializeSeed<'de>>(
self,
seed: S,
) -> Result<(S::Value, Self::Variant), Error> {
let de: StrDeserializer<'_, Error> = (&*self.name).into_deserializer();
let variant = seed.deserialize(de)?;
Ok((variant, self))
}
}
impl<'a, 'm, 'de, M: Text<'a, 'de>> de::VariantAccess<'de> for TaggedScalar<'a, 'm, 'de, M> {
type Error = Error;
fn unit_variant(self) -> Result<(), Error> {
if self.text.is_empty() {
Ok(())
} else {
Err(de::Error::invalid_type(
Unexpected::Str(self.text),
&"a unit variant (no content)",
))
}
}
fn newtype_variant_seed<T: DeserializeSeed<'de>>(self, seed: T) -> Result<T::Value, Error> {
let resolved = if self.resolve {
self.content
.resolver
.variant_content(self.tag, Style::Plain, self.text)
.map_err(|e| resolve_error(e, self.text, Span::empty(0)))?
} else {
None
};
let kind = match resolved.and_then(Kind::plain) {
Some(k @ (Kind::Bool(_) | Kind::Number(_))) => k,
_ => return seed.deserialize(self.content.reentered()?),
};
let sc = Sc {
kind,
value: M::text(self.text),
lenient: true,
yaml11_hint: false,
variant_content: None,
span: Span::empty(0),
depth: self.content.depth,
max: self.content.max,
memo: None,
};
seed.deserialize(ScalarDe(sc.reenter()?))
}
fn tuple_variant<V: Visitor<'de>>(self, _len: usize, v: V) -> Result<V::Value, Error> {
Err(de::Error::invalid_type(Unexpected::Str(self.text), &v))
}
fn struct_variant<V: Visitor<'de>>(
self,
_fields: &'static [&'static str],
v: V,
) -> Result<V::Value, Error> {
Err(de::Error::invalid_type(Unexpected::Str(self.text), &v))
}
}
struct TaggedContent<'a, 'm, 'de, M> {
name: Cow<'static, str>,
content: ValueDe<'a, 'm, 'de, M>,
}
impl<'a, 'm, 'de, M: Text<'a, 'de>> de::EnumAccess<'de> for TaggedContent<'a, 'm, 'de, M> {
type Error = Error;
type Variant = Variant<'a, 'm, 'de, M>;
fn variant_seed<S: DeserializeSeed<'de>>(
self,
seed: S,
) -> Result<(S::Value, Self::Variant), Error> {
let de: StrDeserializer<'_, Error> = (&*self.name).into_deserializer();
let variant = seed.deserialize(de)?;
Ok((
variant,
Variant {
content: self.content,
name: self.name,
},
))
}
}
struct OwnedRoot {
value: Value,
limits: Limits,
resolver: Resolver,
}
macro_rules! root {
($mode:ty, $this:ident, $value:expr, $limits:expr, $resolver:expr;
$(fn $method:ident($($arg:ident: $ty:ty),*);)*) => {$(
fn $method<V: Visitor<'de>>(self, $($arg: $ty,)* v: V) -> Result<V::Value, Error> {
let $this = self;
let limits = $limits;
run::<$mode, _>($value, &limits, $resolver, |d| d.$method($($arg,)* v))
}
)*};
}
macro_rules! root_methods {
($mode:ty, $this:ident, $value:expr, $limits:expr, $resolver:expr) => {
root! { $mode, $this, $value, $limits, $resolver;
fn deserialize_any();
fn deserialize_bool();
fn deserialize_i8();
fn deserialize_i16();
fn deserialize_i32();
fn deserialize_i64();
fn deserialize_i128();
fn deserialize_u8();
fn deserialize_u16();
fn deserialize_u32();
fn deserialize_u64();
fn deserialize_u128();
fn deserialize_f32();
fn deserialize_f64();
fn deserialize_char();
fn deserialize_str();
fn deserialize_string();
fn deserialize_bytes();
fn deserialize_byte_buf();
fn deserialize_option();
fn deserialize_unit();
fn deserialize_seq();
fn deserialize_map();
fn deserialize_identifier();
fn deserialize_ignored_any();
fn deserialize_unit_struct(name: &'static str);
fn deserialize_tuple(len: usize);
fn deserialize_tuple_struct(name: &'static str, len: usize);
fn deserialize_struct(name: &'static str, fields: &'static [&'static str]);
fn deserialize_enum(name: &'static str, variants: &'static [&'static str]);
}
};
}
impl<'de> de::Deserializer<'de> for OwnedRoot {
type Error = Error;
root_methods!(
Copied,
this,
&this.value,
this.limits.clone(),
this.resolver
);
fn deserialize_newtype_struct<V: Visitor<'de>>(
self,
name: &'static str,
v: V,
) -> Result<V::Value, Error> {
if name == VALUE_MAGIC {
if let Err(e) = self.value.check_depth(&self.limits) {
return Err(e.into_error().finish(None, Vec::new()));
}
return hand_off(self.value, v).map_err(finish);
}
let limits = self.limits.clone();
run::<Copied, _>(&self.value, &limits, self.resolver, |d| {
d.deserialize_newtype_struct(name, v)
})
}
}
impl<'de> de::Deserializer<'de> for Value {
type Error = Error;
root_methods!(
Copied,
this,
&this,
Limits::default(),
Resolver::new(&Settings::new())
);
fn deserialize_newtype_struct<V: Visitor<'de>>(
self,
name: &'static str,
v: V,
) -> Result<V::Value, Error> {
OwnedRoot {
value: self,
limits: Limits::default(),
resolver: Resolver::new(&Settings::new()),
}
.deserialize_newtype_struct(name, v)
}
}
impl<'de> de::Deserializer<'de> for &'de Value {
type Error = Error;
root_methods!(
Borrowed,
this,
this,
Limits::default(),
Resolver::new(&Settings::new())
);
fn deserialize_newtype_struct<V: Visitor<'de>>(
self,
name: &'static str,
v: V,
) -> Result<V::Value, Error> {
run::<Borrowed, _>(
self,
&Limits::default(),
Resolver::new(&Settings::new()),
|d| d.deserialize_newtype_struct(name, v),
)
}
}