Skip to main content

mecha10_core/schema/
versioning.rs

1//! Versioned schema operations built on top of [`super::SchemaRegistryExt`].
2
3use super::{CompatibilityCheck, CompatibilityMode, MessageSchema, SchemaRegistryExt};
4use crate::context::Context;
5use crate::error::{Mecha10Error, Result};
6use serde::{Deserialize, Serialize};
7
8/// Message version metadata
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct MessageVersionInfo {
11    /// Schema name
12    pub name: String,
13
14    /// Current version being used
15    pub current_version: u32,
16
17    /// Latest available version
18    pub latest_version: u32,
19
20    /// Whether current version is deprecated
21    pub deprecated: bool,
22
23    /// Deprecation message if applicable
24    pub deprecation_message: Option<String>,
25
26    /// Minimum supported version
27    pub min_supported_version: u32,
28
29    /// Compatibility mode for this message type
30    pub compatibility_mode: CompatibilityMode,
31}
32
33/// Extension trait for versioned schema operations
34pub trait SchemaVersioningExt: SchemaRegistryExt {
35    /// Check compatibility between two schema versions
36    fn check_compatibility(
37        &self,
38        name: &str,
39        old_version: u32,
40        new_version: u32,
41        mode: CompatibilityMode,
42    ) -> impl std::future::Future<Output = Result<CompatibilityCheck>> + Send;
43
44    /// Register a new schema version with compatibility check
45    fn register_versioned_schema(
46        &self,
47        schema: &MessageSchema,
48        mode: CompatibilityMode,
49    ) -> impl std::future::Future<Output = Result<CompatibilityCheck>> + Send;
50
51    /// Get version info for a message type
52    fn get_version_info(
53        &self,
54        name: &str,
55    ) -> impl std::future::Future<Output = Result<Option<MessageVersionInfo>>> + Send;
56
57    /// Mark a schema version as deprecated
58    fn deprecate_version(
59        &self,
60        name: &str,
61        version: u32,
62        message: String,
63    ) -> impl std::future::Future<Output = Result<()>> + Send;
64
65    /// Check if using a deprecated version
66    fn is_version_deprecated(&self, name: &str, version: u32)
67        -> impl std::future::Future<Output = Result<bool>> + Send;
68}
69
70impl SchemaVersioningExt for Context {
71    async fn check_compatibility(
72        &self,
73        name: &str,
74        old_version: u32,
75        new_version: u32,
76        mode: CompatibilityMode,
77    ) -> Result<CompatibilityCheck> {
78        let old_schema = self
79            .get_schema(name, old_version)
80            .await?
81            .ok_or_else(|| Mecha10Error::Other(format!("Schema {} v{} not found", name, old_version)))?;
82
83        let new_schema = self
84            .get_schema(name, new_version)
85            .await?
86            .ok_or_else(|| Mecha10Error::Other(format!("Schema {} v{} not found", name, new_version)))?;
87
88        Ok(CompatibilityCheck::check(&old_schema, &new_schema, mode))
89    }
90
91    async fn register_versioned_schema(
92        &self,
93        schema: &MessageSchema,
94        mode: CompatibilityMode,
95    ) -> Result<CompatibilityCheck> {
96        if schema.version > 1 {
97            let prev_version = schema.version - 1;
98            let check = self
99                .check_compatibility(&schema.name, prev_version, schema.version, mode)
100                .await?;
101
102            if !check.compatible {
103                return Err(Mecha10Error::Other(format!(
104                    "Schema compatibility check failed: {:?}",
105                    check.incompatibilities
106                )));
107            }
108
109            self.register_schema(schema).await?;
110
111            Ok(check)
112        } else {
113            self.register_schema(schema).await?;
114
115            Ok(CompatibilityCheck {
116                compatible: true,
117                incompatibilities: Vec::new(),
118                evolutions: Vec::new(),
119                warnings: Vec::new(),
120            })
121        }
122    }
123
124    async fn get_version_info(&self, name: &str) -> Result<Option<MessageVersionInfo>> {
125        let latest = self.get_latest_schema(name).await?;
126
127        if let Some(latest_schema) = latest {
128            let deprecated = latest_schema.metadata.contains_key("deprecated");
129            let deprecation_message = latest_schema.metadata.get("deprecation_message").cloned();
130            let min_supported = latest_schema
131                .metadata
132                .get("min_supported_version")
133                .and_then(|v| v.parse().ok())
134                .unwrap_or(1);
135
136            let compatibility_mode = latest_schema
137                .metadata
138                .get("compatibility_mode")
139                .and_then(|m| serde_json::from_str(m).ok())
140                .unwrap_or(CompatibilityMode::Backward);
141
142            Ok(Some(MessageVersionInfo {
143                name: name.to_string(),
144                current_version: latest_schema.version,
145                latest_version: latest_schema.version,
146                deprecated,
147                deprecation_message,
148                min_supported_version: min_supported,
149                compatibility_mode,
150            }))
151        } else {
152            Ok(None)
153        }
154    }
155
156    async fn deprecate_version(&self, name: &str, version: u32, message: String) -> Result<()> {
157        let mut schema = self
158            .get_schema(name, version)
159            .await?
160            .ok_or_else(|| Mecha10Error::Other(format!("Schema {} v{} not found", name, version)))?;
161
162        schema.metadata.insert("deprecated".to_string(), "true".to_string());
163        schema.metadata.insert("deprecation_message".to_string(), message);
164
165        self.register_schema(&schema).await
166    }
167
168    async fn is_version_deprecated(&self, name: &str, version: u32) -> Result<bool> {
169        let schema = self
170            .get_schema(name, version)
171            .await?
172            .ok_or_else(|| Mecha10Error::Other(format!("Schema {} v{} not found", name, version)))?;
173
174        Ok(schema.metadata.contains_key("deprecated"))
175    }
176}