use anyhow::Result;
use std::path::{Path, PathBuf};
pub(crate) fn parse_csv_list(
raw: Option<&str>,
flag_help: &str,
) -> Result<Option<Vec<String>>, String> {
match raw {
None => Ok(None),
Some(list) => {
let parsed: Vec<String> = list
.split(',')
.map(str::trim)
.filter(|s| !s.is_empty())
.map(str::to_string)
.collect();
if parsed.is_empty() {
return Err(format!(
"{flag_help} must list at least one entry (got empty / whitespace-only input)"
));
}
Ok(Some(parsed))
}
}
}
pub(crate) fn detect_duplicate_paths<'a, I>(paths: I) -> Result<()>
where
I: IntoIterator<Item = &'a Path>,
{
use std::collections::BTreeMap;
let mut counts: BTreeMap<PathBuf, usize> = BTreeMap::new();
for p in paths {
*counts.entry(p.to_path_buf()).or_insert(0) += 1;
}
let duplicates: Vec<(PathBuf, usize)> = counts.into_iter().filter(|(_, n)| *n > 1).collect();
if duplicates.is_empty() {
return Ok(());
}
let summary = duplicates
.iter()
.map(|(p, n)| format!("{} ({}×)", p.display(), n))
.collect::<Vec<_>>()
.join(", ");
anyhow::bail!(
"duplicate artifact path(s) after merging per-shard manifests: {summary}. \
Hypothesis: two shards overlapped on the same target, so both \
emitted an artifact for the same path. Inspect the matrix in \
`.github/workflows/release.yml` (or the equivalent dispatcher) \
to confirm the shards partition the target set."
);
}
pub(crate) fn detect_missing_files<'a, I>(paths: I, dist: &Path) -> Result<()>
where
I: IntoIterator<Item = &'a Path>,
{
let mut missing: Vec<PathBuf> = Vec::new();
for p in paths {
if p.is_absolute() {
if !p.is_file() {
missing.push(p.to_path_buf());
}
} else if !p.is_file() && !dist.join(p).is_file() {
missing.push(p.to_path_buf());
}
}
if missing.is_empty() {
return Ok(());
}
missing.sort();
let summary = missing
.iter()
.map(|p| p.display().to_string())
.collect::<Vec<_>>()
.join(", ");
anyhow::bail!(
"artifacts manifest references file(s) not present under {}: {summary}. \
The preserved dist is incomplete; re-run \
`anodize check determinism --preserve-dist=<dist>` to repopulate, or \
remove the stale manifest entries before retrying.",
dist.display(),
);
}