//! Ingest a directory of OSV JSON records and report what came out.
//!
//! Used to validate the parser against the real feed rather than against
//! fixtures. Fixtures test the shape you expected; the feed tests the
//! shape that ships.
//!
//! cargo run -p hound-defs --example ingest-osv --
[lookup-name]
use hound_defs::{osv, Index};
fn main() {
let mut args = std::env::args().skip(1);
let Some(dir) = args.next() else {
eprintln!("usage: ingest-osv [name-to-look-up]");
std::process::exit(2);
};
let probe = args.next();
let mut files = 0usize;
let mut unparsed = 0usize;
let mut indicators = Vec::new();
let mut malicious = 0usize;
for entry in std::fs::read_dir(&dir).expect("readable directory").flatten() {
let path = entry.path();
if path.extension().is_none_or(|e| e != "json") {
continue;
}
files += 1;
let Ok(text) = std::fs::read_to_string(&path) else { continue };
let parsed = osv::parse_record(&text);
if parsed.is_empty() {
unparsed += 1;
continue;
}
if parsed[0].id.starts_with("MAL-") {
malicious += parsed.len();
}
indicators.extend(parsed);
}
let mut ecosystems: std::collections::BTreeMap = Default::default();
let mut all_versions = 0usize;
for i in &indicators {
*ecosystems.entry(i.ecosystem.clone()).or_default() += 1;
if i.versions == osv::Versions::All {
all_versions += 1;
}
}
println!("files read {files}");
println!("yielded nothing {unparsed}");
println!("indicators {}", indicators.len());
println!(" of those MAL- {malicious}");
println!(" all-versions {all_versions}");
println!("ecosystems {ecosystems:?}");
let index = Index::build(indicators);
println!("index keys {}", index.len());
println!("filter {:.1} KB", index.filter_bytes() as f64 / 1024.0);
if let Some(name) = probe {
for eco in ["cratesio", "npm", "pypi"] {
match index.any_version(eco, &name) {
Some(hit) => println!("\nLOOKUP {eco}:{name} -> {} — {}", hit.id, hit.summary),
None => println!("\nLOOKUP {eco}:{name} -> clean"),
}
}
}
}