feat: refactor output filename handling and enhance argument parsing
This commit is contained in:
@@ -12,3 +12,9 @@ ignore = "0.4.23"
|
||||
tracing = "0.1.41"
|
||||
tracing-subscriber = "0.3.19"
|
||||
walkdir = "2.5.0"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
opt-level = "z"
|
||||
|
66
src/lib.rs
66
src/lib.rs
@@ -36,7 +36,17 @@ pub fn run(config: Config) -> Result<()> {
|
||||
let mut output_path = config.output.clone();
|
||||
|
||||
if config.append_date || config.append_git_hash {
|
||||
if let Some(path) = &mut output_path {
|
||||
append_date_and_git_hash(&mut output_path, &config)?;
|
||||
}
|
||||
|
||||
let writer = determine_output_writer(&output_path)?;
|
||||
|
||||
process_directory(&config, writer)
|
||||
}
|
||||
|
||||
/// Appends date and git hash to the output file name if required.
|
||||
fn append_date_and_git_hash(output_path: &mut Option<PathBuf>, config: &Config) -> Result<()> {
|
||||
if let Some(path) = output_path {
|
||||
let mut new_filename = path
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
@@ -69,28 +79,29 @@ pub fn run(config: Config) -> Result<()> {
|
||||
}
|
||||
path.set_file_name(new_filename);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Determine the output writer (file or stdout)
|
||||
let writer: Box<dyn Write> = if let Some(path) = &output_path {
|
||||
/// Determines the output writer (file or stdout).
|
||||
fn determine_output_writer(output_path: &Option<PathBuf>) -> Result<Box<dyn Write>> {
|
||||
if let Some(path) = output_path {
|
||||
info!("Output will be written to: {}", path.display());
|
||||
let file = File::create(path)
|
||||
.with_context(|| format!("Failed to create output file: {}", path.display()))?;
|
||||
Box::new(BufWriter::new(file))
|
||||
Ok(Box::new(BufWriter::new(file)))
|
||||
} else {
|
||||
info!("Output will be written to stdout.");
|
||||
Box::new(BufWriter::new(io::stdout()))
|
||||
};
|
||||
|
||||
process_directory(&config, writer)
|
||||
Ok(Box::new(BufWriter::new(io::stdout())))
|
||||
}
|
||||
}
|
||||
|
||||
// Refactored `process_directory` function
|
||||
fn process_directory(config: &Config, mut writer: Box<dyn Write>) -> Result<()> {
|
||||
let (gitignore, _) = Gitignore::new(config.directory.join(".gitignore"));
|
||||
|
||||
let walker = WalkDir::new(&config.directory)
|
||||
.into_iter()
|
||||
.filter_entry(|e| !is_hidden(e, config) && !is_ignored(e, &gitignore, config));
|
||||
.filter_entry(|e| should_include_entry(e, &gitignore, config));
|
||||
|
||||
for result in walker {
|
||||
let entry = match result {
|
||||
@@ -101,22 +112,38 @@ fn process_directory(config: &Config, mut writer: Box<dyn Write>) -> Result<()>
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(err) = process_file_entry(&entry, &mut writer, config) {
|
||||
error!("{}", err);
|
||||
}
|
||||
}
|
||||
|
||||
info!("File bundling complete.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Determines if a directory entry should be included.
|
||||
fn should_include_entry(entry: &DirEntry, gitignore: &Gitignore, config: &Config) -> bool {
|
||||
!is_hidden(entry, config) && !is_ignored(entry, gitignore, config)
|
||||
}
|
||||
|
||||
/// Processes a single file entry.
|
||||
fn process_file_entry(entry: &DirEntry, writer: &mut dyn Write, config: &Config) -> Result<()> {
|
||||
let path = entry.path();
|
||||
if !path.is_file() {
|
||||
continue;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let extension = path.extension().and_then(|s| s.to_str()).unwrap_or("");
|
||||
|
||||
let apply_include_filter = !config.include.is_empty()
|
||||
&& !(config.include.len() == 1 && config.include[0].is_empty());
|
||||
let apply_include_filter =
|
||||
!config.include.is_empty() && !(config.include.len() == 1 && config.include[0].is_empty());
|
||||
|
||||
if apply_include_filter && !config.include.contains(&extension.to_string()) {
|
||||
continue;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if config.exclude.contains(&extension.to_string()) {
|
||||
continue;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let relative_path = path.strip_prefix(&config.directory).unwrap_or(path);
|
||||
@@ -124,19 +151,14 @@ fn process_directory(config: &Config, mut writer: Box<dyn Write>) -> Result<()>
|
||||
Ok(content) => content,
|
||||
Err(_) => {
|
||||
warn!("Skipping non-UTF-8 file: {}", path.display());
|
||||
continue; // Skip non-text files
|
||||
return Ok(()); // Skip non-text files
|
||||
}
|
||||
};
|
||||
|
||||
write_file_content(&mut writer, relative_path, &content, extension, config)
|
||||
.with_context(|| format!("Failed to write file content for {}", path.display()))?;
|
||||
write_file_content(writer, relative_path, &content, extension, config)
|
||||
.with_context(|| format!("Failed to write file content for {}", path.display()))
|
||||
}
|
||||
|
||||
info!("File bundling complete.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes the content of a single file to the writer based on the specified format.
|
||||
fn write_file_content(
|
||||
writer: &mut dyn Write,
|
||||
path: &Path,
|
||||
|
29
src/main.rs
29
src/main.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use codebase_to_prompt::Format;
|
||||
use std::path::PathBuf;
|
||||
use tracing::level_filters::LevelFilter;
|
||||
use tracing::{info, level_filters::LevelFilter};
|
||||
use tracing_subscriber::FmtSubscriber;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
@@ -23,19 +23,19 @@ struct Args {
|
||||
#[arg(long, value_enum, default_value_t = Format::Console)]
|
||||
format: Format,
|
||||
|
||||
#[arg(long)]
|
||||
#[arg(short = 'd', long)]
|
||||
append_date: bool,
|
||||
|
||||
#[arg(long)]
|
||||
#[arg(short = 'g', long)]
|
||||
append_git_hash: bool,
|
||||
|
||||
#[arg(long)]
|
||||
#[arg(short = 'l', long)]
|
||||
line_numbers: bool,
|
||||
|
||||
#[arg(long)]
|
||||
#[arg(short = 'H', long)]
|
||||
ignore_hidden: bool,
|
||||
|
||||
#[arg(long, default_value_t = true)]
|
||||
#[arg(short = 'R', long, default_value_t = true)]
|
||||
respect_gitignore: bool,
|
||||
}
|
||||
|
||||
@@ -47,12 +47,25 @@ fn main() -> Result<()> {
|
||||
|
||||
let args = Args::parse();
|
||||
|
||||
// Automatically detect format if outputting to a .md file
|
||||
let mut format = args.format;
|
||||
if matches!(format, Format::Console) {
|
||||
if let Some(output_path) = &args.output {
|
||||
if output_path.extension().and_then(|s| s.to_str()) == Some("md") {
|
||||
format = Format::Markdown;
|
||||
}
|
||||
if output_path.extension().and_then(|s| s.to_str()) == Some("txt") {
|
||||
format = Format::Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let config = codebase_to_prompt::Config {
|
||||
directory: args.directory,
|
||||
output: args.output,
|
||||
include: args.include,
|
||||
exclude: args.exclude,
|
||||
format: args.format,
|
||||
format,
|
||||
append_date: args.append_date,
|
||||
append_git_hash: args.append_git_hash,
|
||||
line_numbers: args.line_numbers,
|
||||
@@ -60,5 +73,7 @@ fn main() -> Result<()> {
|
||||
respect_gitignore: args.respect_gitignore,
|
||||
};
|
||||
|
||||
info!("Starting codebase to prompt with config: {:?}", config);
|
||||
|
||||
codebase_to_prompt::run(config)
|
||||
}
|
||||
|
Reference in New Issue
Block a user