use crate::batch::{
BatchConfig, BatchExecutor, FormatOperation, LintOperation, ValidationOperation,
};
use crate::error::CliError;
use crate::file_discovery::{DiscoveryConfig, FileDiscovery};
use colored::Colorize;
use std::path::PathBuf;
#[derive(Debug, Clone)]
pub struct BatchFormatParams {
pub patterns: Vec<String>,
pub output_dir: Option<String>,
pub check: bool,
pub ditto: bool,
pub with_counts: bool,
pub recursive: bool,
pub max_depth: usize,
pub parallel: bool,
pub verbose: bool,
pub max_files_override: Option<Option<usize>>,
}
pub fn batch_validate(
patterns: Vec<String>,
strict: bool,
recursive: bool,
max_depth: usize,
parallel: bool,
verbose: bool,
) -> Result<(), CliError> {
batch_validate_with_config(
patterns, strict, recursive, max_depth, parallel, verbose, None,
)
}
pub fn batch_validate_with_config(
patterns: Vec<String>,
strict: bool,
recursive: bool,
max_depth: usize,
parallel: bool,
verbose: bool,
max_files_override: Option<Option<usize>>,
) -> Result<(), CliError> {
let discovery_config = DiscoveryConfig {
max_depth: Some(max_depth),
extension: Some("hedl".to_string()),
recursive,
..Default::default()
};
let discovery = FileDiscovery::new(patterns, discovery_config);
let paths = discovery.discover()?;
let mut config = BatchConfig {
parallel_threshold: if parallel { 1 } else { usize::MAX },
verbose,
..Default::default()
};
if let Some(override_limit) = max_files_override {
config.max_files = override_limit;
}
crate::batch::validate_file_count(paths.len(), config.max_files)?;
crate::batch::warn_large_batch(paths.len(), verbose);
let processor = BatchExecutor::new(config);
let operation = ValidationOperation { strict };
let results = processor.process(&paths, operation, true)?;
if results.has_failures() {
eprintln!();
eprintln!("{}", "Validation failures:".red().bold());
for failure in results.failures() {
eprintln!(" {} {}", "✗".red(), failure.path.display());
if let Err(e) = &failure.result {
let e: &CliError = e;
eprintln!(" {}", e.to_string().dimmed());
}
}
return Err(CliError::invalid_input(format!(
"{} of {} files failed validation",
results.failure_count(),
results.total_files()
)));
}
Ok(())
}
pub fn batch_format(params: BatchFormatParams) -> Result<(), CliError> {
batch_format_with_config(params)
}
pub fn batch_format_with_config(params: BatchFormatParams) -> Result<(), CliError> {
let BatchFormatParams {
patterns,
output_dir,
check,
ditto,
with_counts,
recursive,
max_depth,
parallel,
verbose,
max_files_override,
} = params;
let discovery_config = DiscoveryConfig {
max_depth: Some(max_depth),
extension: Some("hedl".to_string()),
recursive,
..Default::default()
};
let discovery = FileDiscovery::new(patterns, discovery_config);
let paths = discovery.discover()?;
let mut config = BatchConfig {
parallel_threshold: if parallel { 1 } else { usize::MAX },
verbose,
..Default::default()
};
if let Some(override_limit) = max_files_override {
config.max_files = override_limit;
}
crate::batch::validate_file_count(paths.len(), config.max_files)?;
crate::batch::warn_large_batch(paths.len(), verbose);
let processor = BatchExecutor::new(config);
let operation = FormatOperation {
check,
ditto,
with_counts,
};
let results = processor.process(&paths, operation, true)?;
if !check {
if let Some(out_dir) = output_dir {
std::fs::create_dir_all(&out_dir).map_err(|e| CliError::io_error(&out_dir, e))?;
for result in results.successes() {
if let Ok(formatted) = &result.result {
let output_path = PathBuf::from(&out_dir).join(
result
.path
.file_name()
.ok_or_else(|| CliError::invalid_input("Invalid file name"))?,
);
std::fs::write(&output_path, formatted)
.map_err(|e| CliError::io_error(&output_path, e))?;
}
}
}
}
if results.has_failures() {
eprintln!();
eprintln!("{}", "Format failures:".red().bold());
for failure in results.failures() {
eprintln!(" {} {}", "✗".red(), failure.path.display());
if let Err(e) = &failure.result {
let e: &CliError = e;
eprintln!(" {}", e.to_string().dimmed());
}
}
return Err(CliError::invalid_input(format!(
"{} of {} files failed formatting",
results.failure_count(),
results.total_files()
)));
}
Ok(())
}
pub fn batch_lint(
patterns: Vec<String>,
warn_error: bool,
recursive: bool,
max_depth: usize,
parallel: bool,
verbose: bool,
) -> Result<(), CliError> {
batch_lint_with_config(
patterns, warn_error, recursive, max_depth, parallel, verbose, None,
)
}
pub fn batch_lint_with_config(
patterns: Vec<String>,
warn_error: bool,
recursive: bool,
max_depth: usize,
parallel: bool,
verbose: bool,
max_files_override: Option<Option<usize>>,
) -> Result<(), CliError> {
let discovery_config = DiscoveryConfig {
max_depth: Some(max_depth),
extension: Some("hedl".to_string()),
recursive,
..Default::default()
};
let discovery = FileDiscovery::new(patterns, discovery_config);
let paths = discovery.discover()?;
let mut config = BatchConfig {
parallel_threshold: if parallel { 1 } else { usize::MAX },
verbose,
..Default::default()
};
if let Some(override_limit) = max_files_override {
config.max_files = override_limit;
}
crate::batch::validate_file_count(paths.len(), config.max_files)?;
crate::batch::warn_large_batch(paths.len(), verbose);
let processor = BatchExecutor::new(config);
let operation = LintOperation { warn_error };
let results = processor.process(&paths, operation, true)?;
let mut total_issues = 0;
for result in results.successes() {
if let Ok(diagnostics) = &result.result {
let diagnostics: &Vec<String> = diagnostics;
if !diagnostics.is_empty() {
total_issues += diagnostics.len();
println!();
println!("{} {}:", "Linting".yellow().bold(), result.path.display());
for diagnostic in diagnostics {
println!(" {diagnostic}");
}
}
}
}
if results.has_failures() {
eprintln!();
eprintln!("{}", "Lint failures:".red().bold());
for failure in results.failures() {
eprintln!(" {} {}", "✗".red(), failure.path.display());
if let Err(e) = &failure.result {
let e: &CliError = e;
eprintln!(" {}", e.to_string().dimmed());
}
}
return Err(CliError::invalid_input(format!(
"{} of {} files failed linting",
results.failure_count(),
results.total_files()
)));
}
if total_issues > 0 {
println!();
println!(
"{} {} issues found across {} files",
"Summary:".bright_blue().bold(),
total_issues,
results.total_files()
);
}
Ok(())
}