#![cfg_attr(not(feature = "derive"), allow(dead_code))]
use alloc::format;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::fmt;
use crate::Source;
use crate::State;
use crate::error::{Error, ErrorKind, push_expected};
use crate::sync::{Mutex, MutexGuard};
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub enum UnknownFields {
#[default]
Ignore,
Error,
Collect(IgnoredFields),
}
static IGNORE: UnknownFields = UnknownFields::Ignore;
impl UnknownFields {
#[inline]
pub fn of(state: &State) -> &UnknownFields {
state.get::<UnknownFields>().unwrap_or(&IGNORE)
}
#[inline]
pub fn set(self, state: &mut State) {
*state.get_mut::<UnknownFields>() = self;
}
}
#[derive(Clone, Default)]
pub struct IgnoredFields {
errors: Arc<Mutex<Vec<Error>>>,
}
impl IgnoredFields {
pub fn new() -> IgnoredFields {
IgnoredFields::default()
}
pub fn take(&self) -> Vec<Error> {
core::mem::take(&mut *self.lock())
}
pub fn len(&self) -> usize {
self.lock().len()
}
pub fn is_empty(&self) -> bool {
self.lock().is_empty()
}
fn lock(&self) -> MutexGuard<'_, Vec<Error>> {
self.errors.lock()
}
fn push(&self, err: Error) {
self.lock().push(err);
}
}
impl fmt::Debug for IgnoredFields {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("IgnoredFields").field(&*self.lock()).finish()
}
}
#[derive(Debug, Default)]
pub(crate) struct UnclaimedKeys(Vec<Error>);
#[inline]
pub(crate) fn wants_unknown_fields(state: &State) -> bool {
!matches!(UnknownFields::of(state), UnknownFields::Ignore)
}
#[cold]
pub(crate) fn unknown_field_error(key: &str, fields: Option<&[&str]>) -> Error {
let mut msg = format!("unknown field `{}`", key);
if let Some(fields) = fields {
push_expected(&mut msg, fields, "fields");
}
Error::new(ErrorKind::UnknownField, msg)
}
#[cold]
pub fn unknown_field(
key: &str,
offset: Option<usize>,
fields: &[&str],
deny: bool,
state: &mut State,
) -> Result<(), Error> {
let fields = if fields.iter().any(|x| x.is_empty()) {
None
} else {
Some(fields)
};
let make_error = || {
let mut err = unknown_field_error(key, fields);
if let Some(offset) = offset {
err.set_offset(offset);
}
err
};
decide(make_error, deny, state)
}
fn decide(make_error: impl FnOnce() -> Error, deny: bool, state: &mut State) -> Result<(), Error> {
if deny {
return Err(make_error());
}
match UnknownFields::of(state) {
UnknownFields::Ignore => Ok(()),
UnknownFields::Error => Err(make_error()),
UnknownFields::Collect(ignored) => {
let ignored = ignored.clone();
ignored.push(located(make_error(), state));
Ok(())
}
}
}
fn located(mut err: Error, state: &State) -> Error {
state.attach_error_context(&mut err);
if let Some(source) = state.get::<Source>() {
err.resolve_position(source.0.as_bytes());
}
err
}
pub(crate) fn report_unclaimed_key(err: Error, state: &mut State) {
state.get_mut::<UnclaimedKeys>().0.push(err);
}
pub fn unclaimed_keys(standalone: bool, deny: bool, state: &mut State) -> Result<(), Error> {
if !standalone {
return Ok(());
}
let keys = match state.get::<UnclaimedKeys>() {
Some(keys) if !keys.0.is_empty() => {
core::mem::take(&mut state.get_mut::<UnclaimedKeys>().0)
}
_ => return Ok(()),
};
for err in keys {
decide(|| err, deny, state)?;
}
Ok(())
}