1use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::str::FromStr;
9use uuid::Uuid;
10
11use super::tag::Tag;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(rename_all = "PascalCase")]
16pub enum CADSKind {
17 AIModel,
18 MLPipeline,
19 Application,
20 DataPipeline,
21 ETLProcess,
22 ETLPipeline,
23 SourceSystem,
24 DestinationSystem,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
29pub enum CADSStatus {
30 #[serde(rename = "draft")]
31 Draft,
32 #[serde(rename = "validated")]
33 Validated,
34 #[serde(rename = "production")]
35 Production,
36 #[serde(rename = "deprecated")]
37 Deprecated,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
42#[serde(rename_all = "camelCase")]
43pub struct CADSDescription {
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub purpose: Option<String>,
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub usage: Option<String>,
50 #[serde(skip_serializing_if = "Option::is_none")]
52 pub limitations: Option<String>,
53 #[serde(skip_serializing_if = "Option::is_none", alias = "external_links")]
55 pub external_links: Option<Vec<CADSExternalLink>>,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
60pub struct CADSExternalLink {
61 pub url: String,
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub description: Option<String>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
70pub struct CADSRuntime {
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub environment: Option<String>,
74 #[serde(skip_serializing_if = "Option::is_none")]
76 pub endpoints: Option<Vec<String>>,
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub container: Option<CADSRuntimeContainer>,
80 #[serde(skip_serializing_if = "Option::is_none")]
82 pub resources: Option<CADSRuntimeResources>,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
87pub struct CADSRuntimeContainer {
88 #[serde(skip_serializing_if = "Option::is_none")]
90 pub image: Option<String>,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
95pub struct CADSRuntimeResources {
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub cpu: Option<String>,
99 #[serde(skip_serializing_if = "Option::is_none")]
101 pub memory: Option<String>,
102 #[serde(skip_serializing_if = "Option::is_none")]
104 pub gpu: Option<String>,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
109pub struct CADSSLA {
110 #[serde(skip_serializing_if = "Option::is_none")]
112 pub properties: Option<Vec<CADSSLAProperty>>,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
117pub struct CADSSLAProperty {
118 pub element: String,
120 pub value: serde_json::Value,
122 pub unit: String,
124 #[serde(skip_serializing_if = "Option::is_none")]
126 pub driver: Option<String>,
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
131#[serde(rename_all = "camelCase")]
132pub struct CADSPricing {
133 #[serde(skip_serializing_if = "Option::is_none")]
135 pub model: Option<CADSPricingModel>,
136 #[serde(skip_serializing_if = "Option::is_none")]
138 pub currency: Option<String>,
139 #[serde(skip_serializing_if = "Option::is_none", alias = "unit_cost")]
141 pub unit_cost: Option<f64>,
142 #[serde(skip_serializing_if = "Option::is_none", alias = "billing_unit")]
144 pub billing_unit: Option<String>,
145 #[serde(skip_serializing_if = "Option::is_none")]
147 pub notes: Option<String>,
148}
149
150#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
152#[serde(rename_all = "snake_case")]
153pub enum CADSPricingModel {
154 PerRequest,
155 PerHour,
156 PerBatch,
157 Subscription,
158 Internal,
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
163pub struct CADSTeamMember {
164 pub role: String,
166 pub name: String,
168 #[serde(skip_serializing_if = "Option::is_none")]
170 pub contact: Option<String>,
171}
172
173#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
175pub enum CADSRiskClassification {
176 #[serde(rename = "minimal")]
177 Minimal,
178 #[serde(rename = "low")]
179 Low,
180 #[serde(rename = "medium")]
181 Medium,
182 #[serde(rename = "high")]
183 High,
184}
185
186#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
188#[serde(rename_all = "lowercase")]
189pub enum CADSImpactArea {
190 Fairness,
191 Privacy,
192 Safety,
193 Security,
194 Financial,
195 Operational,
196 Reputational,
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
201pub struct CADSRiskAssessment {
202 #[serde(skip_serializing_if = "Option::is_none")]
204 pub methodology: Option<String>,
205 #[serde(skip_serializing_if = "Option::is_none")]
207 pub date: Option<String>,
208 #[serde(skip_serializing_if = "Option::is_none")]
210 pub assessor: Option<String>,
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
215pub struct CADSRiskMitigation {
216 pub description: String,
218 pub status: CADSMitigationStatus,
220}
221
222#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
224#[serde(rename_all = "lowercase")]
225pub enum CADSMitigationStatus {
226 Planned,
227 Implemented,
228 Verified,
229}
230
231#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
233#[serde(rename_all = "camelCase")]
234pub struct CADSRisk {
235 #[serde(skip_serializing_if = "Option::is_none")]
237 pub classification: Option<CADSRiskClassification>,
238 #[serde(skip_serializing_if = "Option::is_none", alias = "impact_areas")]
240 pub impact_areas: Option<Vec<CADSImpactArea>>,
241 #[serde(skip_serializing_if = "Option::is_none", alias = "intended_use")]
243 pub intended_use: Option<String>,
244 #[serde(skip_serializing_if = "Option::is_none", alias = "out_of_scope_use")]
246 pub out_of_scope_use: Option<String>,
247 #[serde(skip_serializing_if = "Option::is_none")]
249 pub assessment: Option<CADSRiskAssessment>,
250 #[serde(skip_serializing_if = "Option::is_none")]
252 pub mitigations: Option<Vec<CADSRiskMitigation>>,
253}
254
255#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
257pub struct CADSComplianceFramework {
258 pub name: String,
260 #[serde(skip_serializing_if = "Option::is_none")]
262 pub category: Option<String>,
263 pub status: CADSComplianceStatus,
265}
266
267#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
269#[serde(rename_all = "snake_case")]
270pub enum CADSComplianceStatus {
271 NotApplicable,
272 Assessed,
273 Compliant,
274 NonCompliant,
275}
276
277#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
279pub struct CADSComplianceControl {
280 pub id: String,
282 pub description: String,
284 #[serde(skip_serializing_if = "Option::is_none")]
286 pub evidence: Option<String>,
287}
288
289#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
291pub struct CADSCompliance {
292 #[serde(skip_serializing_if = "Option::is_none")]
294 pub frameworks: Option<Vec<CADSComplianceFramework>>,
295 #[serde(skip_serializing_if = "Option::is_none")]
297 pub controls: Option<Vec<CADSComplianceControl>>,
298}
299
300#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
302#[serde(rename_all = "camelCase")]
303pub struct CADSValidationProfileAppliesTo {
304 #[serde(skip_serializing_if = "Option::is_none")]
306 pub kind: Option<String>,
307 #[serde(skip_serializing_if = "Option::is_none", alias = "risk_classification")]
309 pub risk_classification: Option<String>,
310}
311
312#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
314#[serde(rename_all = "camelCase")]
315pub struct CADSValidationProfile {
316 pub name: String,
318 #[serde(skip_serializing_if = "Option::is_none", alias = "applies_to")]
320 pub applies_to: Option<CADSValidationProfileAppliesTo>,
321 #[serde(alias = "required_checks")]
323 pub required_checks: Vec<String>,
324}
325
326#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
328#[serde(rename_all = "kebab-case")]
329pub enum CADSBPMNFormat {
330 #[serde(rename = "bpmn20-xml")]
331 Bpmn20Xml,
332 #[serde(rename = "json")]
333 Json,
334}
335
336#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
338pub struct CADSBPMNModel {
339 pub name: String,
341 pub reference: String,
343 pub format: CADSBPMNFormat,
345 #[serde(skip_serializing_if = "Option::is_none")]
347 pub description: Option<String>,
348}
349
350#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
352#[serde(rename_all = "kebab-case")]
353pub enum CADSDMNFormat {
354 #[serde(rename = "dmn13-xml")]
355 Dmn13Xml,
356 #[serde(rename = "json")]
357 Json,
358}
359
360#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
362pub struct CADSDMNModel {
363 pub name: String,
365 pub reference: String,
367 pub format: CADSDMNFormat,
369 #[serde(skip_serializing_if = "Option::is_none")]
371 pub description: Option<String>,
372}
373
374#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
376#[serde(rename_all = "kebab-case")]
377pub enum CADSOpenAPIFormat {
378 #[serde(rename = "openapi-3.0")]
379 Openapi30,
380 #[serde(rename = "openapi-3.1")]
381 Openapi31,
382 #[serde(rename = "swagger-2.0")]
383 Swagger20,
384}
385
386#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
388pub struct CADSOpenAPISpec {
389 pub name: String,
391 pub reference: String,
393 pub format: CADSOpenAPIFormat,
395 #[serde(skip_serializing_if = "Option::is_none")]
397 pub description: Option<String>,
398}
399
400#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
402#[serde(rename_all = "camelCase")]
403pub struct CADSAsset {
404 #[serde(alias = "api_version")]
406 pub api_version: String,
407 pub kind: CADSKind,
409 pub id: String,
411 pub name: String,
413 pub version: String,
415 pub status: CADSStatus,
417 #[serde(skip_serializing_if = "Option::is_none")]
419 pub domain: Option<String>,
420 #[serde(skip_serializing_if = "Option::is_none", alias = "domain_id")]
422 pub domain_id: Option<Uuid>,
423 #[serde(
425 default,
426 skip_serializing_if = "Vec::is_empty",
427 deserialize_with = "deserialize_tags"
428 )]
429 pub tags: Vec<Tag>,
430 #[serde(skip_serializing_if = "Option::is_none")]
432 pub description: Option<CADSDescription>,
433 #[serde(skip_serializing_if = "Option::is_none")]
435 pub runtime: Option<CADSRuntime>,
436 #[serde(skip_serializing_if = "Option::is_none")]
438 pub sla: Option<CADSSLA>,
439 #[serde(skip_serializing_if = "Option::is_none")]
441 pub pricing: Option<CADSPricing>,
442 #[serde(skip_serializing_if = "Option::is_none")]
444 pub team: Option<Vec<CADSTeamMember>>,
445 #[serde(skip_serializing_if = "Option::is_none")]
447 pub risk: Option<CADSRisk>,
448 #[serde(skip_serializing_if = "Option::is_none")]
450 pub compliance: Option<CADSCompliance>,
451 #[serde(skip_serializing_if = "Option::is_none", alias = "validation_profiles")]
453 pub validation_profiles: Option<Vec<CADSValidationProfile>>,
454 #[serde(skip_serializing_if = "Option::is_none", alias = "bpmn_models")]
456 pub bpmn_models: Option<Vec<CADSBPMNModel>>,
457 #[serde(skip_serializing_if = "Option::is_none", alias = "dmn_models")]
459 pub dmn_models: Option<Vec<CADSDMNModel>>,
460 #[serde(skip_serializing_if = "Option::is_none", alias = "openapi_specs")]
462 pub openapi_specs: Option<Vec<CADSOpenAPISpec>>,
463 #[serde(skip_serializing_if = "Option::is_none", alias = "custom_properties")]
465 pub custom_properties: Option<HashMap<String, serde_json::Value>>,
466 #[serde(skip_serializing_if = "Option::is_none", alias = "created_at")]
468 pub created_at: Option<DateTime<Utc>>,
469 #[serde(skip_serializing_if = "Option::is_none", alias = "updated_at")]
471 pub updated_at: Option<DateTime<Utc>>,
472}
473
474fn deserialize_tags<'de, D>(deserializer: D) -> Result<Vec<Tag>, D::Error>
476where
477 D: serde::Deserializer<'de>,
478{
479 struct TagVisitor;
481
482 impl<'de> serde::de::Visitor<'de> for TagVisitor {
483 type Value = Vec<Tag>;
484
485 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
486 formatter.write_str("a vector of tags (strings or Tag objects)")
487 }
488
489 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
490 where
491 A: serde::de::SeqAccess<'de>,
492 {
493 let mut tags = Vec::new();
494 while let Some(item) = seq.next_element::<serde_json::Value>()? {
495 match item {
496 serde_json::Value::String(s) => {
497 if let Ok(tag) = Tag::from_str(&s) {
499 tags.push(tag);
500 }
501 }
502 _ => {
503 if let serde_json::Value::String(s) = item
505 && let Ok(tag) = Tag::from_str(&s)
506 {
507 tags.push(tag);
508 }
509 }
510 }
511 }
512 Ok(tags)
513 }
514 }
515
516 deserializer.deserialize_seq(TagVisitor)
517}