Files
dev/src/xatu/xatu_main.rs

104 lines
2.8 KiB
Rust

use clap::Parser;
use status::ResultExt;
use std::path::{Path, PathBuf};
use std::process::ExitCode;
use xatu::{Report, SourceFile};
fn is_nix_file(path: &Path) -> bool {
path.extension().is_some_and(|extension| extension == "nix")
}
// TODO: Add configurable exclusions.
fn is_excluded_dir(path: &Path) -> bool {
path.file_name()
.and_then(|name| name.to_str())
.is_some_and(|name| {
matches!(
name,
".git"
| "bazel-bin"
| "bazel-dev"
| "bazel-out"
| "bazel-testlogs"
| "target"
| "testdata"
| "third_party"
) || name == "result"
|| name.starts_with("result-")
})
}
fn collect_source_files(path: &Path) -> status::Result<Vec<SourceFile>> {
let metadata = std::fs::symlink_metadata(path)
.with_context(|| format!("failed to read metadata for {}", path.display()))?;
if metadata.file_type().is_dir() {
if is_excluded_dir(path) {
return Ok(Vec::new());
}
let mut files = Vec::new();
for entry in std::fs::read_dir(path)
.with_context(|| format!("failed to read directory {}", path.display()))?
{
let entry =
entry.with_context(|| format!("failed to read entry in {}", path.display()))?;
files.extend(collect_source_files(&entry.path())?);
}
return Ok(files);
}
if metadata.file_type().is_file() && is_nix_file(path) {
return Ok(vec![SourceFile {
path: path.to_path_buf(),
source: std::fs::read_to_string(path)
.with_context(|| format!("failed to read source file {}", path.display()))?,
}]);
}
Ok(Vec::new())
}
#[derive(Debug, Parser)]
struct Args {
#[arg(value_name = "PATH", default_value = ".")]
paths: Vec<PathBuf>,
}
fn analyze(args: Args) -> status::Result<Report> {
let paths: Vec<PathBuf> = args
.paths
.into_iter()
.map(monorepo::workspace::resolve_workspace_path)
.collect();
let files: Vec<SourceFile> = paths
.iter()
.map(|path| collect_source_files(path))
.collect::<status::Result<Vec<_>>>()?
.into_iter()
.flatten()
.collect();
xatu::future_sight(files)
}
fn main() -> ExitCode {
match analyze(Args::parse()) {
Ok(report) if report.is_clean() => {
println!("No findings.");
ExitCode::SUCCESS
}
Ok(report) => {
print!("{report}");
ExitCode::from(1)
}
Err(error) => {
eprintln!("{error}");
ExitCode::from(2)
}
}
}