1use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::str::FromStr;
9
10use super::tag::Tag;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(rename_all = "PascalCase")]
15pub enum CADSKind {
16 AIModel,
17 MLPipeline,
18 Application,
19 DataPipeline,
20 ETLProcess,
21 ETLPipeline,
22 SourceSystem,
23 DestinationSystem,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
28pub enum CADSStatus {
29 #[serde(rename = "draft")]
30 Draft,
31 #[serde(rename = "validated")]
32 Validated,
33 #[serde(rename = "production")]
34 Production,
35 #[serde(rename = "deprecated")]
36 Deprecated,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
41#[serde(rename_all = "camelCase")]
42pub struct CADSDescription {
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub purpose: Option<String>,
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub usage: Option<String>,
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub limitations: Option<String>,
52 #[serde(skip_serializing_if = "Option::is_none", alias = "external_links")]
54 pub external_links: Option<Vec<CADSExternalLink>>,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
59pub struct CADSExternalLink {
60 pub url: String,
62 #[serde(skip_serializing_if = "Option::is_none")]
64 pub description: Option<String>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
69pub struct CADSRuntime {
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub environment: Option<String>,
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub endpoints: Option<Vec<String>>,
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub container: Option<CADSRuntimeContainer>,
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub resources: Option<CADSRuntimeResources>,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
86pub struct CADSRuntimeContainer {
87 #[serde(skip_serializing_if = "Option::is_none")]
89 pub image: Option<String>,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
94pub struct CADSRuntimeResources {
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub cpu: Option<String>,
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub memory: Option<String>,
101 #[serde(skip_serializing_if = "Option::is_none")]
103 pub gpu: Option<String>,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
108pub struct CADSSLA {
109 #[serde(skip_serializing_if = "Option::is_none")]
111 pub properties: Option<Vec<CADSSLAProperty>>,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
116pub struct CADSSLAProperty {
117 pub element: String,
119 pub value: serde_json::Value,
121 pub unit: String,
123 #[serde(skip_serializing_if = "Option::is_none")]
125 pub driver: Option<String>,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
130#[serde(rename_all = "camelCase")]
131pub struct CADSPricing {
132 #[serde(skip_serializing_if = "Option::is_none")]
134 pub model: Option<CADSPricingModel>,
135 #[serde(skip_serializing_if = "Option::is_none")]
137 pub currency: Option<String>,
138 #[serde(skip_serializing_if = "Option::is_none", alias = "unit_cost")]
140 pub unit_cost: Option<f64>,
141 #[serde(skip_serializing_if = "Option::is_none", alias = "billing_unit")]
143 pub billing_unit: Option<String>,
144 #[serde(skip_serializing_if = "Option::is_none")]
146 pub notes: Option<String>,
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
151#[serde(rename_all = "snake_case")]
152pub enum CADSPricingModel {
153 PerRequest,
154 PerHour,
155 PerBatch,
156 Subscription,
157 Internal,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
162pub struct CADSTeamMember {
163 pub role: String,
165 pub name: String,
167 #[serde(skip_serializing_if = "Option::is_none")]
169 pub contact: Option<String>,
170}
171
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
174pub enum CADSRiskClassification {
175 #[serde(rename = "minimal")]
176 Minimal,
177 #[serde(rename = "low")]
178 Low,
179 #[serde(rename = "medium")]
180 Medium,
181 #[serde(rename = "high")]
182 High,
183}
184
185#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
187#[serde(rename_all = "lowercase")]
188pub enum CADSImpactArea {
189 Fairness,
190 Privacy,
191 Safety,
192 Security,
193 Financial,
194 Operational,
195 Reputational,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
200pub struct CADSRiskAssessment {
201 #[serde(skip_serializing_if = "Option::is_none")]
203 pub methodology: Option<String>,
204 #[serde(skip_serializing_if = "Option::is_none")]
206 pub date: Option<String>,
207 #[serde(skip_serializing_if = "Option::is_none")]
209 pub assessor: Option<String>,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
214pub struct CADSRiskMitigation {
215 pub description: String,
217 pub status: CADSMitigationStatus,
219}
220
221#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
223#[serde(rename_all = "lowercase")]
224pub enum CADSMitigationStatus {
225 Planned,
226 Implemented,
227 Verified,
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
232#[serde(rename_all = "camelCase")]
233pub struct CADSRisk {
234 #[serde(skip_serializing_if = "Option::is_none")]
236 pub classification: Option<CADSRiskClassification>,
237 #[serde(skip_serializing_if = "Option::is_none", alias = "impact_areas")]
239 pub impact_areas: Option<Vec<CADSImpactArea>>,
240 #[serde(skip_serializing_if = "Option::is_none", alias = "intended_use")]
242 pub intended_use: Option<String>,
243 #[serde(skip_serializing_if = "Option::is_none", alias = "out_of_scope_use")]
245 pub out_of_scope_use: Option<String>,
246 #[serde(skip_serializing_if = "Option::is_none")]
248 pub assessment: Option<CADSRiskAssessment>,
249 #[serde(skip_serializing_if = "Option::is_none")]
251 pub mitigations: Option<Vec<CADSRiskMitigation>>,
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
256pub struct CADSComplianceFramework {
257 pub name: String,
259 #[serde(skip_serializing_if = "Option::is_none")]
261 pub category: Option<String>,
262 pub status: CADSComplianceStatus,
264}
265
266#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
268#[serde(rename_all = "snake_case")]
269pub enum CADSComplianceStatus {
270 NotApplicable,
271 Assessed,
272 Compliant,
273 NonCompliant,
274}
275
276#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
278pub struct CADSComplianceControl {
279 pub id: String,
281 pub description: String,
283 #[serde(skip_serializing_if = "Option::is_none")]
285 pub evidence: Option<String>,
286}
287
288#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
290pub struct CADSCompliance {
291 #[serde(skip_serializing_if = "Option::is_none")]
293 pub frameworks: Option<Vec<CADSComplianceFramework>>,
294 #[serde(skip_serializing_if = "Option::is_none")]
296 pub controls: Option<Vec<CADSComplianceControl>>,
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
301#[serde(rename_all = "camelCase")]
302pub struct CADSValidationProfileAppliesTo {
303 #[serde(skip_serializing_if = "Option::is_none")]
305 pub kind: Option<String>,
306 #[serde(skip_serializing_if = "Option::is_none", alias = "risk_classification")]
308 pub risk_classification: Option<String>,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
313#[serde(rename_all = "camelCase")]
314pub struct CADSValidationProfile {
315 pub name: String,
317 #[serde(skip_serializing_if = "Option::is_none", alias = "applies_to")]
319 pub applies_to: Option<CADSValidationProfileAppliesTo>,
320 #[serde(alias = "required_checks")]
322 pub required_checks: Vec<String>,
323}
324
325#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
327#[serde(rename_all = "kebab-case")]
328pub enum CADSBPMNFormat {
329 #[serde(rename = "bpmn20-xml")]
330 Bpmn20Xml,
331 #[serde(rename = "json")]
332 Json,
333}
334
335#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
337pub struct CADSBPMNModel {
338 pub name: String,
340 pub reference: String,
342 pub format: CADSBPMNFormat,
344 #[serde(skip_serializing_if = "Option::is_none")]
346 pub description: Option<String>,
347}
348
349#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
351#[serde(rename_all = "kebab-case")]
352pub enum CADSDMNFormat {
353 #[serde(rename = "dmn13-xml")]
354 Dmn13Xml,
355 #[serde(rename = "json")]
356 Json,
357}
358
359#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
361pub struct CADSDMNModel {
362 pub name: String,
364 pub reference: String,
366 pub format: CADSDMNFormat,
368 #[serde(skip_serializing_if = "Option::is_none")]
370 pub description: Option<String>,
371}
372
373#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
375#[serde(rename_all = "kebab-case")]
376pub enum CADSOpenAPIFormat {
377 #[serde(rename = "openapi-3.0")]
378 Openapi30,
379 #[serde(rename = "openapi-3.1")]
380 Openapi31,
381 #[serde(rename = "swagger-2.0")]
382 Swagger20,
383}
384
385#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
387pub struct CADSOpenAPISpec {
388 pub name: String,
390 pub reference: String,
392 pub format: CADSOpenAPIFormat,
394 #[serde(skip_serializing_if = "Option::is_none")]
396 pub description: Option<String>,
397}
398
399#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
401#[serde(rename_all = "camelCase")]
402pub struct CADSAsset {
403 #[serde(alias = "api_version")]
405 pub api_version: String,
406 pub kind: CADSKind,
408 pub id: String,
410 pub name: String,
412 pub version: String,
414 pub status: CADSStatus,
416 #[serde(skip_serializing_if = "Option::is_none")]
418 pub domain: Option<String>,
419 #[serde(default, deserialize_with = "deserialize_tags")]
421 pub tags: Vec<Tag>,
422 #[serde(skip_serializing_if = "Option::is_none")]
424 pub description: Option<CADSDescription>,
425 #[serde(skip_serializing_if = "Option::is_none")]
427 pub runtime: Option<CADSRuntime>,
428 #[serde(skip_serializing_if = "Option::is_none")]
430 pub sla: Option<CADSSLA>,
431 #[serde(skip_serializing_if = "Option::is_none")]
433 pub pricing: Option<CADSPricing>,
434 #[serde(skip_serializing_if = "Option::is_none")]
436 pub team: Option<Vec<CADSTeamMember>>,
437 #[serde(skip_serializing_if = "Option::is_none")]
439 pub risk: Option<CADSRisk>,
440 #[serde(skip_serializing_if = "Option::is_none")]
442 pub compliance: Option<CADSCompliance>,
443 #[serde(skip_serializing_if = "Option::is_none", alias = "validation_profiles")]
445 pub validation_profiles: Option<Vec<CADSValidationProfile>>,
446 #[serde(skip_serializing_if = "Option::is_none", alias = "bpmn_models")]
448 pub bpmn_models: Option<Vec<CADSBPMNModel>>,
449 #[serde(skip_serializing_if = "Option::is_none", alias = "dmn_models")]
451 pub dmn_models: Option<Vec<CADSDMNModel>>,
452 #[serde(skip_serializing_if = "Option::is_none", alias = "openapi_specs")]
454 pub openapi_specs: Option<Vec<CADSOpenAPISpec>>,
455 #[serde(skip_serializing_if = "Option::is_none", alias = "custom_properties")]
457 pub custom_properties: Option<HashMap<String, serde_json::Value>>,
458 #[serde(skip_serializing_if = "Option::is_none", alias = "created_at")]
460 pub created_at: Option<DateTime<Utc>>,
461 #[serde(skip_serializing_if = "Option::is_none", alias = "updated_at")]
463 pub updated_at: Option<DateTime<Utc>>,
464}
465
466fn deserialize_tags<'de, D>(deserializer: D) -> Result<Vec<Tag>, D::Error>
468where
469 D: serde::Deserializer<'de>,
470{
471 struct TagVisitor;
473
474 impl<'de> serde::de::Visitor<'de> for TagVisitor {
475 type Value = Vec<Tag>;
476
477 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
478 formatter.write_str("a vector of tags (strings or Tag objects)")
479 }
480
481 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
482 where
483 A: serde::de::SeqAccess<'de>,
484 {
485 let mut tags = Vec::new();
486 while let Some(item) = seq.next_element::<serde_json::Value>()? {
487 match item {
488 serde_json::Value::String(s) => {
489 if let Ok(tag) = Tag::from_str(&s) {
491 tags.push(tag);
492 }
493 }
494 _ => {
495 if let serde_json::Value::String(s) = item
497 && let Ok(tag) = Tag::from_str(&s)
498 {
499 tags.push(tag);
500 }
501 }
502 }
503 }
504 Ok(tags)
505 }
506 }
507
508 deserializer.deserialize_seq(TagVisitor)
509}