pub mod cli;
pub mod config;
pub mod constants;
pub mod core_types;
pub mod discovery;
pub mod errors;
mod filtering;
pub mod output;
pub mod processing;
pub mod signal;
use crate::config::Config;
use crate::core_types::FileInfo;
use anyhow::Result;
use log::debug;
use log::info;
use std::io::Write; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc;
pub fn run(config: Config) -> Result<()> {
info!("Starting dircat run with config: {:?}", config);
#[cfg(not(test))]
let stop_signal: Arc<AtomicBool> = {
info!("Setting up real signal handler.");
signal::setup_signal_handler()?
};
#[cfg(test)]
let stop_signal: Arc<AtomicBool> = {
info!("Using dummy signal handler for test run.");
Arc::new(AtomicBool::new(true))
};
debug!(
"stop_signal initial state: {}",
stop_signal.load(Ordering::Relaxed) );
info!("Signal handler setup complete (real or dummy).");
let (mut normal_files, mut last_files) =
discovery::discover_files(&config, stop_signal.clone())?; info!(
"Discovered {} normal files and {} files to process last.",
normal_files.len(),
last_files.len()
);
if normal_files.is_empty() && last_files.is_empty() && !config.dry_run {
info!("No files found matching criteria. Exiting.");
eprintln!("dircat: No files found matching the specified criteria.");
return Ok(());
}
let result = {
let writer_setup = output::writer::setup_output_writer(&config)?;
let mut writer: Box<dyn Write + Send> = writer_setup.writer;
let clipboard_buffer = writer_setup.clipboard_buffer;
if config.dry_run {
info!("Performing dry run.");
let all_files_refs: Vec<&FileInfo> =
normal_files.iter().chain(last_files.iter()).collect();
output::dry_run::write_dry_run_output(&mut writer, &all_files_refs, &config)?;
info!("Dry run output generated.");
} else {
info!("Starting file content processing...");
processing::process_batch(&mut normal_files, &config, stop_signal.clone())?; processing::process_batch(&mut last_files, &config, stop_signal)?; info!("File content processing complete.");
info!("Generating final output...");
output::generate_output(&normal_files, &last_files, &config, &mut writer)?;
info!("Final output generated.");
}
output::writer::finalize_output(writer, clipboard_buffer, &config)
};
match result {
Ok(_) => info!("dircat run completed successfully."),
Err(e) => {
log::error!("dircat run failed: {}", e);
if let Some(app_err) = e.downcast_ref::<errors::AppError>() {
if matches!(app_err, errors::AppError::Interrupted) {
log::error!("Run failed due to AppError::Interrupted.");
eprintln!("Operation cancelled."); } else {
eprintln!("Error: {}", e); }
} else {
eprintln!("Error: {}", e); }
return Err(e); }
}
Ok(())
}