use std::collections::HashMap;
use serde::{Deserialize, Serialize};
pub type Extensions = HashMap<String, serde_json::Value>;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum CanonicalType {
String,
Boolean,
Number,
Integer,
Array,
Object,
Null,
Any,
}
impl CanonicalType {
pub fn parse(s: &str) -> Self {
match s.to_lowercase().as_str() {
"string" => Self::String,
"boolean" | "bool" => Self::Boolean,
"number" | "float" | "f64" => Self::Number,
"integer" | "int" | "i64" => Self::Integer,
"array" => Self::Array,
"object" => Self::Object,
"null" | "void" | "undefined" => Self::Null,
"any" | "unknown" => Self::Any,
_ => Self::Object,
}
}
}
impl std::fmt::Display for CanonicalType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
CanonicalType::String => "string",
CanonicalType::Boolean => "boolean",
CanonicalType::Number => "number",
CanonicalType::Integer => "integer",
CanonicalType::Array => "array",
CanonicalType::Object => "object",
CanonicalType::Null => "null",
CanonicalType::Any => "any",
};
write!(f, "{}", s)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmTypeRef {
pub canonical: CanonicalType,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub original: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub r#enum: Option<Vec<String>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmProperty {
pub name: String,
pub r#type: CkmTypeRef,
pub description: String,
pub required: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmConcept {
pub id: String,
pub name: String,
pub slug: String,
pub what: String,
pub tags: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub properties: Option<Vec<CkmProperty>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rules: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub related_to: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extensions: Option<Extensions>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmInput {
pub name: String,
pub r#type: CkmTypeRef,
pub required: bool,
pub description: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmOutput {
pub r#type: CkmTypeRef,
pub description: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmOperation {
pub id: String,
pub name: String,
pub what: String,
pub tags: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub preconditions: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub inputs: Option<Vec<CkmInput>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub outputs: Option<CkmOutput>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub exit_codes: Option<std::collections::HashMap<String, String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub checks_performed: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extensions: Option<Extensions>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmConstraint {
pub id: String,
pub rule: String,
pub enforced_by: String,
pub severity: Severity,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub config_key: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub security: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extensions: Option<Extensions>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Severity {
Error,
Warning,
Info,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmWorkflowStep {
pub action: StepAction,
pub value: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expect: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum StepAction {
Command,
Manual,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmWorkflow {
pub id: String,
pub goal: String,
pub tags: Vec<String>,
pub steps: Vec<CkmWorkflowStep>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extensions: Option<Extensions>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmConfigEntry {
pub key: String,
pub r#type: CkmTypeRef,
pub description: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default: Option<String>,
pub required: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub effect: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extensions: Option<Extensions>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmDeclaredTopic {
pub name: String,
pub summary: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub concept_ids: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub operation_ids: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub constraint_ids: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub config_keys: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmMeta {
pub project: String,
pub language: String,
pub generator: String,
pub generated: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_url: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmManifest {
#[serde(rename = "$schema")]
pub schema: String,
pub version: String,
pub meta: CkmMeta,
pub concepts: Vec<CkmConcept>,
pub operations: Vec<CkmOperation>,
pub constraints: Vec<CkmConstraint>,
pub workflows: Vec<CkmWorkflow>,
pub config_schema: Vec<CkmConfigEntry>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub topics: Option<Vec<CkmDeclaredTopic>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extensions: Option<Extensions>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmTopic {
pub name: String,
pub summary: String,
pub concepts: Vec<CkmConcept>,
pub operations: Vec<CkmOperation>,
pub config_schema: Vec<CkmConfigEntry>,
pub constraints: Vec<CkmConstraint>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmTopicIndexEntry {
pub name: String,
pub summary: String,
pub concepts: usize,
pub operations: usize,
pub config_fields: usize,
pub constraints: usize,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmManifestCounts {
pub concepts: usize,
pub operations: usize,
pub constraints: usize,
pub workflows: usize,
pub config_schema: usize,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmTopicIndex {
pub topics: Vec<CkmTopicIndexEntry>,
pub ckm: CkmManifestCounts,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmInspectResult {
pub meta: CkmMeta,
pub counts: CkmInspectCounts,
pub topic_names: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmInspectCounts {
pub concepts: usize,
pub operations: usize,
pub constraints: usize,
pub workflows: usize,
pub config_keys: usize,
pub topics: usize,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmValidationError {
pub path: String,
pub message: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmValidationResult {
pub valid: bool,
pub errors: Vec<CkmValidationError>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CkmErrorResult {
pub error: String,
pub topics: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum TopicJsonResult {
Index(CkmTopicIndex),
Topic(CkmTopic),
Error(CkmErrorResult),
}