indented-blocks 0.6.0

parser for indented blocks
Documentation
/*!
 * This module provides support for [`View`]s, which provide a
 * reference-mapping functionality to support simple, zero-copy
 * use cases, which require a mapping of `head` values.
 * This is especially useful if the mapping itself either has
 * only a rather small overhead or the resulting [`View`] is only
 * traversed once.
 */

use crate::Block;
use alloc::string::{String, ToString};

#[derive(Debug)]
pub struct View<'ast, S, F> {
    parent: &'ast Block<S>,
    mapping: F,
}

impl<S, F: Clone> Clone for View<'_, S, F> {
    #[inline]
    fn clone(&self) -> Self {
        Self {
            parent: self.parent,
            mapping: self.mapping.clone(),
        }
    }
}

impl<S, F: Copy> Copy for View<'_, S, F> {}

impl<'ast, S, F> View<'ast, S, F> {
    #[inline]
    pub const fn new(parent: &'ast Block<S>, mapping: F) -> Self {
        Self { parent, mapping }
    }
}

#[derive(Clone, Copy)]
pub struct MapViewFn<F1, F2> {
    f1: F1,
    f2: F2,
}

/// helper trait to create namable closures
pub trait ViewFn<S> {
    type Output: Sized;

    fn view(&self, node: S) -> Self::Output;
}

impl<S> ViewFn<S> for () {
    type Output = S;

    #[inline]
    fn view(&self, node: S) -> S {
        node
    }
}

impl<S, F, O> ViewFn<S> for F
where
    F: Fn(S) -> O,
{
    type Output = O;

    #[inline]
    fn view(&self, node: S) -> O {
        (self)(node)
    }
}

impl<S, F: ViewFn<S>> ViewFn<S> for alloc::rc::Rc<F> {
    type Output = F::Output;
    #[inline]
    fn view(&self, node: S) -> F::Output {
        (&**self).view(node)
    }
}
impl<S, F: ViewFn<S>> ViewFn<S> for alloc::sync::Arc<F> {
    type Output = F::Output;
    #[inline]
    fn view(&self, node: S) -> F::Output {
        (&**self).view(node)
    }
}

impl<S, F1, F2, O> ViewFn<S> for MapViewFn<F1, F2>
where
    F1: ViewFn<S>,
    F2: Fn(F1::Output) -> O,
{
    type Output = O;

    #[inline]
    fn view(&self, node: S) -> O {
        (self.f2)(self.f1.view(node))
    }
}

impl<'ast, S: 'ast, F> View<'ast, S, F>
where
    F: ViewFn<&'ast S>,
{
    #[inline]
    pub fn head(&self) -> F::Output {
        self.mapping.view(&self.parent.head)
    }

    #[inline]
    pub fn map<F2, O2>(self, f2: F2) -> View<'ast, S, MapViewFn<F, F2>>
    where
        F2: Fn(F::Output) -> O2,
    {
        let View { parent, mapping } = self;
        View {
            parent,
            mapping: MapViewFn { f1: mapping, f2 },
        }
    }
}

impl<'ast, S: 'ast, F> View<'ast, S, F>
where
    F: Clone + ViewFn<&'ast S>,
{
    #[inline]
    pub fn subs(
        &self,
    ) -> core::iter::Map<
        core::slice::Iter<'ast, Block<S>>,
        impl Fn(&'ast Block<S>) -> View<'ast, S, F>,
    > {
        let mapping = self.mapping.clone();
        let imapf = move |parent: &'ast Block<S>| View {
            parent,
            mapping: mapping.clone(),
        };
        self.parent.subs.iter().map(imapf)
    }
}

impl<'ast, S: 'ast, F> View<'ast, S, F>
where
    F: Clone + ViewFn<&'ast S>,
    F::Output: ToString,
{
    pub(crate) fn append_to_string(
        &self,
        ret: &mut String,
        single_indent: &str,
        upper_indent_factor: u32,
    ) {
        for _ in 0..upper_indent_factor {
            ret.push_str(single_indent);
        }
        *ret += &self.head().to_string();
        ret.push('\n');
        let next_luif = upper_indent_factor + 1;
        for i in self.subs() {
            i.append_to_string(ret, single_indent, next_luif);
        }
    }
}