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 ETLPipeline,
20 SourceSystem,
21 DestinationSystem,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
26pub enum CADSStatus {
27 #[serde(rename = "draft")]
28 Draft,
29 #[serde(rename = "validated")]
30 Validated,
31 #[serde(rename = "production")]
32 Production,
33 #[serde(rename = "deprecated")]
34 Deprecated,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
39pub struct CADSDescription {
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub purpose: Option<String>,
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub usage: Option<String>,
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub limitations: Option<String>,
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub external_links: Option<Vec<CADSExternalLink>>,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
56pub struct CADSExternalLink {
57 pub url: String,
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub description: Option<String>,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
66pub struct CADSRuntime {
67 #[serde(skip_serializing_if = "Option::is_none")]
69 pub environment: Option<String>,
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub endpoints: Option<Vec<String>>,
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub container: Option<CADSRuntimeContainer>,
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub resources: Option<CADSRuntimeResources>,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
83pub struct CADSRuntimeContainer {
84 #[serde(skip_serializing_if = "Option::is_none")]
86 pub image: Option<String>,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
91pub struct CADSRuntimeResources {
92 #[serde(skip_serializing_if = "Option::is_none")]
94 pub cpu: Option<String>,
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub memory: Option<String>,
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub gpu: Option<String>,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
105pub struct CADSSLA {
106 #[serde(skip_serializing_if = "Option::is_none")]
108 pub properties: Option<Vec<CADSSLAProperty>>,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
113pub struct CADSSLAProperty {
114 pub element: String,
116 pub value: serde_json::Value,
118 pub unit: String,
120 #[serde(skip_serializing_if = "Option::is_none")]
122 pub driver: Option<String>,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
127pub struct CADSPricing {
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub model: Option<CADSPricingModel>,
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub currency: Option<String>,
134 #[serde(skip_serializing_if = "Option::is_none")]
136 pub unit_cost: Option<f64>,
137 #[serde(skip_serializing_if = "Option::is_none")]
139 pub billing_unit: Option<String>,
140 #[serde(skip_serializing_if = "Option::is_none")]
142 pub notes: Option<String>,
143}
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
147#[serde(rename_all = "snake_case")]
148pub enum CADSPricingModel {
149 PerRequest,
150 PerHour,
151 PerBatch,
152 Subscription,
153 Internal,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
158pub struct CADSTeamMember {
159 pub role: String,
161 pub name: String,
163 #[serde(skip_serializing_if = "Option::is_none")]
165 pub contact: Option<String>,
166}
167
168#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
170pub enum CADSRiskClassification {
171 #[serde(rename = "minimal")]
172 Minimal,
173 #[serde(rename = "low")]
174 Low,
175 #[serde(rename = "medium")]
176 Medium,
177 #[serde(rename = "high")]
178 High,
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
183#[serde(rename_all = "lowercase")]
184pub enum CADSImpactArea {
185 Fairness,
186 Privacy,
187 Safety,
188 Security,
189 Financial,
190 Operational,
191 Reputational,
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
196pub struct CADSRiskAssessment {
197 #[serde(skip_serializing_if = "Option::is_none")]
199 pub methodology: Option<String>,
200 #[serde(skip_serializing_if = "Option::is_none")]
202 pub date: Option<String>,
203 #[serde(skip_serializing_if = "Option::is_none")]
205 pub assessor: Option<String>,
206}
207
208#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
210pub struct CADSRiskMitigation {
211 pub description: String,
213 pub status: CADSMitigationStatus,
215}
216
217#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
219#[serde(rename_all = "lowercase")]
220pub enum CADSMitigationStatus {
221 Planned,
222 Implemented,
223 Verified,
224}
225
226#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
228pub struct CADSRisk {
229 #[serde(skip_serializing_if = "Option::is_none")]
231 pub classification: Option<CADSRiskClassification>,
232 #[serde(skip_serializing_if = "Option::is_none")]
234 pub impact_areas: Option<Vec<CADSImpactArea>>,
235 #[serde(skip_serializing_if = "Option::is_none")]
237 pub intended_use: Option<String>,
238 #[serde(skip_serializing_if = "Option::is_none")]
240 pub out_of_scope_use: Option<String>,
241 #[serde(skip_serializing_if = "Option::is_none")]
243 pub assessment: Option<CADSRiskAssessment>,
244 #[serde(skip_serializing_if = "Option::is_none")]
246 pub mitigations: Option<Vec<CADSRiskMitigation>>,
247}
248
249#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
251pub struct CADSComplianceFramework {
252 pub name: String,
254 #[serde(skip_serializing_if = "Option::is_none")]
256 pub category: Option<String>,
257 pub status: CADSComplianceStatus,
259}
260
261#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
263#[serde(rename_all = "snake_case")]
264pub enum CADSComplianceStatus {
265 NotApplicable,
266 Assessed,
267 Compliant,
268 NonCompliant,
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
273pub struct CADSComplianceControl {
274 pub id: String,
276 pub description: String,
278 #[serde(skip_serializing_if = "Option::is_none")]
280 pub evidence: Option<String>,
281}
282
283#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
285pub struct CADSCompliance {
286 #[serde(skip_serializing_if = "Option::is_none")]
288 pub frameworks: Option<Vec<CADSComplianceFramework>>,
289 #[serde(skip_serializing_if = "Option::is_none")]
291 pub controls: Option<Vec<CADSComplianceControl>>,
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
296pub struct CADSValidationProfileAppliesTo {
297 #[serde(skip_serializing_if = "Option::is_none")]
299 pub kind: Option<String>,
300 #[serde(skip_serializing_if = "Option::is_none")]
302 pub risk_classification: Option<String>,
303}
304
305#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
307pub struct CADSValidationProfile {
308 pub name: String,
310 #[serde(skip_serializing_if = "Option::is_none")]
312 pub applies_to: Option<CADSValidationProfileAppliesTo>,
313 pub required_checks: Vec<String>,
315}
316
317#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
319#[serde(rename_all = "kebab-case")]
320pub enum CADSBPMNFormat {
321 #[serde(rename = "bpmn20-xml")]
322 Bpmn20Xml,
323 #[serde(rename = "json")]
324 Json,
325}
326
327#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
329pub struct CADSBPMNModel {
330 pub name: String,
332 pub reference: String,
334 pub format: CADSBPMNFormat,
336 #[serde(skip_serializing_if = "Option::is_none")]
338 pub description: Option<String>,
339}
340
341#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
343#[serde(rename_all = "kebab-case")]
344pub enum CADSDMNFormat {
345 #[serde(rename = "dmn13-xml")]
346 Dmn13Xml,
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
351pub struct CADSDMNModel {
352 pub name: String,
354 pub reference: String,
356 pub format: CADSDMNFormat,
358 #[serde(skip_serializing_if = "Option::is_none")]
360 pub description: Option<String>,
361}
362
363#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
365#[serde(rename_all = "kebab-case")]
366pub enum CADSOpenAPIFormat {
367 #[serde(rename = "openapi-311-yaml")]
368 Openapi311Yaml,
369 #[serde(rename = "openapi-311-json")]
370 Openapi311Json,
371}
372
373#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
375pub struct CADSOpenAPISpec {
376 pub name: String,
378 pub reference: String,
380 pub format: CADSOpenAPIFormat,
382 #[serde(skip_serializing_if = "Option::is_none")]
384 pub description: Option<String>,
385}
386
387#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
389pub struct CADSAsset {
390 pub api_version: String,
392 pub kind: CADSKind,
394 pub id: String,
396 pub name: String,
398 pub version: String,
400 pub status: CADSStatus,
402 #[serde(skip_serializing_if = "Option::is_none")]
404 pub domain: Option<String>,
405 #[serde(default, deserialize_with = "deserialize_tags")]
407 pub tags: Vec<Tag>,
408 #[serde(skip_serializing_if = "Option::is_none")]
410 pub description: Option<CADSDescription>,
411 #[serde(skip_serializing_if = "Option::is_none")]
413 pub runtime: Option<CADSRuntime>,
414 #[serde(skip_serializing_if = "Option::is_none")]
416 pub sla: Option<CADSSLA>,
417 #[serde(skip_serializing_if = "Option::is_none")]
419 pub pricing: Option<CADSPricing>,
420 #[serde(skip_serializing_if = "Option::is_none")]
422 pub team: Option<Vec<CADSTeamMember>>,
423 #[serde(skip_serializing_if = "Option::is_none")]
425 pub risk: Option<CADSRisk>,
426 #[serde(skip_serializing_if = "Option::is_none")]
428 pub compliance: Option<CADSCompliance>,
429 #[serde(skip_serializing_if = "Option::is_none")]
431 pub validation_profiles: Option<Vec<CADSValidationProfile>>,
432 #[serde(skip_serializing_if = "Option::is_none")]
434 pub bpmn_models: Option<Vec<CADSBPMNModel>>,
435 #[serde(skip_serializing_if = "Option::is_none")]
437 pub dmn_models: Option<Vec<CADSDMNModel>>,
438 #[serde(skip_serializing_if = "Option::is_none")]
440 pub openapi_specs: Option<Vec<CADSOpenAPISpec>>,
441 #[serde(skip_serializing_if = "Option::is_none")]
443 pub custom_properties: Option<HashMap<String, serde_json::Value>>,
444 #[serde(skip_serializing_if = "Option::is_none")]
446 pub created_at: Option<DateTime<Utc>>,
447 #[serde(skip_serializing_if = "Option::is_none")]
449 pub updated_at: Option<DateTime<Utc>>,
450}
451
452fn deserialize_tags<'de, D>(deserializer: D) -> Result<Vec<Tag>, D::Error>
454where
455 D: serde::Deserializer<'de>,
456{
457 struct TagVisitor;
459
460 impl<'de> serde::de::Visitor<'de> for TagVisitor {
461 type Value = Vec<Tag>;
462
463 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
464 formatter.write_str("a vector of tags (strings or Tag objects)")
465 }
466
467 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
468 where
469 A: serde::de::SeqAccess<'de>,
470 {
471 let mut tags = Vec::new();
472 while let Some(item) = seq.next_element::<serde_json::Value>()? {
473 match item {
474 serde_json::Value::String(s) => {
475 if let Ok(tag) = Tag::from_str(&s) {
477 tags.push(tag);
478 }
479 }
480 _ => {
481 if let serde_json::Value::String(s) = item
483 && let Ok(tag) = Tag::from_str(&s)
484 {
485 tags.push(tag);
486 }
487 }
488 }
489 }
490 Ok(tags)
491 }
492 }
493
494 deserializer.deserialize_seq(TagVisitor)
495}