1use std::collections::HashMap;
6
7use serde::{Deserialize, Serialize};
8
9pub type Extensions = HashMap<String, serde_json::Value>;
12
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19#[serde(rename_all = "lowercase")]
20pub enum CanonicalType {
21 String,
23 Boolean,
25 Number,
27 Integer,
29 Array,
31 Object,
33 Null,
35 Any,
37}
38
39impl CanonicalType {
40 pub fn parse(s: &str) -> Self {
42 match s.to_lowercase().as_str() {
43 "string" => Self::String,
44 "boolean" | "bool" => Self::Boolean,
45 "number" | "float" | "f64" => Self::Number,
46 "integer" | "int" | "i64" => Self::Integer,
47 "array" => Self::Array,
48 "object" => Self::Object,
49 "null" | "void" | "undefined" => Self::Null,
50 "any" | "unknown" => Self::Any,
51 _ => Self::Object,
52 }
53 }
54}
55
56impl std::fmt::Display for CanonicalType {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 let s = match self {
59 CanonicalType::String => "string",
60 CanonicalType::Boolean => "boolean",
61 CanonicalType::Number => "number",
62 CanonicalType::Integer => "integer",
63 CanonicalType::Array => "array",
64 CanonicalType::Object => "object",
65 CanonicalType::Null => "null",
66 CanonicalType::Any => "any",
67 };
68 write!(f, "{}", s)
69 }
70}
71
72#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
74#[serde(rename_all = "camelCase")]
75pub struct CkmTypeRef {
76 pub canonical: CanonicalType,
78
79 #[serde(default, skip_serializing_if = "Option::is_none")]
81 pub original: Option<String>,
82
83 #[serde(default, skip_serializing_if = "Option::is_none")]
85 pub r#enum: Option<Vec<String>>,
86}
87
88#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
90#[serde(rename_all = "camelCase")]
91pub struct CkmProperty {
92 pub name: String,
94
95 pub r#type: CkmTypeRef,
97
98 pub description: String,
100
101 pub required: bool,
103
104 #[serde(default, skip_serializing_if = "Option::is_none")]
106 pub default: Option<String>,
107}
108
109#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
111#[serde(rename_all = "camelCase")]
112pub struct CkmConcept {
113 pub id: String,
115
116 pub name: String,
118
119 pub slug: String,
121
122 pub what: String,
124
125 pub tags: Vec<String>,
127
128 #[serde(default, skip_serializing_if = "Option::is_none")]
130 pub properties: Option<Vec<CkmProperty>>,
131
132 #[serde(default, skip_serializing_if = "Option::is_none")]
134 pub rules: Option<Vec<String>>,
135
136 #[serde(default, skip_serializing_if = "Option::is_none")]
138 pub related_to: Option<Vec<String>>,
139
140 #[serde(default, skip_serializing_if = "Option::is_none")]
142 pub extensions: Option<Extensions>,
143}
144
145#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
147#[serde(rename_all = "camelCase")]
148pub struct CkmInput {
149 pub name: String,
151
152 pub r#type: CkmTypeRef,
154
155 pub required: bool,
157
158 pub description: String,
160}
161
162#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
164#[serde(rename_all = "camelCase")]
165pub struct CkmOutput {
166 pub r#type: CkmTypeRef,
168
169 pub description: String,
171}
172
173#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
175#[serde(rename_all = "camelCase")]
176pub struct CkmOperation {
177 pub id: String,
179
180 pub name: String,
182
183 pub what: String,
185
186 pub tags: Vec<String>,
188
189 #[serde(default, skip_serializing_if = "Option::is_none")]
191 pub preconditions: Option<Vec<String>>,
192
193 #[serde(default, skip_serializing_if = "Option::is_none")]
195 pub inputs: Option<Vec<CkmInput>>,
196
197 #[serde(default, skip_serializing_if = "Option::is_none")]
199 pub outputs: Option<CkmOutput>,
200
201 #[serde(default, skip_serializing_if = "Option::is_none")]
203 pub exit_codes: Option<std::collections::HashMap<String, String>>,
204
205 #[serde(default, skip_serializing_if = "Option::is_none")]
207 pub checks_performed: Option<Vec<String>>,
208
209 #[serde(default, skip_serializing_if = "Option::is_none")]
211 pub extensions: Option<Extensions>,
212}
213
214#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
216#[serde(rename_all = "camelCase")]
217pub struct CkmConstraint {
218 pub id: String,
220
221 pub rule: String,
223
224 pub enforced_by: String,
226
227 pub severity: Severity,
229
230 #[serde(default, skip_serializing_if = "Option::is_none")]
232 pub config_key: Option<String>,
233
234 #[serde(default, skip_serializing_if = "Option::is_none")]
236 pub default: Option<String>,
237
238 #[serde(default, skip_serializing_if = "Option::is_none")]
240 pub security: Option<bool>,
241
242 #[serde(default, skip_serializing_if = "Option::is_none")]
244 pub extensions: Option<Extensions>,
245}
246
247#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
249#[serde(rename_all = "lowercase")]
250pub enum Severity {
251 Error,
253 Warning,
255 Info,
257}
258
259#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
261#[serde(rename_all = "camelCase")]
262pub struct CkmWorkflowStep {
263 pub action: StepAction,
265
266 pub value: String,
268
269 #[serde(default, skip_serializing_if = "Option::is_none")]
271 pub note: Option<String>,
272
273 #[serde(default, skip_serializing_if = "Option::is_none")]
275 pub expect: Option<String>,
276}
277
278#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
280#[serde(rename_all = "lowercase")]
281pub enum StepAction {
282 Command,
284 Manual,
286}
287
288#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
290#[serde(rename_all = "camelCase")]
291pub struct CkmWorkflow {
292 pub id: String,
294
295 pub goal: String,
297
298 pub tags: Vec<String>,
300
301 pub steps: Vec<CkmWorkflowStep>,
303
304 #[serde(default, skip_serializing_if = "Option::is_none")]
306 pub extensions: Option<Extensions>,
307}
308
309#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
311#[serde(rename_all = "camelCase")]
312pub struct CkmConfigEntry {
313 pub key: String,
315
316 pub r#type: CkmTypeRef,
318
319 pub description: String,
321
322 #[serde(default, skip_serializing_if = "Option::is_none")]
324 pub default: Option<String>,
325
326 pub required: bool,
328
329 #[serde(default, skip_serializing_if = "Option::is_none")]
331 pub effect: Option<String>,
332
333 #[serde(default, skip_serializing_if = "Option::is_none")]
335 pub extensions: Option<Extensions>,
336}
337
338#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
343#[serde(rename_all = "camelCase")]
344pub struct CkmDeclaredTopic {
345 pub name: String,
347
348 pub summary: String,
350
351 #[serde(default, skip_serializing_if = "Vec::is_empty")]
353 pub concept_ids: Vec<String>,
354
355 #[serde(default, skip_serializing_if = "Vec::is_empty")]
357 pub operation_ids: Vec<String>,
358
359 #[serde(default, skip_serializing_if = "Vec::is_empty")]
361 pub constraint_ids: Vec<String>,
362
363 #[serde(default, skip_serializing_if = "Vec::is_empty")]
365 pub config_keys: Vec<String>,
366}
367
368#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
370#[serde(rename_all = "camelCase")]
371pub struct CkmMeta {
372 pub project: String,
374
375 pub language: String,
377
378 pub generator: String,
380
381 pub generated: String,
383
384 #[serde(default, skip_serializing_if = "Option::is_none")]
386 pub source_url: Option<String>,
387}
388
389#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
391#[serde(rename_all = "camelCase")]
392pub struct CkmManifest {
393 #[serde(rename = "$schema")]
395 pub schema: String,
396
397 pub version: String,
399
400 pub meta: CkmMeta,
402
403 pub concepts: Vec<CkmConcept>,
405
406 pub operations: Vec<CkmOperation>,
408
409 pub constraints: Vec<CkmConstraint>,
411
412 pub workflows: Vec<CkmWorkflow>,
414
415 pub config_schema: Vec<CkmConfigEntry>,
417
418 #[serde(default, skip_serializing_if = "Option::is_none")]
421 pub topics: Option<Vec<CkmDeclaredTopic>>,
422
423 #[serde(default, skip_serializing_if = "Option::is_none")]
425 pub extensions: Option<Extensions>,
426}
427
428#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
434#[serde(rename_all = "camelCase")]
435pub struct CkmTopic {
436 pub name: String,
438
439 pub summary: String,
441
442 pub concepts: Vec<CkmConcept>,
444
445 pub operations: Vec<CkmOperation>,
447
448 pub config_schema: Vec<CkmConfigEntry>,
450
451 pub constraints: Vec<CkmConstraint>,
453}
454
455#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
457#[serde(rename_all = "camelCase")]
458pub struct CkmTopicIndexEntry {
459 pub name: String,
461
462 pub summary: String,
464
465 pub concepts: usize,
467
468 pub operations: usize,
470
471 pub config_fields: usize,
473
474 pub constraints: usize,
476}
477
478#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
480#[serde(rename_all = "camelCase")]
481pub struct CkmManifestCounts {
482 pub concepts: usize,
484
485 pub operations: usize,
487
488 pub constraints: usize,
490
491 pub workflows: usize,
493
494 pub config_schema: usize,
496}
497
498#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
500#[serde(rename_all = "camelCase")]
501pub struct CkmTopicIndex {
502 pub topics: Vec<CkmTopicIndexEntry>,
504
505 pub ckm: CkmManifestCounts,
507}
508
509#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
511#[serde(rename_all = "camelCase")]
512pub struct CkmInspectResult {
513 pub meta: CkmMeta,
515
516 pub counts: CkmInspectCounts,
518
519 pub topic_names: Vec<String>,
521}
522
523#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
525#[serde(rename_all = "camelCase")]
526pub struct CkmInspectCounts {
527 pub concepts: usize,
529 pub operations: usize,
531 pub constraints: usize,
533 pub workflows: usize,
535 pub config_keys: usize,
537 pub topics: usize,
539}
540
541#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
543#[serde(rename_all = "camelCase")]
544pub struct CkmValidationError {
545 pub path: String,
547
548 pub message: String,
550}
551
552#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
554#[serde(rename_all = "camelCase")]
555pub struct CkmValidationResult {
556 pub valid: bool,
558
559 pub errors: Vec<CkmValidationError>,
561}
562
563#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
565#[serde(rename_all = "camelCase")]
566pub struct CkmErrorResult {
567 pub error: String,
569
570 pub topics: Vec<String>,
572}
573
574#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
576#[serde(untagged)]
577pub enum TopicJsonResult {
578 Index(CkmTopicIndex),
580
581 Topic(CkmTopic),
583
584 Error(CkmErrorResult),
586}