canon_protocol/
specification.rs

1use serde::{Deserialize, Serialize};
2use serde_yaml::Value;
3use std::collections::HashMap;
4
5/// Canon Protocol Specification
6///
7/// This represents ANY Canon document - whether it's a type definition,
8/// a project specification, or any other kind of Canon specification.
9/// The `type` field determines what kind of specification this is.
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct CanonSpecification {
12    /// Protocol version (e.g., "1.0")
13    pub canon: String,
14
15    /// Type reference - determines what kind of specification this is
16    /// Examples:
17    /// - "canon-protocol.org/type@1.0.0" for type definitions
18    /// - "canon-protocol.org/project@1.0.0" for projects
19    /// - "content.org/blog-post@1.0.0" for a blog post
20    pub r#type: String,
21
22    /// Required metadata for all specifications
23    pub metadata: SpecificationMetadata,
24
25    /// Optional includes for type composition (used when this is a type definition)
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub includes: Option<Vec<String>>,
28
29    /// Optional schema definition (only present when this is a type definition)
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub schema: Option<HashMap<String, SchemaField>>,
32
33    /// Type-specific content (any fields defined by the type's schema)
34    #[serde(flatten)]
35    pub content: HashMap<String, Value>,
36}
37
38/// Required metadata for all specifications
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct SpecificationMetadata {
41    /// Unique identifier (lowercase, alphanumeric with hyphens)
42    pub id: String,
43
44    /// Semantic version (MAJOR.MINOR.PATCH)
45    pub version: String,
46
47    /// Publisher domain or subdomain
48    pub publisher: String,
49
50    /// Optional human-readable title
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub title: Option<String>,
53
54    /// Optional description
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub description: Option<String>,
57}
58
59/// Schema field definition
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct SchemaField {
62    /// Field type
63    pub r#type: FieldType,
64
65    /// Whether the field is required
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub required: Option<bool>,
68
69    /// For ref types, the URI with optional version operators
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub uri: Option<String>,
72
73    /// Pattern for string validation
74    #[serde(skip_serializing_if = "Option::is_none")]
75    pub pattern: Option<String>,
76
77    /// Enumeration of allowed values
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub r#enum: Option<Vec<Value>>,
80
81    /// For objects, property definitions
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub properties: Option<HashMap<String, SchemaField>>,
84
85    /// For arrays, item schema
86    #[serde(skip_serializing_if = "Option::is_none")]
87    pub items: Option<Box<SchemaField>>,
88
89    /// Field description
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub description: Option<String>,
92}
93
94/// Supported field types
95#[derive(Debug, Clone, Serialize, Deserialize)]
96#[serde(rename_all = "lowercase")]
97pub enum FieldType {
98    String,
99    Number,
100    Boolean,
101    Object,
102    Array,
103    Ref,
104    Any,
105}
106
107// Keep for backward compatibility during migration
108#[derive(Debug, Serialize, Deserialize)]
109pub struct OutputConfiguration {
110    pub artifacts: Option<Vec<String>>,
111    pub directory: Option<String>,
112}
113
114#[derive(Debug, Serialize, Deserialize)]
115pub struct SourceDefinition {
116    pub path: String,
117    pub r#type: String,
118    pub include: Option<Vec<String>>,
119    pub exclude: Option<Vec<String>>,
120}