use std::{fs, path::Path};
use clap::Parser;
use itertools::Itertools;
use natlint::{
cli::{
cmd::{Commands, NatlintCli},
file_finder::find_matching_files,
},
config::Config,
linter::lint,
rules::Violation,
};
fn main() -> eyre::Result<()> {
let cli = NatlintCli::parse();
match cli.command {
Commands::Run(args) => {
let config = match Config::from_file(Path::new(&args.config)) {
Ok(config) => config,
Err(e) => {
println!("Error reading config file: {}.", e);
println!("Using default settings.");
Config::default()
}
};
let file_violations: Vec<(String, Vec<(Violation, usize)>)> =
find_matching_files(&args.root, args.include, args.exclude)?
.iter()
.map(|file| {
let content = fs::read_to_string(file).unwrap();
let file_path = file.to_str().unwrap().to_owned();
(file_path, lint(&content, &config.rules()).unwrap())
})
.sorted_by(|(file_a, _), (file_b, _)| file_a.cmp(file_b))
.collect::<Vec<_>>();
let violation_count = file_violations
.iter()
.map(|(_, violations)| violations.len())
.sum::<usize>();
for (file, violation_with_line) in file_violations.iter() {
if violation_with_line.is_empty() {
if args.verbose {
println!("\nFile: {}", file);
println!(" No violations found.");
}
continue;
}
println!("\nFile: {}", file);
for (violation, line_number) in violation_with_line {
println!(
" [{}] Line {}: {} {}",
violation.rule_name,
line_number,
violation.rule_description,
violation.error
);
}
}
if violation_count == 0 {
println!("No natspec violations found.");
} else {
println!(
"\nFound {} natspec violations in {} files.",
violation_count,
file_violations.len()
);
std::process::exit(1);
}
Ok(())
}
Commands::Init(args) => {
let config = Config::default();
fs::write(&args.config, toml::to_string(&config)?)?;
println!("Default configuration file created at {}", args.config);
Ok(())
}
}
}