use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use anyhow::Result;
use crate::analysis::result::FailureReason;
use crate::cli::check::CheckArgs;
use crate::diff;
use crate::diff::hunks::{Hunk, group_by_file};
use crate::files;
pub const READ_MAX_BYTES: u64 = 8 * 1024 * 1024;
const _: () = assert!(READ_MAX_BYTES >= crate::analysis::payload::PAYLOAD_MAX_BYTES);
pub struct Work {
pub by_file: Vec<Vec<Hunk>>,
pub read_failures: BTreeMap<PathBuf, FailureReason>,
pub lint_only: Vec<PathBuf>,
}
pub async fn resolve(args: &CheckArgs, root: &Path) -> Result<Work> {
let hunks = if args.staged {
diff::staged_hunks(root, files::is_scan_target).await?
} else if let Some(git_ref) = args.diff.as_deref() {
diff::hunks_between(root, git_ref, args.tip.as_deref(), files::is_scan_target).await?
} else {
return resolve_paths(&args.paths, root);
};
Ok(Work {
by_file: group_by_file(hunks),
read_failures: BTreeMap::new(),
lint_only: Vec::new(),
})
}
fn resolve_paths(paths: &[PathBuf], root: &Path) -> Result<Work> {
let files::Expansion { targets, rejected } =
files::expand_named(paths, root, files::is_scan_target);
let mut by_file: Vec<Vec<Hunk>> = Vec::new();
let mut read_failures: BTreeMap<PathBuf, FailureReason> = BTreeMap::new();
let mut lint_only: Vec<PathBuf> = Vec::new();
for (path, why) in rejected {
let reason = match why {
files::Rejected::Missing => {
FailureReason::Unreadable("no such file or directory".to_owned())
}
files::Rejected::Unanalyzable => {
FailureReason::unsupported(&path, files::redirect_hint(&path))
}
};
read_failures.insert(path, reason);
}
for path in targets {
let bytes = match std::fs::metadata(&path) {
Ok(meta) => meta.len(),
Err(err) => {
read_failures.insert(path, FailureReason::Unreadable(err.to_string()));
continue;
}
};
if bytes > READ_MAX_BYTES {
read_failures.insert(
path.clone(),
FailureReason::FileTooLarge {
bytes,
limit: READ_MAX_BYTES,
},
);
lint_only.push(path);
continue;
}
let content = match std::fs::read_to_string(&path) {
Ok(content) => content,
Err(err) => {
read_failures.insert(path.clone(), FailureReason::Unreadable(err.to_string()));
continue;
}
};
by_file.push(vec![Hunk::whole_file(path, &content)]);
}
Ok(Work {
by_file,
read_failures,
lint_only,
})
}