indented-blocks 0.5.2

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<'ast, S: 'ast> {
    type Output: Sized;

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

    /// Maps the output of this `ViewFn`.
    /// It produces a new `ViewFn` which calls the given closure
    /// on the return value of `Self` and returns value
    /// which the given closure returned.
    #[inline]
    fn map<F2, O2>(self, f2: F2) -> MapViewFn<Self, F2>
    where
        Self: Sized,
        F2: Fn(Self::Output) -> O2,
    {
        MapViewFn { f1: self, f2 }
    }
}

impl<'ast, S: 'ast> ViewFn<'ast, S> for () {
    type Output = &'ast S;

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

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

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

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

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

impl<'ast, S, 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: mapping.map(f2),
        }
    }
}

impl<'ast, S, 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, 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);
        }
    }
}