1use std::path::Path;
4
5use anyhow::Result;
6use clap::Args;
7
8use crate::Exit;
9use crate::analysis::acknowledgements::{Store, validate_fingerprint};
10
11#[derive(Debug, Args)]
12pub struct AcknowledgeArgs {
13 #[arg(required = true, value_name = "FINGERPRINT")]
15 pub fingerprints: Vec<String>,
16}
17
18pub fn run(args: &AcknowledgeArgs, root: &Path) -> Result<Exit> {
19 for fingerprint in &args.fingerprints {
20 validate_fingerprint(fingerprint)?;
21 }
22 let mut store = Store::load(root)?;
23 let added = args
24 .fingerprints
25 .iter()
26 .filter(|fingerprint| store.insert((*fingerprint).clone()))
27 .count();
28 store.save(root)?;
29 println!(
30 "Acknowledged {added} new finding(s) in {}.",
31 root.join(crate::analysis::acknowledgements::DEFAULT_PATH)
32 .display()
33 );
34 Ok(Exit::Clean)
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40
41 #[test]
42 fn rejects_a_short_or_uppercase_fingerprint_without_writing() {
43 let dir = tempfile::tempdir().expect("tempdir");
44 for value in ["abc".to_owned(), "A".repeat(64)] {
45 let error = run(
46 &AcknowledgeArgs {
47 fingerprints: vec![value],
48 },
49 dir.path(),
50 )
51 .expect_err("invalid fingerprint");
52 assert!(error.to_string().contains("64 lowercase hexadecimal"));
53 }
54 assert!(
55 !dir.path()
56 .join(crate::analysis::acknowledgements::DEFAULT_PATH)
57 .exists()
58 );
59 }
60}