yarte_hir 0.15.6

Intermediate representation for yarte
Documentation
use std::ops::Index;

use quote::{format_ident, quote};

#[derive(Debug, Clone)]
pub(super) struct Scope {
    scope: Vec<syn::Expr>,
    level: Vec<usize>,
    pub(super) count: usize,
}

macro_rules! pop {
    ($_self:ident) => {{
        let last = $_self.level.pop().expect("someone scope");
        $_self.scope.drain(last..)
    }};
}

impl Scope {
    pub(super) fn new(root: syn::Expr, count: usize) -> Scope {
        Scope {
            scope: vec![root],
            level: vec![],
            count,
        }
    }

    #[inline]
    pub(super) fn len(&self) -> usize {
        self.level.len() + 1
    }

    #[inline]
    pub(super) fn is_empty(&self) -> bool {
        self.scope.is_empty()
    }

    pub(super) fn pop(&mut self) {
        pop!(self);
    }

    pub(super) fn pops(&mut self) -> Vec<syn::Expr> {
        pop!(self).collect()
    }

    pub(super) fn push_ident(&mut self, ident: &str) -> syn::Ident {
        let ident = format_ident!("{}__{}", ident, format!("{:#010x?}", self.count));
        self.scope
            .push(syn::parse2(quote!(#ident)).expect("Correct expression"));
        self.count += 1;
        ident
    }

    pub(super) fn push_scope(&mut self, t: Vec<syn::Expr>) {
        self.level.push(self.scope.len());
        self.extend(t);
    }

    #[inline]
    pub(super) fn extend(&mut self, t: Vec<syn::Expr>) {
        self.scope.extend(t);
    }

    pub(super) fn get(&self, n: usize) -> Option<&[syn::Expr]> {
        if n == 0 {
            Some(
                self.level
                    .first()
                    .map_or(&self.scope, |j| &self.scope[..*j]),
            )
        } else {
            self.level
                .get(n)
                .map_or(self.level.get(n - 1).map(|j| &self.scope[*j..]), |j| {
                    Some(&self.scope[self.level[n - 1]..*j])
                })
        }
    }

    #[inline]
    pub(super) fn root(&self) -> &syn::Expr {
        debug_assert!(!self.scope.is_empty());
        &self.scope[0]
    }

    #[inline]
    pub(super) fn get_by(&self, ident: &str) -> Option<&syn::Expr> {
        Self::find(ident, self.scope.iter().rev())
    }

    #[inline]
    #[allow(dead_code)]
    pub(super) fn get_by_at(&self, take: usize, ident: &str) -> Option<&syn::Expr> {
        Self::find(ident, self.scope.iter().take(take).rev())
    }

    fn find<'a, I>(ident: &str, mut i: I) -> Option<&'a syn::Expr>
    where
        I: Iterator<Item = &'a syn::Expr>,
    {
        i.find(|e| {
            let e = quote!(#e).to_string();
            let e = e.as_bytes();
            let ident = ident.as_bytes();
            e.eq(ident)
                || (ident.len() + 12 == e.len()
                    && ident.eq(&e[..ident.len()])
                    && e[ident.len()..ident.len() + 4].eq(b"__0x")
                    && e[ident.len() + 4..].iter().all(|x| x.is_ascii_hexdigit()))
        })
    }
}

impl Index<usize> for Scope {
    type Output = [syn::Expr];

    fn index(&self, i: usize) -> &Self::Output {
        self.get(i).expect("Scope not found.")
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use syn::parse_str;

    #[test]
    fn test_root() {
        let root: syn::Expr = parse_str("self").unwrap();
        let s = Scope::new(root.clone(), 0);
        assert_eq!(s.root(), &root);
    }

    #[test]
    fn test_get() {
        let root: syn::Expr = parse_str("self").unwrap();
        let mut s = Scope::new(root.clone(), 0);
        let scope = vec![parse_str("foo").unwrap()];
        let id = s.len();
        s.push_scope(scope.clone());

        assert_eq!(s.root(), &root);
        assert_eq!(s.get(id).unwrap(), scope.as_slice());
        assert_eq!(&s[id], scope.as_slice());
    }

    #[test]
    fn test_push() {
        let root: syn::Expr = parse_str("self").unwrap();
        let mut s = Scope::new(root.clone(), 0);
        let mut scope = vec![parse_str("foo").unwrap()];
        let id = s.len();
        s.push_scope(scope.clone());
        let ident = "bar";
        let var = s.push_ident(ident);
        let var: syn::Expr = syn::parse2(quote!(#var)).unwrap();
        scope.push(var.clone());

        assert_eq!(s.root(), &root);
        assert_eq!(s.get(id).unwrap(), scope.as_slice());
        assert_eq!(&s[id], scope.as_slice());
        assert_eq!(s.get_by(ident).unwrap(), &var);
    }

    #[test]
    fn test_extend() {
        let root: syn::Expr = parse_str("self").unwrap();
        let mut s = Scope::new(root.clone(), 0);
        let mut scope = vec![parse_str("foo").unwrap()];
        let id = s.len();
        s.push_scope(scope.clone());
        let extend = vec![parse_str("bar").unwrap()];
        s.extend(extend.clone());
        scope.extend(extend);

        assert_eq!(s.root(), &root);
        assert_eq!(s.get(id).unwrap(), scope.as_slice());
        assert_eq!(&s[id], scope.as_slice());
    }
}