phytrace_sdk/models/domains/
compliance.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use validator::Validate;
6
7#[derive(Debug, Clone, Default, Serialize, Deserialize)]
9pub struct ComplianceDomain {
10 #[serde(skip_serializing_if = "Option::is_none")]
12 pub certifications: Option<Vec<Certification>>,
13
14 #[serde(skip_serializing_if = "Option::is_none")]
16 pub functional_safety: Option<FunctionalSafety>,
17
18 #[serde(skip_serializing_if = "Option::is_none")]
20 pub cybersecurity: Option<Cybersecurity>,
21
22 #[serde(skip_serializing_if = "Option::is_none")]
24 pub regulatory: Option<RegulatoryStatus>,
25}
26
27#[derive(Debug, Clone, Default, Serialize, Deserialize)]
29pub struct Certification {
30 #[serde(skip_serializing_if = "Option::is_none")]
32 pub cert_id: Option<String>,
33
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub standard: Option<String>,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
40 pub certifying_body: Option<String>,
41
42 #[serde(skip_serializing_if = "Option::is_none")]
44 pub issue_date: Option<DateTime<Utc>>,
45
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub expiry_date: Option<DateTime<Utc>>,
49
50 #[serde(skip_serializing_if = "Option::is_none")]
52 pub is_valid: Option<bool>,
53
54 #[serde(skip_serializing_if = "Option::is_none")]
56 pub scope: Option<String>,
57
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub document_ref: Option<String>,
61}
62
63#[derive(Debug, Clone, Default, Serialize, Deserialize, Validate)]
65pub struct FunctionalSafety {
66 #[serde(skip_serializing_if = "Option::is_none")]
68 #[validate(range(min = 1, max = 4))]
69 pub sil_level: Option<u8>,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub performance_level: Option<String>,
74
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub safety_functions: Option<Vec<SafetyFunction>>,
78
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub last_test: Option<DateTime<Utc>>,
82
83 #[serde(skip_serializing_if = "Option::is_none")]
85 #[validate(range(min = 0.0, max = 100.0))]
86 pub diagnostic_coverage: Option<f64>,
87
88 #[serde(skip_serializing_if = "Option::is_none")]
90 #[validate(range(min = 0.0, max = 100.0))]
91 pub safe_failure_fraction: Option<f64>,
92
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub mttf_hours: Option<f64>,
96}
97
98#[derive(Debug, Clone, Default, Serialize, Deserialize)]
100pub struct SafetyFunction {
101 #[serde(skip_serializing_if = "Option::is_none")]
103 pub function_id: Option<String>,
104
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub name: Option<String>,
108
109 #[serde(skip_serializing_if = "Option::is_none")]
111 pub is_active: Option<bool>,
112
113 #[serde(skip_serializing_if = "Option::is_none")]
115 pub status: Option<String>,
116
117 #[serde(skip_serializing_if = "Option::is_none")]
119 pub last_check: Option<DateTime<Utc>>,
120}
121
122#[derive(Debug, Clone, Default, Serialize, Deserialize, Validate)]
124pub struct Cybersecurity {
125 #[serde(skip_serializing_if = "Option::is_none")]
127 #[validate(range(min = 0.0, max = 100.0))]
128 pub security_score: Option<f64>,
129
130 #[serde(skip_serializing_if = "Option::is_none")]
132 #[validate(range(min = 1, max = 4))]
133 pub security_level: Option<u8>,
134
135 #[serde(skip_serializing_if = "Option::is_none")]
137 pub encryption: Option<EncryptionStatus>,
138
139 #[serde(skip_serializing_if = "Option::is_none")]
141 pub authentication: Option<AuthenticationStatus>,
142
143 #[serde(skip_serializing_if = "Option::is_none")]
145 pub network_security: Option<NetworkSecurity>,
146
147 #[serde(skip_serializing_if = "Option::is_none")]
149 pub vulnerabilities: Option<Vec<Vulnerability>>,
150
151 #[serde(skip_serializing_if = "Option::is_none")]
153 pub last_audit: Option<DateTime<Utc>>,
154
155 #[serde(skip_serializing_if = "Option::is_none")]
157 pub last_patch: Option<DateTime<Utc>>,
158}
159
160#[derive(Debug, Clone, Default, Serialize, Deserialize)]
162pub struct EncryptionStatus {
163 #[serde(skip_serializing_if = "Option::is_none")]
165 pub data_at_rest: Option<bool>,
166
167 #[serde(skip_serializing_if = "Option::is_none")]
169 pub data_in_transit: Option<bool>,
170
171 #[serde(skip_serializing_if = "Option::is_none")]
173 pub algorithm: Option<String>,
174
175 #[serde(skip_serializing_if = "Option::is_none")]
177 pub key_strength_bits: Option<u32>,
178}
179
180#[derive(Debug, Clone, Default, Serialize, Deserialize)]
182pub struct AuthenticationStatus {
183 #[serde(skip_serializing_if = "Option::is_none")]
185 pub method: Option<String>,
186
187 #[serde(skip_serializing_if = "Option::is_none")]
189 pub mfa_enabled: Option<bool>,
190
191 #[serde(skip_serializing_if = "Option::is_none")]
193 pub cert_auth: Option<bool>,
194
195 #[serde(skip_serializing_if = "Option::is_none")]
197 pub token_expiry_sec: Option<u64>,
198}
199
200#[derive(Debug, Clone, Default, Serialize, Deserialize)]
202pub struct NetworkSecurity {
203 #[serde(skip_serializing_if = "Option::is_none")]
205 pub firewall_enabled: Option<bool>,
206
207 #[serde(skip_serializing_if = "Option::is_none")]
209 pub vpn_connected: Option<bool>,
210
211 #[serde(skip_serializing_if = "Option::is_none")]
213 pub segmented: Option<bool>,
214
215 #[serde(skip_serializing_if = "Option::is_none")]
217 pub open_ports: Option<Vec<u16>>,
218
219 #[serde(skip_serializing_if = "Option::is_none")]
221 pub ids_enabled: Option<bool>,
222}
223
224#[derive(Debug, Clone, Default, Serialize, Deserialize)]
226pub struct Vulnerability {
227 #[serde(skip_serializing_if = "Option::is_none")]
229 pub cve_id: Option<String>,
230
231 #[serde(skip_serializing_if = "Option::is_none")]
233 pub severity: Option<String>,
234
235 #[serde(skip_serializing_if = "Option::is_none")]
237 pub cvss_score: Option<f64>,
238
239 #[serde(skip_serializing_if = "Option::is_none")]
241 pub component: Option<String>,
242
243 #[serde(skip_serializing_if = "Option::is_none")]
245 pub is_patched: Option<bool>,
246
247 #[serde(skip_serializing_if = "Option::is_none")]
249 pub discovered: Option<DateTime<Utc>>,
250}
251
252#[derive(Debug, Clone, Default, Serialize, Deserialize)]
254pub struct RegulatoryStatus {
255 #[serde(skip_serializing_if = "Option::is_none")]
257 pub region: Option<String>,
258
259 #[serde(skip_serializing_if = "Option::is_none")]
261 pub required_certs: Option<Vec<String>>,
262
263 #[serde(skip_serializing_if = "Option::is_none")]
265 pub status: Option<String>,
266
267 #[serde(skip_serializing_if = "Option::is_none")]
269 pub pending_requirements: Option<Vec<String>>,
270
271 #[serde(skip_serializing_if = "Option::is_none")]
273 pub last_check: Option<DateTime<Utc>>,
274}
275
276impl ComplianceDomain {
277 pub fn with_certification(mut self, cert: Certification) -> Self {
279 let certs = self.certifications.get_or_insert_with(Vec::new);
280 certs.push(cert);
281 self
282 }
283
284 pub fn with_functional_safety(mut self, safety: FunctionalSafety) -> Self {
286 self.functional_safety = Some(safety);
287 self
288 }
289
290 pub fn with_cybersecurity(mut self, cyber: Cybersecurity) -> Self {
292 self.cybersecurity = Some(cyber);
293 self
294 }
295}
296
297impl Certification {
298 pub fn new(standard: impl Into<String>) -> Self {
300 Self {
301 standard: Some(standard.into()),
302 is_valid: Some(true),
303 ..Default::default()
304 }
305 }
306
307 pub fn is_expired(&self) -> bool {
309 self.expiry_date
310 .map(|exp| exp < Utc::now())
311 .unwrap_or(false)
312 }
313}
314
315impl FunctionalSafety {
316 pub fn with_sil(sil_level: u8) -> Self {
318 Self {
319 sil_level: Some(sil_level.clamp(1, 4)),
320 ..Default::default()
321 }
322 }
323}
324
325#[cfg(test)]
326mod tests {
327 use super::*;
328
329 #[test]
330 fn test_certification() {
331 let cert = Certification::new("ISO 13482");
332 assert_eq!(cert.standard, Some("ISO 13482".to_string()));
333 assert_eq!(cert.is_valid, Some(true));
334 }
335
336 #[test]
337 fn test_functional_safety() {
338 let safety = FunctionalSafety::with_sil(3);
339 assert_eq!(safety.sil_level, Some(3));
340 }
341
342 #[test]
343 fn test_compliance_domain() {
344 let compliance = ComplianceDomain::default()
345 .with_certification(Certification::new("CE"))
346 .with_functional_safety(FunctionalSafety::with_sil(2));
347
348 assert_eq!(compliance.certifications.unwrap().len(), 1);
349 assert!(compliance.functional_safety.is_some());
350 }
351}