use serde_json::Value;
use crate::genome::migration::{MigrationError, MigrationStepDiagnostics, Migrator};
use crate::genome::migrator::migrate_genome;
use crate::genome::schema::GenomeSchemaVersion;
#[derive(Debug, Default, Clone, Copy)]
pub struct V2ToV3Migrator;
impl V2ToV3Migrator {
pub const fn new() -> Self {
Self
}
}
impl Migrator for V2ToV3Migrator {
fn from_version(&self) -> GenomeSchemaVersion {
GenomeSchemaVersion(2)
}
fn to_version(&self) -> GenomeSchemaVersion {
GenomeSchemaVersion(3)
}
fn name(&self) -> &'static str {
"v2_to_v3"
}
fn migrate(&self, genome: &mut Value) -> Result<MigrationStepDiagnostics, MigrationError> {
let result = migrate_genome(genome).map_err(|e| MigrationError::StepFailed {
name: "v2_to_v3",
from: GenomeSchemaVersion(2),
to: GenomeSchemaVersion(3),
reason: e.to_string(),
})?;
let mut diag =
MigrationStepDiagnostics::new(GenomeSchemaVersion(2), GenomeSchemaVersion(3));
if result.cortical_ids_migrated > 0 {
diag.record(format!(
"renamed {} cortical IDs to v3 template-compliant form",
result.cortical_ids_migrated
));
for (old, new) in result.id_mapping.iter().take(5) {
diag.record(format!("'{old}' -> '{new}'"));
}
}
for w in &result.warnings {
diag.record(format!("warning: {w}"));
}
*genome = result.genome;
Ok(diag)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn declares_v2_to_v3_endpoints() {
let m = V2ToV3Migrator::new();
assert_eq!(m.from_version(), GenomeSchemaVersion(2));
assert_eq!(m.to_version(), GenomeSchemaVersion(3));
assert_eq!(m.name(), "v2_to_v3");
}
#[test]
fn no_op_on_already_compliant_genome() {
let m = V2ToV3Migrator::new();
let mut g = json!({
"blueprint": {},
"brain_regions": {},
"neuron_morphologies": {}
});
let snapshot = g.clone();
let diag = m.migrate(&mut g).unwrap();
assert_eq!(diag.from_version, GenomeSchemaVersion(2));
assert_eq!(diag.to_version, GenomeSchemaVersion(3));
assert!(diag.transformations.is_empty());
assert_eq!(g["blueprint"], snapshot["blueprint"]);
assert_eq!(g["brain_regions"], snapshot["brain_regions"]);
}
#[test]
fn renames_legacy_cortical_ids() {
let m = V2ToV3Migrator::new();
let mut g = json!({
"blueprint": {
"_power": {
"cortical_name": "Power",
"block_boundaries": [1, 1, 1],
"relative_coordinate": [0, 0, 0],
"cortical_type": "INTERCONNECT"
}
},
"brain_regions": {},
"neuron_morphologies": {}
});
let diag = m.migrate(&mut g).unwrap();
let blueprint = g["blueprint"].as_object().unwrap();
assert!(
!blueprint.contains_key("_power"),
"legacy ID '_power' should have been renamed; blueprint keys: {:?}",
blueprint.keys().collect::<Vec<_>>()
);
assert_eq!(
blueprint.len(),
1,
"rename must preserve area count; blueprint keys: {:?}",
blueprint.keys().collect::<Vec<_>>()
);
assert!(
diag.transformations.iter().any(|t| t.contains("renamed")),
"diagnostics should record at least one rename; got: {:?}",
diag.transformations
);
}
}