Skip to main content

drep/cli/
acknowledge.rs

1//! Record source-sensitive LLM findings that have been reviewed and rejected.
2
3use 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    /// Finding fingerprints printed by `drep check`.
14    #[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}