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")]
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")]
140 pub unit_cost: Option<f64>,
141 #[serde(skip_serializing_if = "Option::is_none")]
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")]
239 pub impact_areas: Option<Vec<CADSImpactArea>>,
240 #[serde(skip_serializing_if = "Option::is_none")]
242 pub intended_use: Option<String>,
243 #[serde(skip_serializing_if = "Option::is_none")]
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")]
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")]
319 pub applies_to: Option<CADSValidationProfileAppliesTo>,
320 pub required_checks: Vec<String>,
322}
323
324#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
326#[serde(rename_all = "kebab-case")]
327pub enum CADSBPMNFormat {
328 #[serde(rename = "bpmn20-xml")]
329 Bpmn20Xml,
330 #[serde(rename = "json")]
331 Json,
332}
333
334#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
336pub struct CADSBPMNModel {
337 pub name: String,
339 pub reference: String,
341 pub format: CADSBPMNFormat,
343 #[serde(skip_serializing_if = "Option::is_none")]
345 pub description: Option<String>,
346}
347
348#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
350#[serde(rename_all = "kebab-case")]
351pub enum CADSDMNFormat {
352 #[serde(rename = "dmn13-xml")]
353 Dmn13Xml,
354 #[serde(rename = "json")]
355 Json,
356}
357
358#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
360pub struct CADSDMNModel {
361 pub name: String,
363 pub reference: String,
365 pub format: CADSDMNFormat,
367 #[serde(skip_serializing_if = "Option::is_none")]
369 pub description: Option<String>,
370}
371
372#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
374#[serde(rename_all = "kebab-case")]
375pub enum CADSOpenAPIFormat {
376 #[serde(rename = "openapi-3.0")]
377 Openapi30,
378 #[serde(rename = "openapi-3.1")]
379 Openapi31,
380 #[serde(rename = "swagger-2.0")]
381 Swagger20,
382}
383
384#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
386pub struct CADSOpenAPISpec {
387 pub name: String,
389 pub reference: String,
391 pub format: CADSOpenAPIFormat,
393 #[serde(skip_serializing_if = "Option::is_none")]
395 pub description: Option<String>,
396}
397
398#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
400#[serde(rename_all = "camelCase")]
401pub struct CADSAsset {
402 pub api_version: String,
404 pub kind: CADSKind,
406 pub id: String,
408 pub name: String,
410 pub version: String,
412 pub status: CADSStatus,
414 #[serde(skip_serializing_if = "Option::is_none")]
416 pub domain: Option<String>,
417 #[serde(default, deserialize_with = "deserialize_tags")]
419 pub tags: Vec<Tag>,
420 #[serde(skip_serializing_if = "Option::is_none")]
422 pub description: Option<CADSDescription>,
423 #[serde(skip_serializing_if = "Option::is_none")]
425 pub runtime: Option<CADSRuntime>,
426 #[serde(skip_serializing_if = "Option::is_none")]
428 pub sla: Option<CADSSLA>,
429 #[serde(skip_serializing_if = "Option::is_none")]
431 pub pricing: Option<CADSPricing>,
432 #[serde(skip_serializing_if = "Option::is_none")]
434 pub team: Option<Vec<CADSTeamMember>>,
435 #[serde(skip_serializing_if = "Option::is_none")]
437 pub risk: Option<CADSRisk>,
438 #[serde(skip_serializing_if = "Option::is_none")]
440 pub compliance: Option<CADSCompliance>,
441 #[serde(skip_serializing_if = "Option::is_none")]
443 pub validation_profiles: Option<Vec<CADSValidationProfile>>,
444 #[serde(skip_serializing_if = "Option::is_none")]
446 pub bpmn_models: Option<Vec<CADSBPMNModel>>,
447 #[serde(skip_serializing_if = "Option::is_none")]
449 pub dmn_models: Option<Vec<CADSDMNModel>>,
450 #[serde(skip_serializing_if = "Option::is_none")]
452 pub openapi_specs: Option<Vec<CADSOpenAPISpec>>,
453 #[serde(skip_serializing_if = "Option::is_none")]
455 pub custom_properties: Option<HashMap<String, serde_json::Value>>,
456 #[serde(skip_serializing_if = "Option::is_none")]
458 pub created_at: Option<DateTime<Utc>>,
459 #[serde(skip_serializing_if = "Option::is_none")]
461 pub updated_at: Option<DateTime<Utc>>,
462}
463
464fn deserialize_tags<'de, D>(deserializer: D) -> Result<Vec<Tag>, D::Error>
466where
467 D: serde::Deserializer<'de>,
468{
469 struct TagVisitor;
471
472 impl<'de> serde::de::Visitor<'de> for TagVisitor {
473 type Value = Vec<Tag>;
474
475 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
476 formatter.write_str("a vector of tags (strings or Tag objects)")
477 }
478
479 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
480 where
481 A: serde::de::SeqAccess<'de>,
482 {
483 let mut tags = Vec::new();
484 while let Some(item) = seq.next_element::<serde_json::Value>()? {
485 match item {
486 serde_json::Value::String(s) => {
487 if let Ok(tag) = Tag::from_str(&s) {
489 tags.push(tag);
490 }
491 }
492 _ => {
493 if let serde_json::Value::String(s) = item
495 && let Ok(tag) = Tag::from_str(&s)
496 {
497 tags.push(tag);
498 }
499 }
500 }
501 }
502 Ok(tags)
503 }
504 }
505
506 deserializer.deserialize_seq(TagVisitor)
507}