use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::str::FromStr;
use uuid::Uuid;
use super::tag::Tag;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub enum CADSKind {
AIModel,
MLPipeline,
Application,
DataPipeline,
ETLProcess,
ETLPipeline,
SourceSystem,
DestinationSystem,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum CADSStatus {
#[serde(rename = "draft")]
Draft,
#[serde(rename = "validated")]
Validated,
#[serde(rename = "production")]
Production,
#[serde(rename = "deprecated")]
Deprecated,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CADSDescription {
#[serde(skip_serializing_if = "Option::is_none")]
pub purpose: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub usage: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limitations: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", alias = "external_links")]
pub external_links: Option<Vec<CADSExternalLink>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CADSExternalLink {
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CADSRuntime {
#[serde(skip_serializing_if = "Option::is_none")]
pub environment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub endpoints: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub container: Option<CADSRuntimeContainer>,
#[serde(skip_serializing_if = "Option::is_none")]
pub resources: Option<CADSRuntimeResources>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CADSRuntimeContainer {
#[serde(skip_serializing_if = "Option::is_none")]
pub image: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CADSRuntimeResources {
#[serde(skip_serializing_if = "Option::is_none")]
pub cpu: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub memory: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub gpu: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CADSSLA {
#[serde(skip_serializing_if = "Option::is_none")]
pub properties: Option<Vec<CADSSLAProperty>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CADSSLAProperty {
pub element: String,
pub value: serde_json::Value,
pub unit: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub driver: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CADSPricing {
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<CADSPricingModel>,
#[serde(skip_serializing_if = "Option::is_none")]
pub currency: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", alias = "unit_cost")]
pub unit_cost: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none", alias = "billing_unit")]
pub billing_unit: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub notes: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CADSPricingModel {
PerRequest,
PerHour,
PerBatch,
Subscription,
Internal,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CADSTeamMember {
pub role: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub contact: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum CADSRiskClassification {
#[serde(rename = "minimal")]
Minimal,
#[serde(rename = "low")]
Low,
#[serde(rename = "medium")]
Medium,
#[serde(rename = "high")]
High,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum CADSImpactArea {
Fairness,
Privacy,
Safety,
Security,
Financial,
Operational,
Reputational,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CADSRiskAssessment {
#[serde(skip_serializing_if = "Option::is_none")]
pub methodology: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub date: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub assessor: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CADSRiskMitigation {
pub description: String,
pub status: CADSMitigationStatus,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum CADSMitigationStatus {
Planned,
Implemented,
Verified,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CADSRisk {
#[serde(skip_serializing_if = "Option::is_none")]
pub classification: Option<CADSRiskClassification>,
#[serde(skip_serializing_if = "Option::is_none", alias = "impact_areas")]
pub impact_areas: Option<Vec<CADSImpactArea>>,
#[serde(skip_serializing_if = "Option::is_none", alias = "intended_use")]
pub intended_use: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", alias = "out_of_scope_use")]
pub out_of_scope_use: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub assessment: Option<CADSRiskAssessment>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mitigations: Option<Vec<CADSRiskMitigation>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CADSComplianceFramework {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
pub status: CADSComplianceStatus,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CADSComplianceStatus {
NotApplicable,
Assessed,
Compliant,
NonCompliant,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CADSComplianceControl {
pub id: String,
pub description: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub evidence: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CADSCompliance {
#[serde(skip_serializing_if = "Option::is_none")]
pub frameworks: Option<Vec<CADSComplianceFramework>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub controls: Option<Vec<CADSComplianceControl>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CADSValidationProfileAppliesTo {
#[serde(skip_serializing_if = "Option::is_none")]
pub kind: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", alias = "risk_classification")]
pub risk_classification: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CADSValidationProfile {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none", alias = "applies_to")]
pub applies_to: Option<CADSValidationProfileAppliesTo>,
#[serde(alias = "required_checks")]
pub required_checks: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum CADSBPMNFormat {
#[serde(rename = "bpmn20-xml")]
Bpmn20Xml,
#[serde(rename = "json")]
Json,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CADSBPMNModel {
pub name: String,
pub reference: String,
pub format: CADSBPMNFormat,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum CADSDMNFormat {
#[serde(rename = "dmn13-xml")]
Dmn13Xml,
#[serde(rename = "json")]
Json,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CADSDMNModel {
pub name: String,
pub reference: String,
pub format: CADSDMNFormat,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum CADSOpenAPIFormat {
#[serde(rename = "openapi-3.0")]
Openapi30,
#[serde(rename = "openapi-3.1")]
Openapi31,
#[serde(rename = "swagger-2.0")]
Swagger20,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CADSOpenAPISpec {
pub name: String,
pub reference: String,
pub format: CADSOpenAPIFormat,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CADSAsset {
#[serde(alias = "api_version")]
pub api_version: String,
pub kind: CADSKind,
pub id: String,
pub name: String,
pub version: String,
pub status: CADSStatus,
#[serde(skip_serializing_if = "Option::is_none")]
pub domain: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", alias = "domain_id")]
pub domain_id: Option<Uuid>,
#[serde(
default,
skip_serializing_if = "Vec::is_empty",
deserialize_with = "deserialize_tags"
)]
pub tags: Vec<Tag>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<CADSDescription>,
#[serde(skip_serializing_if = "Option::is_none")]
pub runtime: Option<CADSRuntime>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sla: Option<CADSSLA>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pricing: Option<CADSPricing>,
#[serde(skip_serializing_if = "Option::is_none")]
pub team: Option<Vec<CADSTeamMember>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub risk: Option<CADSRisk>,
#[serde(skip_serializing_if = "Option::is_none")]
pub compliance: Option<CADSCompliance>,
#[serde(skip_serializing_if = "Option::is_none", alias = "validation_profiles")]
pub validation_profiles: Option<Vec<CADSValidationProfile>>,
#[serde(skip_serializing_if = "Option::is_none", alias = "bpmn_models")]
pub bpmn_models: Option<Vec<CADSBPMNModel>>,
#[serde(skip_serializing_if = "Option::is_none", alias = "dmn_models")]
pub dmn_models: Option<Vec<CADSDMNModel>>,
#[serde(skip_serializing_if = "Option::is_none", alias = "openapi_specs")]
pub openapi_specs: Option<Vec<CADSOpenAPISpec>>,
#[serde(skip_serializing_if = "Option::is_none", alias = "custom_properties")]
pub custom_properties: Option<HashMap<String, serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none", alias = "created_at")]
pub created_at: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none", alias = "updated_at")]
pub updated_at: Option<DateTime<Utc>>,
}
fn deserialize_tags<'de, D>(deserializer: D) -> Result<Vec<Tag>, D::Error>
where
D: serde::Deserializer<'de>,
{
struct TagVisitor;
impl<'de> serde::de::Visitor<'de> for TagVisitor {
type Value = Vec<Tag>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a vector of tags (strings or Tag objects)")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut tags = Vec::new();
while let Some(item) = seq.next_element::<serde_json::Value>()? {
match item {
serde_json::Value::String(s) => {
if let Ok(tag) = Tag::from_str(&s) {
tags.push(tag);
}
}
_ => {
if let serde_json::Value::String(s) = item
&& let Ok(tag) = Tag::from_str(&s)
{
tags.push(tag);
}
}
}
}
Ok(tags)
}
}
deserializer.deserialize_seq(TagVisitor)
}