221 lines
No EOL
6.8 KiB
Rust
221 lines
No EOL
6.8 KiB
Rust
use std::fs::{self, File};
|
|
use std::io::{self, Read, Write};
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::Command;
|
|
|
|
/// Directories to always skip, regardless of .gitignore
|
|
const IGNORED_DIRS: &[&str] = &[
|
|
"target",
|
|
"node_modules",
|
|
".git",
|
|
"dist",
|
|
"build",
|
|
".next",
|
|
".nuxt",
|
|
".cache",
|
|
".turbo",
|
|
"out",
|
|
"pkg",
|
|
"vendor",
|
|
"coverage",
|
|
"__pycache__",
|
|
];
|
|
|
|
/// Files to always skip
|
|
const IGNORED_FILES: &[&str] = &[
|
|
"package-lock.json",
|
|
"yarn.lock",
|
|
"pnpm-lock.yaml",
|
|
"Cargo.lock",
|
|
"concatenated_code.txt",
|
|
];
|
|
|
|
fn is_ignored_dir(path: &Path) -> bool {
|
|
path.file_name()
|
|
.and_then(|name| name.to_str())
|
|
.map(|name| IGNORED_DIRS.contains(&name))
|
|
.unwrap_or(false)
|
|
}
|
|
|
|
fn is_ignored_file(path: &Path) -> bool {
|
|
path.file_name()
|
|
.and_then(|name| name.to_str())
|
|
.map(|name| IGNORED_FILES.contains(&name))
|
|
.unwrap_or(false)
|
|
}
|
|
|
|
fn is_hidden(path: &Path) -> bool {
|
|
path.file_name()
|
|
.and_then(|name| name.to_str())
|
|
.map(|name| name.starts_with('.'))
|
|
.unwrap_or(false)
|
|
}
|
|
|
|
fn is_git_ignored(root: &Path, path: &Path) -> bool {
|
|
let relative_path = path.strip_prefix(root).unwrap_or(path);
|
|
let relative_path_str = match relative_path.to_str() {
|
|
Some(s) => s,
|
|
None => return false,
|
|
};
|
|
|
|
let output = Command::new("git")
|
|
.args(["check-ignore", "-q", relative_path_str])
|
|
.current_dir(root)
|
|
.output();
|
|
|
|
match output {
|
|
Ok(output) => output.status.success(),
|
|
Err(_) => false, // git not available — don't skip
|
|
}
|
|
}
|
|
|
|
fn should_skip(root: &Path, path: &Path) -> bool {
|
|
if is_hidden(path) {
|
|
println!(" skip (hidden): {}", path.display());
|
|
return true;
|
|
}
|
|
|
|
if path.is_dir() && is_ignored_dir(path) {
|
|
println!(" skip (ignored dir): {}", path.display());
|
|
return true;
|
|
}
|
|
|
|
if path.is_file() && is_ignored_file(path) {
|
|
println!(" skip (ignored file): {}", path.display());
|
|
return true;
|
|
}
|
|
|
|
if is_git_ignored(root, path) {
|
|
println!(" skip (gitignored): {}", path.display());
|
|
return true;
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
const VALID_EXTENSIONS: &[&str] = &[
|
|
"rs", "toml", "mjs", "md", "txt", "js", "ts", "tsx", "html", "css", "json", "yaml", "yml",
|
|
"env", "sh", "sql", "graphql", "proto", "dockerfile",
|
|
];
|
|
|
|
fn has_valid_extension(path: &Path) -> bool {
|
|
// Also allow files with no extension but known names
|
|
let known_extensionless = &["Makefile", "Dockerfile", "Justfile", ".env", ".env.example"];
|
|
|
|
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
|
|
if known_extensionless.contains(&name) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
path.extension()
|
|
.and_then(|ext| ext.to_str())
|
|
.map(|ext| VALID_EXTENSIONS.contains(&ext.to_lowercase().as_str()))
|
|
.unwrap_or(false)
|
|
}
|
|
|
|
fn copy_files_to_single_file(
|
|
source_dir: &Path,
|
|
output_file: &mut File,
|
|
root: &Path,
|
|
output_file_abs_path: &Path,
|
|
) -> Result<(), io::Error> {
|
|
// Read and sort entries for deterministic output
|
|
let mut entries: Vec<_> = fs::read_dir(source_dir)?
|
|
.filter_map(|e| e.ok())
|
|
.collect();
|
|
entries.sort_by_key(|e| e.path());
|
|
|
|
for entry in entries {
|
|
let path = entry.path();
|
|
|
|
if should_skip(root, &path) {
|
|
continue;
|
|
}
|
|
|
|
if path.is_dir() {
|
|
copy_files_to_single_file(&path, output_file, root, output_file_abs_path)?;
|
|
} else if path.is_file() {
|
|
// Skip the output file itself
|
|
let file_abs_path = fs::canonicalize(&path)?;
|
|
if file_abs_path == output_file_abs_path {
|
|
println!(" skip (output file): {}", path.display());
|
|
continue;
|
|
}
|
|
|
|
if !has_valid_extension(&path) {
|
|
println!(" skip (extension): {}", path.display());
|
|
continue;
|
|
}
|
|
|
|
let relative_path = path.strip_prefix(root).unwrap_or(&path);
|
|
writeln!(output_file, "// File: {}\n", relative_path.display())?;
|
|
|
|
let mut file = File::open(&path)?;
|
|
let mut contents = String::new();
|
|
file.read_to_string(&mut contents)?;
|
|
writeln!(output_file, "{}\n", contents)?;
|
|
|
|
println!(" ✓ included: {}", relative_path.display());
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn main() -> Result<(), io::Error> {
|
|
// let project_root = PathBuf::from("./src/gateway/io_backend");
|
|
// let project_root = PathBuf::from("./src/gateway/optimized");
|
|
// let project_root = PathBuf::from("./src/gateway/inbox");
|
|
// let project_root = PathBuf::from("./src/gateway/inbox");
|
|
// let project_root = PathBuf::from("./crates/kra-etims-sdk");
|
|
// let project_root = PathBuf::from("./src/observability");
|
|
// let project_root = PathBuf::from("./src/monitoring");
|
|
let project_root = PathBuf::from("./src/routes");
|
|
// let project_root = PathBuf::from("./src/proxy");
|
|
// let project_root = PathBuf::from("./src");
|
|
let output_file_path = project_root.join("concatenated_code.txt");
|
|
// let output_file_abs_path = fs::canonicalize(&output_file_path).unwrap_or_else(|_| {
|
|
// std::env::current_dir()
|
|
// .unwrap()
|
|
// .join("concatenated_code.txt")
|
|
// });/Users/aok/Projects/rustdev/CratesCode/nimanyatta/src/gateway/optimized
|
|
|
|
// Ensure the output file exists so canonicalize works
|
|
if !output_file_path.exists() {
|
|
File::create(&output_file_path)?;
|
|
}
|
|
let output_file_abs_path = fs::canonicalize(&output_file_path)?;
|
|
|
|
let mut output_file = File::create(&output_file_path)?;
|
|
|
|
println!("╔══════════════════════════════════════════╗");
|
|
println!("║ Code Concatenation Tool ║");
|
|
println!("╚══════════════════════════════════════════╝");
|
|
println!();
|
|
println!("Root: {}", project_root.display());
|
|
println!("Output: {}", output_file_abs_path.display());
|
|
println!();
|
|
println!("Ignored dirs: {}", IGNORED_DIRS.join(", "));
|
|
println!("Ignored files: {}", IGNORED_FILES.join(", "));
|
|
println!("Valid exts: {}", VALID_EXTENSIONS.join(", "));
|
|
println!();
|
|
|
|
copy_files_to_single_file(
|
|
&project_root,
|
|
&mut output_file,
|
|
&project_root,
|
|
&output_file_abs_path,
|
|
)?;
|
|
|
|
// Print summary
|
|
let output_size = fs::metadata(&output_file_path)?.len();
|
|
println!();
|
|
println!("════════════════════════════════════════════");
|
|
println!(
|
|
"Done. Output: {} ({} bytes)",
|
|
output_file_path.display(),
|
|
output_size
|
|
);
|
|
|
|
Ok(())
|
|
} |