join_ai/processor.rs
1use std::fs::{self, File};
2use std::io::{self, Write};
3use std::path::PathBuf;
4use std::sync::mpsc;
5
6/// This module handles the processing of files. It receives file paths from the
7/// walker, reads their content, and writes it to the final output file.
8///
9/// # Arguments
10/// * `rx` - The receiver end of a channel, which provides `PathBuf`s from the walker.
11/// * `output_file_path` - The path to the file where content should be written.
12pub fn process_files(
13 rx: mpsc::Receiver<PathBuf>,
14 output_file_path: &PathBuf,
15) -> anyhow::Result<()> {
16 // Create or truncate the output file, making it ready for writing.
17 let mut output_file = File::create(output_file_path)?;
18
19 // Iterate over every file path sent by the walker.
20 // This loop will block until the channel is empty and the sender is dropped.
21 for path in rx {
22 match fs::read(&path) {
23 Ok(contents) => {
24 // A simple and robust way to detect binary files is to check for the NUL byte,
25 // which is common in compiled files but rare in text files.
26 if contents.contains(&0) {
27 println!("Skipping binary file: {}", path.display());
28 continue; // Skip to the next file.
29 }
30
31 // Write a header comment to delineate files in the concatenated output.
32 writeln!(output_file, "// FILE: {}", path.display())?;
33 // Write the actual content of the file.
34 output_file.write_all(&contents)?;
35 // Add a newline for spacing between files.
36 writeln!(output_file)?;
37 }
38 Err(e) => {
39 // It's possible to encounter files that can't be read (e.g., system pipes,
40 // broken symlinks). We log these errors but don't stop the process.
41 if e.kind() != io::ErrorKind::InvalidData {
42 eprintln!("Failed to read file {}: {}", path.display(), e);
43 }
44 }
45 }
46 }
47
48 Ok(())
49}