use super::{CompatibilityCheck, CompatibilityMode, MessageSchema, SchemaRegistryExt};
use crate::context::Context;
use crate::error::{Mecha10Error, Result};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MessageVersionInfo {
pub name: String,
pub current_version: u32,
pub latest_version: u32,
pub deprecated: bool,
pub deprecation_message: Option<String>,
pub min_supported_version: u32,
pub compatibility_mode: CompatibilityMode,
}
pub trait SchemaVersioningExt: SchemaRegistryExt {
fn check_compatibility(
&self,
name: &str,
old_version: u32,
new_version: u32,
mode: CompatibilityMode,
) -> impl std::future::Future<Output = Result<CompatibilityCheck>> + Send;
fn register_versioned_schema(
&self,
schema: &MessageSchema,
mode: CompatibilityMode,
) -> impl std::future::Future<Output = Result<CompatibilityCheck>> + Send;
fn get_version_info(
&self,
name: &str,
) -> impl std::future::Future<Output = Result<Option<MessageVersionInfo>>> + Send;
fn deprecate_version(
&self,
name: &str,
version: u32,
message: String,
) -> impl std::future::Future<Output = Result<()>> + Send;
fn is_version_deprecated(&self, name: &str, version: u32)
-> impl std::future::Future<Output = Result<bool>> + Send;
}
impl SchemaVersioningExt for Context {
async fn check_compatibility(
&self,
name: &str,
old_version: u32,
new_version: u32,
mode: CompatibilityMode,
) -> Result<CompatibilityCheck> {
let old_schema = self
.get_schema(name, old_version)
.await?
.ok_or_else(|| Mecha10Error::Other(format!("Schema {} v{} not found", name, old_version)))?;
let new_schema = self
.get_schema(name, new_version)
.await?
.ok_or_else(|| Mecha10Error::Other(format!("Schema {} v{} not found", name, new_version)))?;
Ok(CompatibilityCheck::check(&old_schema, &new_schema, mode))
}
async fn register_versioned_schema(
&self,
schema: &MessageSchema,
mode: CompatibilityMode,
) -> Result<CompatibilityCheck> {
if schema.version > 1 {
let prev_version = schema.version - 1;
let check = self
.check_compatibility(&schema.name, prev_version, schema.version, mode)
.await?;
if !check.compatible {
return Err(Mecha10Error::Other(format!(
"Schema compatibility check failed: {:?}",
check.incompatibilities
)));
}
self.register_schema(schema).await?;
Ok(check)
} else {
self.register_schema(schema).await?;
Ok(CompatibilityCheck {
compatible: true,
incompatibilities: Vec::new(),
evolutions: Vec::new(),
warnings: Vec::new(),
})
}
}
async fn get_version_info(&self, name: &str) -> Result<Option<MessageVersionInfo>> {
let latest = self.get_latest_schema(name).await?;
if let Some(latest_schema) = latest {
let deprecated = latest_schema.metadata.contains_key("deprecated");
let deprecation_message = latest_schema.metadata.get("deprecation_message").cloned();
let min_supported = latest_schema
.metadata
.get("min_supported_version")
.and_then(|v| v.parse().ok())
.unwrap_or(1);
let compatibility_mode = latest_schema
.metadata
.get("compatibility_mode")
.and_then(|m| serde_json::from_str(m).ok())
.unwrap_or(CompatibilityMode::Backward);
Ok(Some(MessageVersionInfo {
name: name.to_string(),
current_version: latest_schema.version,
latest_version: latest_schema.version,
deprecated,
deprecation_message,
min_supported_version: min_supported,
compatibility_mode,
}))
} else {
Ok(None)
}
}
async fn deprecate_version(&self, name: &str, version: u32, message: String) -> Result<()> {
let mut schema = self
.get_schema(name, version)
.await?
.ok_or_else(|| Mecha10Error::Other(format!("Schema {} v{} not found", name, version)))?;
schema.metadata.insert("deprecated".to_string(), "true".to_string());
schema.metadata.insert("deprecation_message".to_string(), message);
self.register_schema(&schema).await
}
async fn is_version_deprecated(&self, name: &str, version: u32) -> Result<bool> {
let schema = self
.get_schema(name, version)
.await?
.ok_or_else(|| Mecha10Error::Other(format!("Schema {} v{} not found", name, version)))?;
Ok(schema.metadata.contains_key("deprecated"))
}
}