derail-report 0.1.0

Tools for reporting `derail::Error`s.
Documentation
//! Buffers for generating reports.

#[cfg(feature = "alloc")]
use alloc::{vec, vec::Vec};
use core::array;

use crate::sealed::Sealed;

/// A factory that creates buffers.
pub trait BufferFactory {
    /// The buffer this factory produces.
    ///
    /// Not public API.
    #[doc(hidden)]
    #[expect(private_bounds)]
    type Buffer<T>: Buffer<T>
    where
        T: Default;

    /// Create a new buffer.
    ///
    /// Not public API.
    #[doc(hidden)]
    #[expect(private_interfaces)]
    fn new<T>(_: Sealed) -> Self::Buffer<T>
    where
        T: Default;
}

/// A [`BufferFactory`] that creates stack-allocated buffers.
pub struct StackFactory<const LEN: usize>;

impl<const LEN: usize> BufferFactory for StackFactory<LEN> {
    type Buffer<T>
        = Stack<LEN, T>
    where
        T: Default;

    #[expect(private_interfaces)]
    fn new<T>(_: Sealed) -> Self::Buffer<T>
    where
        T: Default,
    {
        Stack {
            // `Visitor::push` is not called before the first `Visitor::visit`
            // call, so we need to start with a length of 1.
            length: 1,
            stack: array::from_fn::<_, LEN, _>(|_| T::default()),
        }
    }
}

/// A [`BufferFactory`] that creates heap-allocated buffers.
#[cfg(feature = "alloc")]
pub struct HeapFactory;

#[cfg(feature = "alloc")]
impl BufferFactory for HeapFactory {
    type Buffer<T>
        = Heap<T>
    where
        T: Default;

    #[expect(private_interfaces)]
    fn new<T>(_: Sealed) -> Self::Buffer<T>
    where
        T: Default,
    {
        Heap {
            // `Visitor::push` is not called before the first `Visitor::visit`
            // call, so we need to start with a length of 1.
            stack: vec![T::default()],
        }
    }
}

/// A buffer.
pub(crate) trait Buffer<T> {
    /// Push `x` onto the buffer.
    fn push(&mut self, _: Sealed);

    /// Pop the last element off of the buffer.
    fn pop(&mut self, _: Sealed);

    /// Get the last element from the buffer.
    fn last_mut(&mut self, _: Sealed) -> Option<&mut T>;

    /// Get a particular element from the buffer.
    fn get_mut(&mut self, x: usize, _: Sealed) -> Option<&mut T>;

    /// Get the current depth of the buffer.
    fn depth(&self, _: Sealed) -> usize;

    /// Get the maximum depth of the buffer.
    fn max_depth(&self, _: Sealed) -> usize;
}

/// A stack-allocated buffer.
///
/// Not public API.
#[doc(hidden)]
pub struct Stack<const LEN: usize, T> {
    /// Length of the stack.
    length: usize,

    /// The stack.
    stack: [T; LEN],
}

impl<const LEN: usize, T> Buffer<T> for Stack<LEN, T>
where
    T: Default,
{
    fn push(&mut self, _: Sealed) {
        self.length = self.length.saturating_add(1);
        if let Some(y) = self.stack.get_mut(self.length) {
            *y = T::default();
        }
    }

    fn pop(&mut self, _: Sealed) {
        self.length = self.length.saturating_sub(1);
    }

    fn get_mut(&mut self, x: usize, _: Sealed) -> Option<&mut T> {
        self.stack.get_mut(x)
    }

    fn last_mut(&mut self, _: Sealed) -> Option<&mut T> {
        let depth = self.depth(Sealed);
        self.stack.get_mut(depth)
    }

    fn depth(&self, _: Sealed) -> usize {
        // The depth into the tree is one less than the length of the stack.
        self.length.saturating_sub(1)
    }

    fn max_depth(&self, _: Sealed) -> usize {
        LEN
    }
}

/// A heap-allocated buffer.
///
/// Not public API.
#[doc(hidden)]
#[cfg(feature = "alloc")]
pub struct Heap<T> {
    /// The stack.
    stack: Vec<T>,
}

#[cfg(feature = "alloc")]
impl<T> Buffer<T> for Heap<T>
where
    T: Default,
{
    fn push(&mut self, _: Sealed) {
        self.stack.push(T::default());
    }

    fn pop(&mut self, _: Sealed) {
        self.stack.pop();
    }

    fn get_mut(&mut self, x: usize, _: Sealed) -> Option<&mut T> {
        self.stack.get_mut(x)
    }

    fn last_mut(&mut self, _: Sealed) -> Option<&mut T> {
        self.stack.last_mut()
    }

    fn depth(&self, _: Sealed) -> usize {
        // The depth into the tree is one less than the length of the stack.
        self.stack.len().saturating_sub(1)
    }

    fn max_depth(&self, _: Sealed) -> usize {
        unreachable!()
    }
}