use mecha10::prelude::*;
pub fn basic_versioning_example() -> Result<()> {
info!("Example 1: Basic schema versioning");
let v1 = MessageSchema::new("LaserScan", 1)
.with_description("2D laser scan data")
.with_field("timestamp", "integer", true)
.with_field("ranges", "array", true)
.with_field("intensities", "array", true);
info!("LaserScan v1:");
info!(" Fields: timestamp (required), ranges (required), intensities (required)");
let v2 = MessageSchema::new("LaserScan", 2)
.with_description("2D laser scan data with angle info")
.with_field("timestamp", "integer", true)
.with_field("ranges", "array", true)
.with_field("intensities", "array", true)
.with_field("angle_min", "number", false)
.with_field("angle_max", "number", false);
info!("\nLaserScan v2:");
info!(" Added: angle_min (optional), angle_max (optional)");
Ok(())
}
pub fn compatibility_checking_example() -> Result<()> {
info!("Example 2: Compatibility checking");
let v1 = MessageSchema::new("Image", 1)
.with_field("width", "integer", true)
.with_field("height", "integer", true)
.with_field("data", "array", true);
let v2 = MessageSchema::new("Image", 2)
.with_field("width", "integer", true)
.with_field("height", "integer", true)
.with_field("data", "array", true)
.with_field("encoding", "string", false);
let check = CompatibilityCheck::check(&v1, &v2, CompatibilityMode::Backward);
info!("\nBackward compatibility check (v1 → v2):");
info!(" Compatible: {}", check.compatible);
info!(" Evolutions:");
for evolution in &check.evolutions {
info!(" {:?}", evolution);
}
info!(" Warnings:");
for warning in &check.warnings {
info!(" {}", warning);
}
Ok(())
}
pub fn backward_compatibility_example() -> Result<()> {
info!("Example 3: Backward compatibility");
info!(" New schema can read old data");
info!(" Rules:");
info!(" ✓ Can add optional fields");
info!(" ✓ Can make required fields optional");
info!(" ✗ Cannot remove required fields");
info!(" ✗ Cannot add required fields");
info!(" ✗ Cannot change field types");
let v1 = MessageSchema::new("Odometry", 1)
.with_field("x", "number", true)
.with_field("y", "number", true)
.with_field("theta", "number", true);
let v2_valid = MessageSchema::new("Odometry", 2)
.with_field("x", "number", true)
.with_field("y", "number", true)
.with_field("theta", "number", true)
.with_field("covariance", "array", false);
let check = CompatibilityCheck::check(&v1, &v2_valid, CompatibilityMode::Backward);
info!("\nAdding optional field:");
info!(" Compatible: {} ✓", check.compatible);
let v2_invalid = MessageSchema::new("Odometry", 2)
.with_field("x", "number", true)
.with_field("y", "number", true)
.with_field("theta", "number", true)
.with_field("velocity", "number", true);
let check = CompatibilityCheck::check(&v1, &v2_invalid, CompatibilityMode::Backward);
info!("\nAdding required field:");
info!(" Compatible: {} ✗", check.compatible);
info!(" Incompatibilities:");
for incomp in &check.incompatibilities {
info!(" {}", incomp);
}
Ok(())
}
pub fn forward_compatibility_example() -> Result<()> {
info!("Example 4: Forward compatibility");
info!(" Old schema can read new data");
info!(" Rules:");
info!(" ✓ Can remove optional fields");
info!(" ✓ Can make optional fields required");
info!(" ✗ Cannot add required fields");
info!(" ✗ Cannot remove required fields");
info!(" ✗ Cannot change field types");
let v1 = MessageSchema::new("Point", 1)
.with_field("x", "number", true)
.with_field("y", "number", true)
.with_field("label", "string", false);
let v2_valid = MessageSchema::new("Point", 2)
.with_field("x", "number", true)
.with_field("y", "number", true);
let check = CompatibilityCheck::check(&v1, &v2_valid, CompatibilityMode::Forward);
info!("\nRemoving optional field:");
info!(" Compatible: {} ✓", check.compatible);
Ok(())
}
pub fn full_compatibility_example() -> Result<()> {
info!("Example 5: Full compatibility (Backward + Forward)");
info!(" Both old and new schemas can read each other's data");
info!(" Rules:");
info!(" ✓ Can add/remove optional fields");
info!(" ✗ Cannot change field types");
info!(" ✗ Cannot change required status");
let v1 = MessageSchema::new("Config", 1)
.with_field("timeout", "integer", true)
.with_field("retries", "integer", true)
.with_field("debug", "boolean", false);
let v2_valid = MessageSchema::new("Config", 2)
.with_field("timeout", "integer", true)
.with_field("retries", "integer", true)
.with_field("debug", "boolean", false)
.with_field("verbose", "boolean", false);
let check = CompatibilityCheck::check(&v1, &v2_valid, CompatibilityMode::Full);
info!("\nAdding optional field:");
info!(" Compatible: {} ✓", check.compatible);
let v2_invalid = MessageSchema::new("Config", 2)
.with_field("timeout", "integer", true)
.with_field("retries", "integer", true)
.with_field("debug", "boolean", true);
let check = CompatibilityCheck::check(&v1, &v2_invalid, CompatibilityMode::Full);
info!("\nChanging field to required:");
info!(" Compatible: {} ✗", check.compatible);
info!(" Incompatibilities:");
for incomp in &check.incompatibilities {
info!(" {}", incomp);
}
Ok(())
}
pub fn strict_compatibility_example() -> Result<()> {
info!("Example 6: Strict compatibility");
info!(" No schema changes allowed");
info!(" Rules:");
info!(" ✗ No changes of any kind");
let v1 = MessageSchema::new("StrictMessage", 1)
.with_field("id", "integer", true)
.with_field("data", "string", true);
let v2 = MessageSchema::new("StrictMessage", 2)
.with_field("id", "integer", true)
.with_field("data", "string", true)
.with_field("metadata", "object", false);
let check = CompatibilityCheck::check(&v1, &v2, CompatibilityMode::Strict);
info!("\nAdding any field:");
info!(" Compatible: {} ✗", check.compatible);
info!(" Incompatibilities:");
for incomp in &check.incompatibilities {
info!(" {}", incomp);
}
Ok(())
}
pub fn evolution_operations_example() -> Result<()> {
info!("Example 7: Schema evolution operations");
let v1 = MessageSchema::new("Sensor", 1)
.with_field("id", "integer", true)
.with_field("value", "number", true)
.with_field("deprecated_field", "string", true);
let v2 = MessageSchema::new("Sensor", 2)
.with_field("id", "integer", true)
.with_field("value", "number", true)
.with_field("unit", "string", false)
.with_field("quality", "number", false);
let check = CompatibilityCheck::check(&v1, &v2, CompatibilityMode::Backward);
info!("\nDetected evolutions:");
for evolution in &check.evolutions {
match evolution {
SchemaEvolution::FieldAdded {
name,
field_type,
required,
..
} => {
info!(
" + Field added: {} ({}, {})",
name,
field_type,
if *required { "required" } else { "optional" }
);
}
SchemaEvolution::FieldRemoved { name } => {
info!(" - Field removed: {}", name);
}
SchemaEvolution::FieldMadeOptional { name } => {
info!(" ~ Field made optional: {}", name);
}
SchemaEvolution::FieldMadeRequired { name, .. } => {
info!(" ~ Field made required: {}", name);
}
SchemaEvolution::FieldTypeChanged {
name,
old_type,
new_type,
} => {
info!(" ⚠ Field type changed: {} ({} → {})", name, old_type, new_type);
}
SchemaEvolution::FieldRenamed { old_name, new_name } => {
info!(" → Field renamed: {} → {}", old_name, new_name);
}
}
}
Ok(())
}
pub fn migration_scenario_example() -> Result<()> {
info!("Example 8: Real-world migration scenario");
info!(" Evolving a robot pose message over time");
let v1 = MessageSchema::new("RobotPose", 1)
.with_description("Robot 2D pose")
.with_field("x", "number", true)
.with_field("y", "number", true)
.with_field("theta", "number", true)
.with_field("timestamp", "integer", true);
info!("\nV1 (Initial): 2D pose only");
let v2 = MessageSchema::new("RobotPose", 2)
.with_description("Robot 2D pose with uncertainty")
.with_field("x", "number", true)
.with_field("y", "number", true)
.with_field("theta", "number", true)
.with_field("timestamp", "integer", true)
.with_field("covariance", "array", false);
let check_v1_v2 = CompatibilityCheck::check(&v1, &v2, CompatibilityMode::Backward);
info!("\nV2: Added covariance - Compatible: {} ✓", check_v1_v2.compatible);
let v3 = MessageSchema::new("RobotPose", 3)
.with_description("Robot pose with optional 3D")
.with_field("x", "number", true)
.with_field("y", "number", true)
.with_field("theta", "number", true)
.with_field("timestamp", "integer", true)
.with_field("covariance", "array", false)
.with_field("z", "number", false)
.with_field("roll", "number", false)
.with_field("pitch", "number", false);
let check_v2_v3 = CompatibilityCheck::check(&v2, &v3, CompatibilityMode::Backward);
info!("V3: Added 3D fields - Compatible: {} ✓", check_v2_v3.compatible);
let v4 = MessageSchema::new("RobotPose", 4)
.with_description("Robot pose with optional timestamp")
.with_field("x", "number", true)
.with_field("y", "number", true)
.with_field("theta", "number", true)
.with_field("timestamp", "integer", false)
.with_field("covariance", "array", false)
.with_field("z", "number", false)
.with_field("roll", "number", false)
.with_field("pitch", "number", false);
let check_v3_v4 = CompatibilityCheck::check(&v3, &v4, CompatibilityMode::Backward);
info!("V4: Timestamp optional - Compatible: {} ✓", check_v3_v4.compatible);
info!("\nMigration path: v1 → v2 → v3 → v4");
info!(" All transitions backward compatible");
info!(" Old code continues to work with new messages");
Ok(())
}
pub fn deprecation_example() -> Result<()> {
info!("Example 9: Deprecation workflow");
let v1 = MessageSchema::new("OldAPI", 1)
.with_description("Legacy API - deprecated")
.with_field("data", "string", true)
.with_metadata("deprecated", "true")
.with_metadata("deprecation_message", "Use NewAPI instead. Will be removed in v3.0");
info!("\nDeprecated schema:");
info!(" Version: {}", v1.version);
if v1.metadata.contains_key("deprecated") {
info!(" ⚠ DEPRECATED");
if let Some(msg) = v1.metadata.get("deprecation_message") {
info!(" Message: {}", msg);
}
}
let v2 = MessageSchema::new("NewAPI", 1)
.with_description("Replacement API")
.with_field("data", "string", true)
.with_field("version", "integer", true)
.with_field("checksum", "string", true);
info!("\nNew API:");
info!(" Version: {}", v2.version);
info!(" Description: {}", v2.description);
info!(" Migration: Add version and checksum fields");
Ok(())
}
pub fn best_practices_example() -> Result<()> {
info!("Example 10: Schema evolution best practices");
info!("\n1. Always add new fields as optional:");
info!(" ✓ field: data, type: string, required: false");
info!(" ✗ field: data, type: string, required: true");
info!("\n2. Never change field types:");
info!(" ✗ old: timestamp (integer) → new: timestamp (string)");
info!(" ✓ Add new field with different name instead");
info!("\n3. Make fields optional before removing:");
info!(" v1: field required");
info!(" v2: field optional (transition)");
info!(" v3: field removed");
info!("\n4. Use metadata for compatibility hints:");
info!(" - deprecated: true");
info!(" - deprecation_message: Migration instructions");
info!(" - min_supported_version: 2");
info!(" - compatibility_mode: Backward");
info!("\n5. Version incrementally:");
info!(" ✓ v1 → v2 → v3 (gradual changes)");
info!(" ✗ v1 → v10 (big bang)");
info!("\n6. Test compatibility before deployment:");
info!(" let check = CompatibilityCheck::check(&old, &new, mode);");
info!(" assert!(check.compatible);");
Ok(())
}
fn main() -> Result<()> {
tracing_subscriber::fmt().with_max_level(tracing::Level::INFO).init();
println!("Schema Versioning and Evolution Examples");
println!("========================================\n");
println!("Backward-compatible schema evolution for distributed systems.");
println!();
basic_versioning_example()?;
println!();
compatibility_checking_example()?;
println!();
backward_compatibility_example()?;
println!();
forward_compatibility_example()?;
println!();
full_compatibility_example()?;
println!();
strict_compatibility_example()?;
println!();
evolution_operations_example()?;
println!();
migration_scenario_example()?;
println!();
deprecation_example()?;
println!();
best_practices_example()?;
println!();
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
println!("All examples completed!");
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
println!("Compatibility Modes Summary:");
println!(" • None - No checking");
println!(" • Backward - New code reads old data");
println!(" • Forward - Old code reads new data");
println!(" • Full - Both directions work");
println!(" • Strict - No changes allowed");
println!();
println!("Common Patterns:");
println!(" ✓ Add optional fields");
println!(" ✓ Make required → optional");
println!(" ✓ Deprecate before removing");
println!(" ✗ Change field types");
println!(" ✗ Add required fields");
println!(" ✗ Remove required fields");
Ok(())
}