use crate::LintMap; use lib::Report; use rnix::{Root, WalkEvent}; use vfs::{FileId, VfsEntry}; #[derive(Debug)] pub struct LintResult { pub file_id: FileId, pub reports: Vec, } #[must_use] pub fn lint_with(vfs_entry: &VfsEntry, lints: &LintMap) -> LintResult { let file_id = vfs_entry.file_id; let source = vfs_entry.contents; let parsed = Root::parse(source); let error_reports = parsed .errors() .iter() .map(|err: &rnix::parser::ParseError| Report::from_parse_err(err)); let reports = parsed .syntax() .preorder_with_tokens() .filter_map(|event| match event { WalkEvent::Enter(child) => lints.get(&child.kind()).map(|rules| { rules .iter() .filter_map(|rule| rule.validate(&child)) .collect::>() }), WalkEvent::Leave(_) => None, }) .flatten() .chain(error_reports) .collect(); LintResult { file_id, reports } } pub mod main { use std::io; use super::lint_with; use crate::{ config::{Check as CheckConfig, ConfFile}, err::StatixErr, traits::WriteDiagnostic, }; use rayon::prelude::*; pub fn main(check_config: &CheckConfig) -> Result<(), StatixErr> { let conf_file = ConfFile::discover(&check_config.conf_path)?; let lints = conf_file.lints(); let vfs = check_config.vfs(conf_file.ignore.as_slice())?; let mut stdout = io::stdout(); let lint = |vfs_entry| lint_with(&vfs_entry, &lints); let results = vfs .par_iter() .map(lint) .filter(|lr| !lr.reports.is_empty()) .collect::>(); if !results.is_empty() { for r in &results { stdout.write(r, &vfs, check_config.format).unwrap(); } std::process::exit(1); } std::process::exit(0); } }