elint 0.0.1

Erlang code linter
Documentation
pub mod command_parse;
pub mod expect;
pub mod fs;
pub mod item;
pub mod parse;
pub mod rule_element_bif;
pub mod rule_nested_cases;
pub mod token;

pub use item::Span;

#[derive(Debug)]
pub struct Error {
    pub span: item::Span,
    pub message: String,
}

impl Error {
    pub fn new(span: item::Span, message: impl Into<String>) -> Self {
        Self {
            span,
            message: message.into(),
        }
    }

    pub fn fix_span(mut self, span: item::Span) -> Self {
        self.span = span;
        self
    }
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{} ({:?})", self.message, self.span)
    }
}

impl std::error::Error for Error {}

#[derive(Debug)]
pub struct Ast {
    pub text: String,
    pub items: Vec<item::Item>,
}

impl Ast {
    pub fn root(&self) -> item::ItemView<'_> {
        item::ItemView::new(&self.text, &self.items, 0)
    }

    pub fn text(&self, span: item::Span) -> &str {
        span.text(&self.text)
    }

    pub fn item_views(&self) -> impl Iterator<Item = item::ItemView<'_>> {
        (0..self.items.len()).map(|i| item::ItemView::new(&self.text, &self.items, i))
    }

    pub fn is_atom(&self, t: item::ItemView, name: &str) -> bool {
        t.kind() == item::ItemKind::Atom && self.text(t.span()) == name
    }

    pub fn is_tagged_tuple(&self, t: item::ItemView, tag: &str) -> bool {
        t.kind() == item::ItemKind::Tuple
            && t.children()
                .next()
                .is_some_and(|t| self.text(t.span()) == tag)
    }
}

#[derive(Debug)]
pub struct Rule {
    pub name: &'static str,
    pub text: &'static str,
    pub check: fn(&Ast) -> Vec<Span>,
}

impl Rule {
    pub const fn new(name: &'static str, text: &'static str, check: fn(&Ast) -> Vec<Span>) -> Self {
        Self { name, text, check }
    }
}

pub const RULES: &[Rule] = &[rule_nested_cases::RULE, rule_element_bif::RULE];