feat: enhance configuration structure and improve documentation for bundling process

This commit is contained in:
2025-08-24 13:35:36 +02:00
parent acaf79b4ee
commit e47a3a0a66

View File

@@ -10,6 +10,11 @@ use ignore::gitignore::Gitignore;
use tracing::{error, info, warn}; use tracing::{error, info, warn};
use walkdir::{DirEntry, WalkDir}; use walkdir::{DirEntry, WalkDir};
/// Represents the output format for the bundled files.
///
/// - `Markdown`: Outputs files in Markdown format with code blocks.
/// - `Text`: Outputs files as plain text.
/// - `Console`: Outputs files formatted for console display (default).
#[derive(Debug, Clone, ValueEnum, Default)] #[derive(Debug, Clone, ValueEnum, Default)]
pub enum Format { pub enum Format {
Markdown, Markdown,
@@ -18,20 +23,38 @@ pub enum Format {
Console, Console,
} }
/// Configuration options for the file bundling process.
#[derive(Debug)] #[derive(Debug)]
pub struct Config { pub struct Config {
/// The directory to process.
pub directory: PathBuf, pub directory: PathBuf,
/// The optional output file path. If not provided, output is written to stdout.
pub output: Option<PathBuf>, pub output: Option<PathBuf>,
/// File extensions to include in the output.
pub include: Vec<String>, pub include: Vec<String>,
/// File extensions to exclude from the output.
pub exclude: Vec<String>, pub exclude: Vec<String>,
/// The format of the output (Markdown, Text, or Console).
pub format: Format, pub format: Format,
/// Whether to append the current date to the output file name.
pub append_date: bool, pub append_date: bool,
/// Whether to append the current Git hash to the output file name.
pub append_git_hash: bool, pub append_git_hash: bool,
/// Whether to include line numbers in the output.
pub line_numbers: bool, pub line_numbers: bool,
/// Whether to ignore hidden files and directories.
pub ignore_hidden: bool, pub ignore_hidden: bool,
/// Whether to respect `.gitignore` rules.
pub respect_gitignore: bool, pub respect_gitignore: bool,
} }
/// Runs the file bundling process based on the provided configuration.
///
/// # Arguments
/// * `config` - The configuration options for the bundling process.
///
/// # Returns
/// * `Result<()>` - Returns `Ok(())` if successful, or an error if the process fails.
pub fn run(config: Config) -> Result<()> { pub fn run(config: Config) -> Result<()> {
let mut output_path = config.output.clone(); let mut output_path = config.output.clone();
@@ -44,7 +67,14 @@ pub fn run(config: Config) -> Result<()> {
process_directory(&config, writer) process_directory(&config, writer)
} }
/// Appends date and git hash to the output file name if required. /// Appends the current date and/or Git hash to the output file name if required.
///
/// # Arguments
/// * `output_path` - The optional output file path to modify.
/// * `config` - The configuration options for the bundling process.
///
/// # Returns
/// * `Result<()>` - Returns `Ok(())` if successful, or an error if the operation fails.
fn append_date_and_git_hash(output_path: &mut Option<PathBuf>, config: &Config) -> Result<()> { fn append_date_and_git_hash(output_path: &mut Option<PathBuf>, config: &Config) -> Result<()> {
if let Some(path) = output_path { if let Some(path) = output_path {
let mut new_filename = path let mut new_filename = path
@@ -82,7 +112,13 @@ fn append_date_and_git_hash(output_path: &mut Option<PathBuf>, config: &Config)
Ok(()) Ok(())
} }
/// Determines the output writer (file or stdout). /// Determines the output writer (file or stdout) based on the configuration.
///
/// # Arguments
/// * `output_path` - The optional output file path.
///
/// # Returns
/// * `Result<Box<dyn Write>>` - Returns a writer for the output.
fn determine_output_writer(output_path: &Option<PathBuf>) -> Result<Box<dyn Write>> { fn determine_output_writer(output_path: &Option<PathBuf>) -> Result<Box<dyn Write>> {
if let Some(path) = output_path { if let Some(path) = output_path {
info!("Output will be written to: {}", path.display()); info!("Output will be written to: {}", path.display());
@@ -95,7 +131,14 @@ fn determine_output_writer(output_path: &Option<PathBuf>) -> Result<Box<dyn Writ
} }
} }
// Refactored `process_directory` function /// Processes the specified directory and writes the bundled content to the writer.
///
/// # Arguments
/// * `config` - The configuration options for the bundling process.
/// * `writer` - The writer to output the bundled content.
///
/// # Returns
/// * `Result<()>` - Returns `Ok(())` if successful, or an error if the process fails.
fn process_directory(config: &Config, mut writer: Box<dyn Write>) -> Result<()> { fn process_directory(config: &Config, mut writer: Box<dyn Write>) -> Result<()> {
let (gitignore, _) = Gitignore::new(config.directory.join(".gitignore")); let (gitignore, _) = Gitignore::new(config.directory.join(".gitignore"));
@@ -121,12 +164,28 @@ fn process_directory(config: &Config, mut writer: Box<dyn Write>) -> Result<()>
Ok(()) Ok(())
} }
/// Determines if a directory entry should be included. /// Determines if a directory entry should be included based on the configuration.
///
/// # Arguments
/// * `entry` - The directory entry to check.
/// * `gitignore` - The `.gitignore` rules to respect.
/// * `config` - The configuration options for the bundling process.
///
/// # Returns
/// * `bool` - Returns `true` if the entry should be included, `false` otherwise.
fn should_include_entry(entry: &DirEntry, gitignore: &Gitignore, config: &Config) -> bool { fn should_include_entry(entry: &DirEntry, gitignore: &Gitignore, config: &Config) -> bool {
!is_hidden(entry, config) && !is_ignored(entry, gitignore, config) !is_hidden(entry, config) && !is_ignored(entry, gitignore, config)
} }
/// Processes a single file entry. /// Processes a single file entry and writes its content to the writer.
///
/// # Arguments
/// * `entry` - The file entry to process.
/// * `writer` - The writer to output the file content.
/// * `config` - The configuration options for the bundling process.
///
/// # Returns
/// * `Result<()>` - Returns `Ok(())` if successful, or an error if the process fails.
fn process_file_entry(entry: &DirEntry, writer: &mut dyn Write, config: &Config) -> Result<()> { fn process_file_entry(entry: &DirEntry, writer: &mut dyn Write, config: &Config) -> Result<()> {
let path = entry.path(); let path = entry.path();
if !path.is_file() { if !path.is_file() {
@@ -159,6 +218,17 @@ fn process_file_entry(entry: &DirEntry, writer: &mut dyn Write, config: &Config)
.with_context(|| format!("Failed to write file content for {}", path.display())) .with_context(|| format!("Failed to write file content for {}", path.display()))
} }
/// Writes the content of a single file to the writer based on the specified format.
///
/// # Arguments
/// * `writer` - The writer to output the file content.
/// * `path` - The relative path of the file.
/// * `content` - The content of the file.
/// * `extension` - The file extension.
/// * `config` - The configuration options for the bundling process.
///
/// # Returns
/// * `Result<()>` - Returns `Ok(())` if successful, or an error if the operation fails.
fn write_file_content( fn write_file_content(
writer: &mut dyn Write, writer: &mut dyn Write,
path: &Path, path: &Path,
@@ -183,7 +253,15 @@ fn write_file_content(
Ok(()) Ok(())
} }
/// Helper to write content line by line, optionally with line numbers. /// Writes content line by line to the writer, optionally including line numbers.
///
/// # Arguments
/// * `writer` - The writer to output the content.
/// * `content` - The content to write.
/// * `line_numbers` - Whether to include line numbers.
///
/// # Returns
/// * `Result<()>` - Returns `Ok(())` if successful, or an error if the operation fails.
fn write_content_lines(writer: &mut dyn Write, content: &str, line_numbers: bool) -> Result<()> { fn write_content_lines(writer: &mut dyn Write, content: &str, line_numbers: bool) -> Result<()> {
if line_numbers { if line_numbers {
for (i, line) in content.lines().enumerate() { for (i, line) in content.lines().enumerate() {
@@ -195,7 +273,14 @@ fn write_content_lines(writer: &mut dyn Write, content: &str, line_numbers: bool
Ok(()) Ok(())
} }
/// Helper function to check if a directory entry is hidden. /// Checks if a directory entry is hidden based on the configuration.
///
/// # Arguments
/// * `entry` - The directory entry to check.
/// * `config` - The configuration options for the bundling process.
///
/// # Returns
/// * `bool` - Returns `true` if the entry is hidden, `false` otherwise.
fn is_hidden(entry: &DirEntry, config: &Config) -> bool { fn is_hidden(entry: &DirEntry, config: &Config) -> bool {
config.ignore_hidden config.ignore_hidden
&& entry && entry
@@ -205,7 +290,15 @@ fn is_hidden(entry: &DirEntry, config: &Config) -> bool {
.unwrap_or(false) .unwrap_or(false)
} }
/// Helper function to check if a directory entry is ignored by .gitignore. /// Checks if a directory entry is ignored by `.gitignore` rules.
///
/// # Arguments
/// * `entry` - The directory entry to check.
/// * `gitignore` - The `.gitignore` rules to respect.
/// * `config` - The configuration options for the bundling process.
///
/// # Returns
/// * `bool` - Returns `true` if the entry is ignored, `false` otherwise.
fn is_ignored(entry: &DirEntry, gitignore: &Gitignore, config: &Config) -> bool { fn is_ignored(entry: &DirEntry, gitignore: &Gitignore, config: &Config) -> bool {
if !config.respect_gitignore { if !config.respect_gitignore {
return false; return false;