animsmith-core 0.4.2

Engine-agnostic data model, sampling, measurements, and checks for the animsmith animation-clip linter
Documentation
use serde::de::{DeserializeSeed, IgnoredAny, SeqAccess, Visitor};
use serde::{Deserialize, Deserializer};
use std::fmt;
use std::marker::PhantomData;

#[derive(Debug)]
pub(crate) struct CappedSequence<T> {
    pub(crate) values: Vec<T>,
    pub(crate) overflowed: bool,
}

struct CappedSequenceVisitor<T> {
    limit: usize,
    element: PhantomData<fn() -> T>,
}

impl<'de, T> Visitor<'de> for CappedSequenceVisitor<T>
where
    T: Deserialize<'de>,
{
    type Value = CappedSequence<T>;

    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(formatter, "a sequence with at most {} elements", self.limit)
    }

    fn visit_seq<A>(self, mut sequence: A) -> Result<Self::Value, A::Error>
    where
        A: SeqAccess<'de>,
    {
        let mut values = Vec::with_capacity(sequence.size_hint().unwrap_or(0).min(self.limit));
        while values.len() < self.limit {
            let Some(value) = sequence.next_element()? else {
                return Ok(CappedSequence {
                    values,
                    overflowed: false,
                });
            };
            values.push(value);
        }
        let overflowed = sequence.next_element::<IgnoredAny>()?.is_some();
        if overflowed {
            while sequence.next_element::<IgnoredAny>()?.is_some() {}
        }
        Ok(CappedSequence { values, overflowed })
    }
}

pub(crate) fn deserialize_capped_sequence<'de, D, T>(
    deserializer: D,
    limit: usize,
) -> Result<CappedSequence<T>, D::Error>
where
    D: Deserializer<'de>,
    T: Deserialize<'de>,
{
    deserializer.deserialize_seq(CappedSequenceVisitor {
        limit,
        element: PhantomData,
    })
}

pub(crate) struct CappedSequenceSeed<T> {
    pub(crate) limit: usize,
    pub(crate) element: PhantomData<fn() -> T>,
}

impl<'de, T> DeserializeSeed<'de> for CappedSequenceSeed<T>
where
    T: Deserialize<'de>,
{
    type Value = CappedSequence<T>;

    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserialize_capped_sequence(deserializer, self.limit)
    }
}

#[derive(Debug)]
pub(crate) struct RowBudget {
    limit: usize,
    retained: usize,
    overflowed: bool,
}

pub(crate) fn consume_ignored_tail<'de, A>(
    sequence: &mut A,
    mut seen: usize,
    local_limit: usize,
) -> Result<bool, A::Error>
where
    A: SeqAccess<'de>,
{
    while seen < local_limit {
        if sequence.next_element::<IgnoredAny>()?.is_none() {
            return Ok(false);
        }
        seen += 1;
    }
    let overflowed = sequence.next_element::<IgnoredAny>()?.is_some();
    if overflowed {
        while sequence.next_element::<IgnoredAny>()?.is_some() {}
    }
    Ok(overflowed)
}

impl RowBudget {
    pub(crate) const fn new(limit: usize) -> Self {
        Self {
            limit,
            retained: 0,
            overflowed: false,
        }
    }

    pub(crate) fn admit(&mut self) -> bool {
        if self.overflowed {
            return false;
        }
        if self.retained == self.limit {
            self.overflowed = true;
            return false;
        }
        self.retained += 1;
        true
    }

    pub(crate) const fn overflowed(&self) -> bool {
        self.overflowed
    }

    pub(crate) const fn found(&self) -> usize {
        if self.overflowed {
            self.limit + 1
        } else {
            self.retained
        }
    }
}

enum BudgetedElement<T> {
    Value(T),
    Skipped,
}

struct BudgetedElementSeed<'a, T> {
    budget: &'a mut RowBudget,
    element: PhantomData<fn() -> T>,
}

impl<'de, T> DeserializeSeed<'de> for BudgetedElementSeed<'_, T>
where
    T: Deserialize<'de>,
{
    type Value = BudgetedElement<T>;

    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
    where
        D: Deserializer<'de>,
    {
        if self.budget.admit() {
            T::deserialize(deserializer).map(BudgetedElement::Value)
        } else {
            IgnoredAny::deserialize(deserializer).map(|_| BudgetedElement::Skipped)
        }
    }
}

pub(crate) struct BudgetedCappedSequenceSeed<'a, T> {
    pub(crate) budget: &'a mut RowBudget,
    pub(crate) local_limit: usize,
    pub(crate) element: PhantomData<fn() -> T>,
}

impl<'de, T> DeserializeSeed<'de> for BudgetedCappedSequenceSeed<'_, T>
where
    T: Deserialize<'de>,
{
    type Value = CappedSequence<T>;

    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct BudgetedSequenceVisitor<'a, T> {
            budget: &'a mut RowBudget,
            local_limit: usize,
            element: PhantomData<fn() -> T>,
        }

        impl<'de, T> Visitor<'de> for BudgetedSequenceVisitor<'_, T>
        where
            T: Deserialize<'de>,
        {
            type Value = CappedSequence<T>;

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                write!(
                    formatter,
                    "a sequence with at most {} elements",
                    self.local_limit
                )
            }

            fn visit_seq<A>(self, mut sequence: A) -> Result<Self::Value, A::Error>
            where
                A: SeqAccess<'de>,
            {
                let mut values =
                    Vec::with_capacity(sequence.size_hint().unwrap_or(0).min(self.local_limit));
                let mut seen = 0usize;
                while seen < self.local_limit {
                    let Some(element) = sequence.next_element_seed(BudgetedElementSeed {
                        budget: self.budget,
                        element: PhantomData,
                    })?
                    else {
                        return Ok(CappedSequence {
                            values,
                            overflowed: false,
                        });
                    };
                    seen += 1;
                    match element {
                        BudgetedElement::Value(value) => values.push(value),
                        BudgetedElement::Skipped => {
                            let overflowed =
                                consume_ignored_tail(&mut sequence, seen, self.local_limit)?;
                            return Ok(CappedSequence { values, overflowed });
                        }
                    }
                }
                let overflowed = consume_ignored_tail(&mut sequence, seen, self.local_limit)?;
                Ok(CappedSequence { values, overflowed })
            }
        }

        deserializer.deserialize_seq(BudgetedSequenceVisitor {
            budget: self.budget,
            local_limit: self.local_limit,
            element: PhantomData,
        })
    }
}