leap-lang 0.3.0

Leap language parser
Documentation
use crate::leaptypes::*;
use std::collections::HashSet;

pub struct PropRecursionCheck<'a> {
    spec: &'a LeapSpec,
    start_name: String,
    visited: HashSet<ValueType>,
}

impl<'a> PropRecursionCheck<'a> {
    pub fn is_recursive(spec: &'a LeapSpec, leap_type: &LeapType, prop: &Prop) -> bool {
        let mut check = Self {
            spec,
            start_name: leap_type.name().get().to_owned(),
            visited: HashSet::new(),
        };
        check.is_recursive_check(&prop.prop_type)
    }

    fn is_recursive_check(&mut self, next: &ValueType) -> bool {
        if self.start_name == next.name() {
            return true;
        }
        if self.visited.contains(next) {
            return false;
        }
        self.visited.insert(next.clone());
        // get type if it is .struct or .enum
        let next_t = if let Some(t) = self.spec.get_type_by_name(&next.name()) {
            t
        } else {
            return false;
        };
        let next_t = next_t.apply_args(&next.args());
        match next_t {
            LeapType::Struct(s) => {
                for Prop { prop_type, .. } in &s.props {
                    if self.is_recursive_check(prop_type) {
                        return true;
                    }
                }
            }
            LeapType::Enum(e) => {
                for Prop { prop_type, .. } in &e.variants {
                    if self.is_recursive_check(prop_type) {
                        return true;
                    }
                }
            }
        }
        false
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::{parser::parser::Parser, stdtypes::STD_TYPES};

    #[test]
    fn test_simple1() {
        let spec_text = "
            .struct s1
                a: str
        ";
        let spec = LeapSpec::new(Parser::parse(spec_text).unwrap());
        let t = spec.get_type_by_name("s1").unwrap();
        let s = t.as_struct().unwrap();
        assert!(!PropRecursionCheck::is_recursive(&spec, t, &s.props[0]));
    }

    #[test]
    fn test_simple2() {
        let spec_text = "
            .struct s1
                a: s1
        ";
        let spec = LeapSpec::new(Parser::parse(spec_text).unwrap());
        let t = spec.get_type_by_name("s1").unwrap();
        let s = t.as_struct().unwrap();
        assert!(PropRecursionCheck::is_recursive(&spec, t, &s.props[0]));
    }

    #[test]
    fn test_simple3() {
        let spec_text = "
            .struct s1
                a: e

            .struct s2
                a: float

            .enum e
                s1
                s2
        ";
        let spec = LeapSpec::new(Parser::parse(spec_text).unwrap());
        let t = spec.get_type_by_name("e").unwrap();
        let e = t.as_enum().unwrap();
        assert!(PropRecursionCheck::is_recursive(&spec, t, &e.variants[0]));
        assert!(!PropRecursionCheck::is_recursive(&spec, t, &e.variants[1]));
    }

    #[test]
    fn test_complex() {
        let spec_text = "
            .struct s1
                a: s2[option[s3]]
                b: s4

            .struct s2[t]
                a: t

            .struct s3
                a: s1

            .struct s4
                a: option[s5]

            .struct s5
                a: s4
        ";
        let mut spec = LeapSpec::new(Parser::parse(spec_text).unwrap());
        spec.join(LeapSpec::new(Parser::parse(STD_TYPES).unwrap()));
        let t = spec.get_type_by_name("s1").unwrap();
        let s = t.as_struct().unwrap();
        assert!(PropRecursionCheck::is_recursive(&spec, t, &s.props[0]));
        assert!(!PropRecursionCheck::is_recursive(&spec, t, &s.props[1]));

        let t = spec.get_type_by_name("s4").unwrap();
        let s = t.as_struct().unwrap();
        assert!(PropRecursionCheck::is_recursive(&spec, t, &s.props[0]));

        let t = spec.get_type_by_name("s5").unwrap();
        let s = t.as_struct().unwrap();
        assert!(PropRecursionCheck::is_recursive(&spec, t, &s.props[0]));
    }
}