initial diagnostics implementation
This commit is contained in:
parent
f2b57488fe
commit
2a45d0b983
4 changed files with 1326 additions and 9 deletions
106
src/main.rs
106
src/main.rs
|
@ -1,5 +1,6 @@
|
|||
mod ast;
|
||||
mod cst;
|
||||
mod diagnostic;
|
||||
mod lexer;
|
||||
mod parser;
|
||||
mod syntax;
|
||||
|
@ -12,6 +13,7 @@ use std::path::Path;
|
|||
use thiserror::Error;
|
||||
|
||||
use crate::cst::CstBuilder;
|
||||
use crate::diagnostic::{DiagnosticAnalyzer, DiagnosticConfig};
|
||||
use crate::lexer::NftablesLexer;
|
||||
use crate::parser::Parser as NftablesParser;
|
||||
use crate::syntax::{FormatConfig, IndentStyle, NftablesFormatter};
|
||||
|
@ -28,7 +30,7 @@ enum FormatterError {
|
|||
Io(#[from] io::Error),
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
#[command(
|
||||
name = "nff",
|
||||
version = "0.1.0",
|
||||
|
@ -63,6 +65,34 @@ struct Args {
|
|||
/// Check syntax only, don't format
|
||||
#[arg(long)]
|
||||
check: bool,
|
||||
|
||||
/// Run diagnostics and show issues (syntax, style, best practices)
|
||||
#[arg(long)]
|
||||
diagnostics: bool,
|
||||
|
||||
/// Output diagnostics in JSON format (useful for tooling integration)
|
||||
#[arg(long)]
|
||||
json: bool,
|
||||
|
||||
/// Include style warnings in diagnostics
|
||||
#[arg(long, default_value = "true")]
|
||||
style_warnings: bool,
|
||||
|
||||
/// Include best practice recommendations in diagnostics
|
||||
#[arg(long, default_value = "true")]
|
||||
best_practices: bool,
|
||||
|
||||
/// Include performance hints in diagnostics
|
||||
#[arg(long, default_value = "true")]
|
||||
performance_hints: bool,
|
||||
|
||||
/// Include security warnings in diagnostics
|
||||
#[arg(long, default_value = "true")]
|
||||
security_warnings: bool,
|
||||
|
||||
/// Diagnostic modules to run (comma-separated: lexical,syntax,style,semantic)
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
modules: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
fn process_nftables_config(args: Args) -> Result<()> {
|
||||
|
@ -116,7 +146,59 @@ fn process_nftables_config(args: Args) -> Result<()> {
|
|||
eprintln!();
|
||||
}
|
||||
|
||||
// Parse
|
||||
// Run diagnostics if requested (do this early to catch parse errors)
|
||||
if args.diagnostics || args.json {
|
||||
let diagnostic_config = DiagnosticConfig {
|
||||
enable_style_warnings: args.style_warnings,
|
||||
enable_best_practices: args.best_practices,
|
||||
enable_performance_hints: args.performance_hints,
|
||||
enable_security_warnings: args.security_warnings,
|
||||
max_line_length: 120,
|
||||
max_empty_lines: if args.optimize { 1 } else { 2 },
|
||||
preferred_indent: Some(match args.indent {
|
||||
IndentStyle::Tabs => "tabs".to_string(),
|
||||
IndentStyle::Spaces => "spaces".to_string(),
|
||||
}),
|
||||
};
|
||||
|
||||
let analyzer = DiagnosticAnalyzer::new(diagnostic_config);
|
||||
|
||||
let diagnostics = if let Some(modules) = &args.modules {
|
||||
let module_names: Vec<&str> = modules.iter().map(|s| s.as_str()).collect();
|
||||
analyzer.analyze_with_modules(&source, &args.file, &module_names)
|
||||
} else {
|
||||
analyzer.analyze(&source, &args.file)
|
||||
};
|
||||
|
||||
if args.json {
|
||||
// Output JSON format for tooling integration
|
||||
match diagnostics.to_json() {
|
||||
Ok(json) => println!("{}", json),
|
||||
Err(e) => {
|
||||
if args.json {
|
||||
// Even JSON serialization errors should be in JSON format when --json is used
|
||||
let error_json =
|
||||
format!(r#"{{"error": "JSON serialization failed: {}"}}"#, e);
|
||||
println!("{}", error_json);
|
||||
} else {
|
||||
eprintln!("Error serializing diagnostics to JSON: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Output human-readable format
|
||||
println!("{}", diagnostics.to_human_readable());
|
||||
}
|
||||
|
||||
// Exit with non-zero code if there are errors
|
||||
if diagnostics.has_errors() {
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Parse (only if not doing diagnostics)
|
||||
let ruleset = if args.debug {
|
||||
// Use error-recovery parsing for debug mode
|
||||
let (parsed_ruleset, errors) = NftablesParser::parse_with_errors(&source);
|
||||
|
@ -177,14 +259,20 @@ fn process_nftables_config(args: Args) -> Result<()> {
|
|||
fn main() -> Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
if let Err(e) = process_nftables_config(args) {
|
||||
eprintln!("Error: {}", e);
|
||||
if let Err(e) = process_nftables_config(args.clone()) {
|
||||
if args.json {
|
||||
// Output error in JSON format when --json flag is used
|
||||
let error_json = format!(r#"{{"error": "{}"}}"#, e);
|
||||
println!("{}", error_json);
|
||||
} else {
|
||||
eprintln!("Error: {}", e);
|
||||
|
||||
// Print the error chain
|
||||
let mut current = e.source();
|
||||
while let Some(cause) = current {
|
||||
eprintln!(" Caused by: {}", cause);
|
||||
current = cause.source();
|
||||
// Print the error chain
|
||||
let mut current = e.source();
|
||||
while let Some(cause) = current {
|
||||
eprintln!(" Caused by: {}", cause);
|
||||
current = cause.source();
|
||||
}
|
||||
}
|
||||
|
||||
std::process::exit(1);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue