systemprompt-models 0.2.1

Foundation data models for systemprompt.io AI governance infrastructure. Shared DTOs, config, and domain types consumed by every layer of the MCP governance pipeline.
Documentation
use super::super::chart::ChartDataset;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct MetricsCardsData {
    pub cards: Vec<MetricCard>,
}

impl MetricsCardsData {
    pub const fn new(cards: Vec<MetricCard>) -> Self {
        Self { cards }
    }

    pub fn add_card(mut self, card: MetricCard) -> Self {
        self.cards.push(card);
        self
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct MetricCard {
    pub title: String,
    pub value: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub subtitle: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub icon: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<MetricStatus>,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "lowercase")]
pub enum MetricStatus {
    Success,
    Warning,
    Error,
    #[default]
    Info,
}

impl std::str::FromStr for MetricStatus {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "success" | "healthy" | "ok" | "active" => Ok(Self::Success),
            "warning" | "degraded" => Ok(Self::Warning),
            "error" | "failed" | "critical" => Ok(Self::Error),
            "info" | "unknown" => Ok(Self::Info),
            _ => Err(format!("Invalid metric status: {s}")),
        }
    }
}

impl MetricCard {
    pub fn new(title: impl Into<String>, value: impl Into<String>) -> Self {
        Self {
            title: title.into(),
            value: value.into(),
            subtitle: None,
            icon: None,
            status: None,
        }
    }

    pub fn with_subtitle(mut self, subtitle: impl Into<String>) -> Self {
        self.subtitle = Some(subtitle.into());
        self
    }

    pub fn with_icon(mut self, icon: impl Into<String>) -> Self {
        self.icon = Some(icon.into());
        self
    }

    pub const fn with_status(mut self, status: MetricStatus) -> Self {
        self.status = Some(status);
        self
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ChartSectionData {
    pub chart_type: String,
    pub labels: Vec<String>,
    pub datasets: Vec<ChartDataset>,
}

impl ChartSectionData {
    pub fn new(
        chart_type: impl Into<String>,
        labels: Vec<String>,
        datasets: Vec<ChartDataset>,
    ) -> Self {
        Self {
            chart_type: chart_type.into(),
            labels,
            datasets,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct TableSectionData {
    pub columns: Vec<String>,
    pub rows: Vec<serde_json::Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sortable: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub default_sort: Option<SortConfig>,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct SortConfig {
    pub column: String,
    pub order: String,
}

impl TableSectionData {
    pub const fn new(columns: Vec<String>, rows: Vec<serde_json::Value>) -> Self {
        Self {
            columns,
            rows,
            sortable: None,
            default_sort: None,
        }
    }

    pub const fn with_sortable(mut self, sortable: bool) -> Self {
        self.sortable = Some(sortable);
        self
    }

    pub fn with_default_sort(
        mut self,
        column: impl Into<String>,
        order: impl Into<String>,
    ) -> Self {
        self.default_sort = Some(SortConfig {
            column: column.into(),
            order: order.into(),
        });
        self
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct StatusSectionData {
    pub services: Vec<ServiceStatus>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub database: Option<DatabaseStatus>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub recent_errors: Option<ErrorCounts>,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ServiceStatus {
    pub name: String,
    pub status: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub uptime: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct DatabaseStatus {
    pub size_mb: f64,
    pub status: String,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
pub struct ErrorCounts {
    pub critical: i32,
    pub error: i32,
    pub warn: i32,
}

impl StatusSectionData {
    pub const fn new(services: Vec<ServiceStatus>) -> Self {
        Self {
            services,
            database: None,
            recent_errors: None,
        }
    }

    pub fn with_database(mut self, status: DatabaseStatus) -> Self {
        self.database = Some(status);
        self
    }

    pub const fn with_error_counts(mut self, counts: ErrorCounts) -> Self {
        self.recent_errors = Some(counts);
        self
    }
}

impl ServiceStatus {
    pub fn new(name: impl Into<String>, status: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            status: status.into(),
            uptime: None,
        }
    }

    pub fn with_uptime(mut self, uptime: impl Into<String>) -> Self {
        self.uptime = Some(uptime.into());
        self
    }
}

impl DatabaseStatus {
    pub fn new(size_mb: f64, status: impl Into<String>) -> Self {
        Self {
            size_mb,
            status: status.into(),
        }
    }
}

impl ErrorCounts {
    pub const fn new(critical: i32, error: i32, warn: i32) -> Self {
        Self {
            critical,
            error,
            warn,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ListSectionData {
    pub lists: Vec<ItemList>,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ItemList {
    pub title: String,
    pub items: Vec<ListItem>,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ListItem {
    pub rank: i32,
    pub label: String,
    pub value: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub badge: Option<String>,
}

impl ListSectionData {
    pub const fn new(lists: Vec<ItemList>) -> Self {
        Self { lists }
    }
}

impl ItemList {
    pub fn new(title: impl Into<String>, items: Vec<ListItem>) -> Self {
        Self {
            title: title.into(),
            items,
        }
    }
}

impl ListItem {
    pub fn new(rank: i32, label: impl Into<String>, value: impl Into<String>) -> Self {
        Self {
            rank,
            label: label.into(),
            value: value.into(),
            badge: None,
        }
    }

    pub fn with_badge(mut self, badge: impl Into<String>) -> Self {
        self.badge = Some(badge.into());
        self
    }
}