sbom_tools/parsers/
traits.rs1use crate::model::NormalizedSbom;
7use std::path::Path;
8use thiserror::Error;
9
10#[derive(Error, Debug)]
12pub enum ParseError {
13 #[error("IO error: {0}")]
14 IoError(String),
15
16 #[error("JSON parse error: {0}")]
17 JsonError(String),
18
19 #[error("XML parse error: {0}")]
20 XmlError(String),
21
22 #[error("YAML parse error: {0}")]
23 YamlError(String),
24
25 #[error("Invalid SBOM structure: {0}")]
26 InvalidStructure(String),
27
28 #[error("Unsupported format version: {0}")]
29 UnsupportedVersion(String),
30
31 #[error("Unknown SBOM format: {0}")]
32 UnknownFormat(String),
33
34 #[error("Missing required field: {0}")]
35 MissingField(String),
36
37 #[error("Validation error: {0}")]
38 ValidationError(String),
39}
40
41impl From<std::io::Error> for ParseError {
42 fn from(err: std::io::Error) -> Self {
43 Self::IoError(err.to_string())
44 }
45}
46
47impl From<serde_json::Error> for ParseError {
48 fn from(err: serde_json::Error) -> Self {
49 Self::JsonError(err.to_string())
50 }
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
55pub struct FormatConfidence(f32);
56
57impl FormatConfidence {
58 pub const NONE: Self = Self(0.0);
60 pub const LOW: Self = Self(0.25);
62 pub const MEDIUM: Self = Self(0.5);
64 pub const HIGH: Self = Self(0.75);
66 pub const CERTAIN: Self = Self(1.0);
68
69 #[must_use]
71 pub const fn new(value: f32) -> Self {
72 Self(value.clamp(0.0, 1.0))
73 }
74
75 #[must_use]
77 pub const fn value(&self) -> f32 {
78 self.0
79 }
80
81 #[must_use]
83 pub fn can_parse(&self) -> bool {
84 self.0 >= 0.25
85 }
86}
87
88impl Default for FormatConfidence {
89 fn default() -> Self {
90 Self::NONE
91 }
92}
93
94#[derive(Debug, Clone)]
96pub struct FormatDetection {
97 pub confidence: FormatConfidence,
99 pub variant: Option<String>,
101 pub version: Option<String>,
103 pub warnings: Vec<String>,
105}
106
107impl FormatDetection {
108 #[must_use]
110 pub const fn no_match() -> Self {
111 Self {
112 confidence: FormatConfidence::NONE,
113 variant: None,
114 version: None,
115 warnings: Vec::new(),
116 }
117 }
118
119 #[must_use]
121 pub const fn with_confidence(confidence: FormatConfidence) -> Self {
122 Self {
123 confidence,
124 variant: None,
125 version: None,
126 warnings: Vec::new(),
127 }
128 }
129
130 #[must_use]
132 pub fn variant(mut self, variant: &str) -> Self {
133 self.variant = Some(variant.to_string());
134 self
135 }
136
137 #[must_use]
139 pub fn version(mut self, version: &str) -> Self {
140 self.version = Some(version.to_string());
141 self
142 }
143
144 #[must_use]
146 pub fn warning(mut self, warning: &str) -> Self {
147 self.warnings.push(warning.to_string());
148 self
149 }
150}
151
152pub trait SbomParser {
157 fn parse(&self, path: &Path) -> Result<NormalizedSbom, ParseError> {
159 let content = std::fs::read_to_string(path)?;
160 self.parse_str(&content)
161 }
162
163 fn parse_str(&self, content: &str) -> Result<NormalizedSbom, ParseError>;
165
166 fn supported_versions(&self) -> Vec<&str>;
168
169 fn format_name(&self) -> &str;
171
172 fn detect(&self, content: &str) -> FormatDetection;
177
178 fn can_parse(&self, content: &str) -> bool {
183 self.detect(content).confidence.can_parse()
184 }
185
186 fn confidence(&self, content: &str) -> FormatConfidence {
190 self.detect(content).confidence
191 }
192}