Skip to main content

provenant/models/
diagnostic.rs

1// SPDX-FileCopyrightText: Provenant contributors
2// SPDX-License-Identifier: Apache-2.0
3
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7pub enum DiagnosticSeverity {
8    /// Purely informational outcome that is not a failure. Provenant
9    /// deliberately took a benign action (for example, declining text detection
10    /// on a file that is binary). Info diagnostics stay auditable in the
11    /// internal model and verbose output, but must not surface in the
12    /// ScanCode-compatible `scan_errors` field or inflate error/warning counts.
13    Info,
14    Warning,
15    Error,
16    Timeout,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20pub struct ScanDiagnostic {
21    pub severity: DiagnosticSeverity,
22    pub message: String,
23}
24
25impl ScanDiagnostic {
26    pub fn info(message: impl Into<String>) -> Self {
27        Self {
28            severity: DiagnosticSeverity::Info,
29            message: message.into(),
30        }
31    }
32
33    pub fn warning(message: impl Into<String>) -> Self {
34        Self {
35            severity: DiagnosticSeverity::Warning,
36            message: message.into(),
37        }
38    }
39
40    pub fn error(message: impl Into<String>) -> Self {
41        Self {
42            severity: DiagnosticSeverity::Error,
43            message: message.into(),
44        }
45    }
46
47    pub fn timeout(message: impl Into<String>) -> Self {
48        Self {
49            severity: DiagnosticSeverity::Timeout,
50            message: message.into(),
51        }
52    }
53
54    pub fn is_timeout(&self) -> bool {
55        self.severity == DiagnosticSeverity::Timeout
56    }
57}
58
59pub fn diagnostics_from_legacy_scan_errors(messages: &[String]) -> Vec<ScanDiagnostic> {
60    messages
61        .iter()
62        .cloned()
63        .map(|message| {
64            if is_legacy_warning_message(&message) {
65                ScanDiagnostic::warning(message)
66            } else {
67                ScanDiagnostic::error(message)
68            }
69        })
70        .collect()
71}
72
73pub fn is_legacy_warning_message(message: &str) -> bool {
74    let first_line = message.lines().next().unwrap_or(message).trim();
75    first_line.starts_with("Maven property ")
76        || first_line.starts_with("Skipping Maven template coordinates")
77        || first_line.starts_with("Circular include detected")
78}