use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use tracing::warn;
use crate::types::{EvoError, EvoResult};
use feagi_structures::genomic::brain_regions::RegionID;
use feagi_structures::genomic::cortical_area::CorticalID;
use feagi_structures::genomic::cortical_area::{
CorticalArea, CorticalAreaDimensions as Dimensions,
};
use feagi_structures::genomic::descriptors::GenomeCoordinate3D;
use feagi_structures::genomic::{BrainRegion, RegionType};
#[derive(Debug, Clone)]
pub struct ParsedGenome {
pub genome_id: String,
pub genome_title: String,
pub version: String,
pub cortical_areas: Vec<CorticalArea>,
pub brain_regions: Vec<(BrainRegion, Option<String>)>,
pub neuron_morphologies: HashMap<String, Value>,
pub physiology: Option<Value>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RawGenome {
pub genome_id: Option<String>,
pub genome_title: Option<String>,
pub genome_description: Option<String>,
pub version: String,
pub blueprint: HashMap<String, RawCorticalArea>,
#[serde(default)]
pub brain_regions: HashMap<String, RawBrainRegion>,
#[serde(default)]
pub neuron_morphologies: HashMap<String, Value>,
#[serde(default)]
pub physiology: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub brain_regions_root: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RawCorticalArea {
pub cortical_name: Option<String>,
pub block_boundaries: Option<Vec<u32>>,
pub relative_coordinate: Option<Vec<i32>>,
pub cortical_type: Option<String>,
pub group_id: Option<String>,
pub sub_group_id: Option<String>,
pub per_voxel_neuron_cnt: Option<u32>,
pub cortical_mapping_dst: Option<Value>,
pub synapse_attractivity: Option<f32>,
pub refractory_period: Option<u32>,
pub firing_threshold: Option<f32>,
pub firing_threshold_limit: Option<f32>,
pub firing_threshold_increment_x: Option<f32>,
pub firing_threshold_increment_y: Option<f32>,
pub firing_threshold_increment_z: Option<f32>,
pub leak_coefficient: Option<f32>,
pub leak_variability: Option<f32>,
pub neuron_excitability: Option<f32>,
pub postsynaptic_current: Option<f32>,
pub postsynaptic_current_max: Option<f32>,
pub degeneration: Option<f32>,
pub psp_uniform_distribution: Option<bool>,
pub mp_charge_accumulation: Option<bool>,
pub mp_driven_psp: Option<bool>,
pub visualization: Option<bool>,
pub burst_engine_activation: Option<bool>,
#[serde(rename = "2d_coordinate")]
pub coordinate_2d: Option<Vec<i32>>,
pub is_mem_type: Option<bool>,
pub longterm_mem_threshold: Option<u32>,
pub lifespan_growth_rate: Option<f32>,
pub init_lifespan: Option<u32>,
pub temporal_depth: Option<u32>,
pub consecutive_fire_cnt_max: Option<u32>,
pub snooze_length: Option<u32>,
#[serde(flatten)]
pub other: HashMap<String, Value>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RawBrainRegion {
#[serde(alias = "name")]
pub title: Option<String>,
pub description: Option<String>,
pub parent_region_id: Option<String>,
pub coordinate_2d: Option<Vec<i32>>,
pub coordinate_3d: Option<Vec<i32>>,
#[serde(alias = "cortical_areas")]
pub areas: Option<Vec<String>>,
pub regions: Option<Vec<String>>,
pub inputs: Option<Vec<String>>,
pub outputs: Option<Vec<String>>,
pub designated_inputs: Option<Vec<String>>,
pub designated_outputs: Option<Vec<String>>,
pub signature: Option<String>,
pub properties: Option<HashMap<String, Value>>,
}
fn convert_dstmap_keys_to_base64(dstmap: &Value) -> Value {
if let Some(dstmap_obj) = dstmap.as_object() {
let mut converted = serde_json::Map::new();
for (dest_id_str, mapping_value) in dstmap_obj {
match string_to_cortical_id(dest_id_str) {
Ok(dest_cortical_id) => {
converted.insert(dest_cortical_id.as_base_64(), mapping_value.clone());
}
Err(e) => {
tracing::warn!(
"Failed to convert dstmap key '{}' to base64: {}, keeping original",
dest_id_str,
e
);
converted.insert(dest_id_str.clone(), mapping_value.clone());
}
}
}
Value::Object(converted)
} else {
dstmap.clone()
}
}
pub fn string_to_cortical_id(id_str: &str) -> EvoResult<CorticalID> {
use feagi_structures::genomic::cortical_area::CoreCorticalType;
if let Ok(cortical_id) = CorticalID::try_from_base_64(id_str) {
let mut bytes = [0u8; CorticalID::CORTICAL_ID_LENGTH];
cortical_id.write_id_to_bytes(&mut bytes);
if bytes == *b"___power" {
return Ok(CoreCorticalType::Power.to_cortical_id());
}
if bytes == *b"___death" {
return Ok(CoreCorticalType::Death.to_cortical_id());
}
if bytes == *b"___fatig" {
return Ok(CoreCorticalType::Fatigue.to_cortical_id());
}
return Ok(cortical_id);
}
if id_str == "_power" {
return Ok(CoreCorticalType::Power.to_cortical_id());
}
if id_str == "___pwr" {
return Ok(CoreCorticalType::Power.to_cortical_id());
}
if id_str == "___power" {
return Ok(CoreCorticalType::Power.to_cortical_id());
}
if id_str == "___pwr__" {
return Ok(CoreCorticalType::Power.to_cortical_id());
}
if id_str == "___death" {
return Ok(CoreCorticalType::Death.to_cortical_id());
}
if id_str == "___fatig" {
return Ok(CoreCorticalType::Fatigue.to_cortical_id());
}
if id_str == "_death" {
return Ok(CoreCorticalType::Death.to_cortical_id());
}
if id_str == "_fatigue" {
return Ok(CoreCorticalType::Fatigue.to_cortical_id());
}
if id_str.len() == 6 || id_str.len() == 8 {
CorticalID::try_from_legacy_ascii(id_str).map_err(|e| {
EvoError::InvalidArea(format!("Failed to convert cortical_id '{}': {}", id_str, e))
})
} else {
Err(EvoError::InvalidArea(format!(
"Invalid cortical_id length: '{}' (expected 6 or 8 ASCII chars, or base64)",
id_str
)))
}
}
pub struct GenomeParser;
impl GenomeParser {
fn normalize_brain_region_cortical_id_list_properties(region: &mut BrainRegion, keys: &[&str]) {
for key in keys {
let Some(val) = region.get_property(key) else {
continue;
};
let Some(arr) = val.as_array() else {
continue;
};
let mut out: Vec<String> = Vec::new();
for item in arr {
let Some(s) = item.as_str() else {
continue;
};
match string_to_cortical_id(s) {
Ok(cortical_id) => out.push(cortical_id.as_base_64()),
Err(e) => {
warn!(target: "feagi-evo",
"Failed to convert brain region '{}' entry '{}': {}. Skipping.",
key, s, e);
}
}
}
if out.is_empty() {
region.properties.remove(*key);
} else {
region.add_property((*key).to_string(), serde_json::json!(out));
}
}
}
pub fn parse(json_str: &str) -> EvoResult<ParsedGenome> {
let raw: RawGenome = serde_json::from_str(json_str)
.map_err(|e| EvoError::InvalidGenome(format!("Failed to parse JSON: {}", e)))?;
if !raw.version.starts_with("2.") && !raw.version.starts_with("3.") && raw.version != "3" {
return Err(EvoError::InvalidGenome(format!(
"Unsupported genome version: {}. Expected 2.x or 3.x",
raw.version
)));
}
let cortical_areas = Self::parse_cortical_areas(&raw.blueprint)?;
let brain_regions = Self::parse_brain_regions(&raw.brain_regions)?;
Ok(ParsedGenome {
genome_id: raw.genome_id.unwrap_or_else(|| "unknown".to_string()),
genome_title: raw.genome_title.unwrap_or_else(|| "Untitled".to_string()),
version: raw.version,
cortical_areas,
brain_regions,
neuron_morphologies: raw.neuron_morphologies,
physiology: raw.physiology,
})
}
fn parse_cortical_areas(
blueprint: &HashMap<String, RawCorticalArea>,
) -> EvoResult<Vec<CorticalArea>> {
let mut areas = Vec::with_capacity(blueprint.len());
for (cortical_id_str, raw_area) in blueprint.iter() {
if cortical_id_str.is_empty() {
warn!(target: "feagi-evo","Skipping empty cortical_id");
continue;
}
let cortical_id = match string_to_cortical_id(cortical_id_str) {
Ok(id) => id,
Err(e) => {
warn!(target: "feagi-evo","Skipping invalid cortical_id '{}': {}", cortical_id_str, e);
continue;
}
};
let name = raw_area
.cortical_name
.clone()
.unwrap_or_else(|| cortical_id_str.clone());
let dimensions = if let Some(boundaries) = &raw_area.block_boundaries {
if boundaries.len() != 3 {
return Err(EvoError::InvalidArea(format!(
"Invalid block_boundaries for {}: expected 3 values, got {}",
cortical_id_str,
boundaries.len()
)));
}
Dimensions::new(boundaries[0], boundaries[1], boundaries[2])
.map_err(|e| EvoError::InvalidArea(format!("Invalid dimensions: {}", e)))?
} else {
warn!(target: "feagi-evo","Cortical area {} missing block_boundaries, defaulting to 1x1x1", cortical_id_str);
Dimensions::new(1, 1, 1).map_err(|e| {
EvoError::InvalidArea(format!("Invalid default dimensions: {}", e))
})?
};
let position = if let Some(coords) = &raw_area.relative_coordinate {
if coords.len() != 3 {
return Err(EvoError::InvalidArea(format!(
"Invalid relative_coordinate for {}: expected 3 values, got {}",
cortical_id_str,
coords.len()
)));
}
GenomeCoordinate3D::new(coords[0], coords[1], coords[2])
} else {
warn!(target: "feagi-evo","Cortical area {} missing relative_coordinate, defaulting to (0,0,0)", cortical_id_str);
GenomeCoordinate3D::new(0, 0, 0)
};
let cortical_type = cortical_id.as_cortical_type().map_err(|e| {
EvoError::InvalidArea(format!(
"Failed to determine cortical type from ID {}: {}",
cortical_id_str, e
))
})?;
let mut area = CorticalArea::new(
cortical_id,
0, name,
dimensions,
position,
cortical_type,
)?;
if let Some(ref cortical_type_str) = raw_area.cortical_type {
area.properties.insert(
"cortical_group".to_string(),
serde_json::json!(cortical_type_str),
);
}
if let Some(v) = raw_area.synapse_attractivity {
area.properties
.insert("synapse_attractivity".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.refractory_period {
area.properties
.insert("refractory_period".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.firing_threshold {
area.properties
.insert("firing_threshold".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.firing_threshold_limit {
area.properties
.insert("firing_threshold_limit".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.firing_threshold_increment_x {
area.properties.insert(
"firing_threshold_increment_x".to_string(),
serde_json::json!(v),
);
}
if let Some(v) = raw_area.firing_threshold_increment_y {
area.properties.insert(
"firing_threshold_increment_y".to_string(),
serde_json::json!(v),
);
}
if let Some(v) = raw_area.firing_threshold_increment_z {
area.properties.insert(
"firing_threshold_increment_z".to_string(),
serde_json::json!(v),
);
}
if let Some(v) = raw_area.leak_coefficient {
area.properties
.insert("leak_coefficient".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.leak_variability {
area.properties
.insert("leak_variability".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.neuron_excitability {
area.properties
.insert("neuron_excitability".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.postsynaptic_current {
area.properties
.insert("postsynaptic_current".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.postsynaptic_current_max {
area.properties
.insert("postsynaptic_current_max".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.degeneration {
area.properties
.insert("degeneration".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.psp_uniform_distribution {
area.properties
.insert("psp_uniform_distribution".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.mp_charge_accumulation {
area.properties
.insert("mp_charge_accumulation".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.mp_driven_psp {
area.properties
.insert("mp_driven_psp".to_string(), serde_json::json!(v));
tracing::info!(
target: "feagi-evo",
"[GENOME-LOAD] Loaded mp_driven_psp={} for area {}",
v,
cortical_id_str
);
} else {
tracing::debug!(
target: "feagi-evo",
"[GENOME-LOAD] mp_driven_psp not found in raw_area for {}, will use default=false",
cortical_id_str
);
}
if let Some(v) = raw_area.visualization {
area.properties
.insert("visualization".to_string(), serde_json::json!(v));
area.properties
.insert("visible".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.burst_engine_activation {
area.properties
.insert("burst_engine_active".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.is_mem_type {
area.properties
.insert("is_mem_type".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.longterm_mem_threshold {
area.properties
.insert("longterm_mem_threshold".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.lifespan_growth_rate {
area.properties
.insert("lifespan_growth_rate".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.init_lifespan {
area.properties
.insert("init_lifespan".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.temporal_depth {
area.properties
.insert("temporal_depth".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.consecutive_fire_cnt_max {
area.properties
.insert("consecutive_fire_cnt_max".to_string(), serde_json::json!(v));
area.properties
.insert("consecutive_fire_limit".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.snooze_length {
area.properties
.insert("snooze_period".to_string(), serde_json::json!(v));
}
if let Some(v) = &raw_area.group_id {
area.properties
.insert("group_id".to_string(), serde_json::json!(v));
}
if let Some(v) = &raw_area.sub_group_id {
area.properties
.insert("sub_group_id".to_string(), serde_json::json!(v));
}
if let Some(v) = raw_area.per_voxel_neuron_cnt {
area.properties
.insert("neurons_per_voxel".to_string(), serde_json::json!(v));
}
if let Some(v) = &raw_area.cortical_mapping_dst {
let converted_dstmap = convert_dstmap_keys_to_base64(v);
area.properties
.insert("cortical_mapping_dst".to_string(), converted_dstmap);
}
if let Some(v) = &raw_area.coordinate_2d {
area.properties
.insert("2d_coordinate".to_string(), serde_json::json!(v));
}
for (key, value) in &raw_area.other {
area.properties.insert(key.clone(), value.clone());
}
areas.push(area);
}
Ok(areas)
}
fn parse_brain_regions(
raw_regions: &HashMap<String, RawBrainRegion>,
) -> EvoResult<Vec<(BrainRegion, Option<String>)>> {
let mut regions = Vec::with_capacity(raw_regions.len());
for (region_id_str, raw_region) in raw_regions.iter() {
let title = raw_region
.title
.clone()
.unwrap_or_else(|| region_id_str.clone());
let region_id = match RegionID::from_string(region_id_str) {
Ok(id) => id,
Err(_) => {
RegionID::new()
}
};
let region_type = RegionType::Undefined;
let mut region = BrainRegion::new(region_id, title, region_type)?;
if let Some(props) = &raw_region.properties {
for (k, v) in props {
region.add_property(k.clone(), v.clone());
}
}
if let Some(areas) = &raw_region.areas {
for area_id in areas {
match string_to_cortical_id(area_id) {
Ok(cortical_id) => {
region.add_area(cortical_id);
}
Err(e) => {
warn!(target: "feagi-evo",
"Failed to convert brain region area ID '{}' to CorticalID: {}. Skipping.",
area_id, e);
}
}
}
}
if let Some(desc) = &raw_region.description {
region.add_property("description".to_string(), serde_json::json!(desc));
}
if let Some(coord_2d) = &raw_region.coordinate_2d {
region.add_property("coordinate_2d".to_string(), serde_json::json!(coord_2d));
}
if let Some(coord_3d) = &raw_region.coordinate_3d {
region.add_property("coordinate_3d".to_string(), serde_json::json!(coord_3d));
}
if let Some(inputs) = &raw_region.inputs {
let input_ids: Vec<String> = inputs
.iter()
.filter_map(|id| match string_to_cortical_id(id) {
Ok(cortical_id) => Some(cortical_id.as_base_64()),
Err(e) => {
warn!(target: "feagi-evo",
"Failed to convert brain region input ID '{}': {}. Skipping.",
id, e);
None
}
})
.collect();
if !input_ids.is_empty() {
region.add_property("inputs".to_string(), serde_json::json!(input_ids));
}
}
if let Some(outputs) = &raw_region.outputs {
let output_ids: Vec<String> = outputs
.iter()
.filter_map(|id| match string_to_cortical_id(id) {
Ok(cortical_id) => Some(cortical_id.as_base_64()),
Err(e) => {
warn!(target: "feagi-evo",
"Failed to convert brain region output ID '{}': {}. Skipping.",
id, e);
None
}
})
.collect();
if !output_ids.is_empty() {
region.add_property("outputs".to_string(), serde_json::json!(output_ids));
}
}
if let Some(signature) = &raw_region.signature {
region.add_property("signature".to_string(), serde_json::json!(signature));
}
if let Some(d) = &raw_region.designated_inputs {
let ids: Vec<String> = d
.iter()
.filter_map(|id| match string_to_cortical_id(id) {
Ok(cortical_id) => Some(cortical_id.as_base_64()),
Err(e) => {
warn!(target: "feagi-evo",
"Failed to convert designated_inputs entry '{}': {}. Skipping.",
id, e);
None
}
})
.collect();
if !ids.is_empty() {
region.add_property("designated_inputs".to_string(), serde_json::json!(ids));
}
}
if let Some(d) = &raw_region.designated_outputs {
let ids: Vec<String> = d
.iter()
.filter_map(|id| match string_to_cortical_id(id) {
Ok(cortical_id) => Some(cortical_id.as_base_64()),
Err(e) => {
warn!(target: "feagi-evo",
"Failed to convert designated_outputs entry '{}': {}. Skipping.",
id, e);
None
}
})
.collect();
if !ids.is_empty() {
region.add_property("designated_outputs".to_string(), serde_json::json!(ids));
}
}
Self::normalize_brain_region_cortical_id_list_properties(
&mut region,
&[
"inputs",
"outputs",
"designated_inputs",
"designated_outputs",
],
);
let parent_id = raw_region.parent_region_id.clone();
if let Some(ref parent_id_str) = parent_id {
region.add_property(
"parent_region_id".to_string(),
serde_json::json!(parent_id_str),
);
}
regions.push((region, parent_id));
}
Ok(regions)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_minimal_genome() {
let json = r#"{
"version": "2.1",
"blueprint": {
"_power": {
"cortical_name": "Test Area",
"block_boundaries": [10, 10, 10],
"relative_coordinate": [0, 0, 0],
"cortical_type": "CORE"
}
},
"brain_regions": {
"root": {
"title": "Root",
"parent_region_id": null,
"areas": ["_power"]
}
}
}"#;
let parsed = GenomeParser::parse(json).unwrap();
assert_eq!(parsed.version, "2.1");
assert_eq!(parsed.cortical_areas.len(), 1);
assert_eq!(
parsed.cortical_areas[0].cortical_id.as_base_64(),
"X19fcG93ZXI="
);
assert_eq!(parsed.cortical_areas[0].name, "Test Area");
assert_eq!(parsed.brain_regions.len(), 1);
assert!(parsed.cortical_areas[0]
.cortical_id
.as_cortical_type()
.is_ok());
}
#[test]
fn test_parse_multiple_areas() {
let json = r#"{
"version": "2.1",
"blueprint": {
"_power": {
"cortical_name": "Area 1",
"cortical_type": "CORE",
"block_boundaries": [5, 5, 5],
"relative_coordinate": [0, 0, 0]
},
"_death": {
"cortical_name": "Area 2",
"cortical_type": "CORE",
"block_boundaries": [10, 10, 10],
"relative_coordinate": [5, 0, 0]
}
}
}"#;
let parsed = GenomeParser::parse(json).unwrap();
assert_eq!(parsed.cortical_areas.len(), 2);
for area in &parsed.cortical_areas {
assert!(
area.cortical_id.as_cortical_type().is_ok(),
"Area {} should have cortical_type_new populated",
area.cortical_id
);
}
}
#[test]
fn test_string_to_cortical_id_legacy_power_shorthand() {
use feagi_structures::genomic::cortical_area::CoreCorticalType;
let id = string_to_cortical_id("___pwr").unwrap();
assert_eq!(
id.as_base_64(),
CoreCorticalType::Power.to_cortical_id().as_base_64()
);
}
#[test]
fn test_string_to_cortical_id_legacy_power_padded() {
use feagi_structures::genomic::cortical_area::CoreCorticalType;
let id = string_to_cortical_id("___pwr__").unwrap();
assert_eq!(
id.as_base_64(),
CoreCorticalType::Power.to_cortical_id().as_base_64()
);
}
#[test]
fn test_parse_with_properties() {
let json = r#"{
"version": "2.1",
"blueprint": {
"mem001": {
"cortical_name": "Memory Area",
"block_boundaries": [8, 8, 8],
"relative_coordinate": [0, 0, 0],
"cortical_type": "MEMORY",
"is_mem_type": true,
"firing_threshold": 50.0,
"leak_coefficient": 0.9
}
}
}"#;
let parsed = GenomeParser::parse(json).unwrap();
assert_eq!(parsed.cortical_areas.len(), 1);
let area = &parsed.cortical_areas[0];
use feagi_structures::genomic::cortical_area::CorticalAreaType;
assert!(matches!(area.cortical_type, CorticalAreaType::Memory(_)));
assert!(area.properties.contains_key("is_mem_type"));
assert!(area.properties.contains_key("firing_threshold"));
assert!(area.properties.contains_key("cortical_group"));
assert!(
area.cortical_id.as_cortical_type().is_ok(),
"cortical_id should be parseable to cortical_type"
);
if let Ok(cortical_type) = area.cortical_id.as_cortical_type() {
use feagi_structures::genomic::cortical_area::CorticalAreaType;
assert!(
matches!(cortical_type, CorticalAreaType::Memory(_)),
"Should be classified as MEMORY type"
);
}
}
#[test]
fn test_parse_v3_brain_region_nested_properties_retains_designated_io() {
let json = r#"{
"version": "3.0",
"blueprint": {
"_power": {
"cortical_name": "Core",
"block_boundaries": [10, 10, 10],
"relative_coordinate": [0, 0, 0],
"cortical_type": "CORE"
}
},
"brain_regions": {
"550e8400-e29b-41d4-a716-446655440000": {
"name": "Sub",
"cortical_areas": ["_power"],
"properties": {
"designated_inputs": ["_power"],
"designated_outputs": []
}
}
}
}"#;
let parsed = GenomeParser::parse(json).unwrap();
assert_eq!(parsed.brain_regions.len(), 1);
let (region, _) = &parsed.brain_regions[0];
let di = region
.get_property("designated_inputs")
.and_then(|v| v.as_array())
.expect("designated_inputs");
assert_eq!(di.len(), 1);
assert_eq!(di[0].as_str().unwrap(), "X19fcG93ZXI=");
}
#[test]
fn test_invalid_version() {
let json = r#"{
"version": "1.0",
"blueprint": {}
}"#;
let result = GenomeParser::parse(json);
assert!(result.is_err());
}
#[test]
fn test_malformed_json() {
let json = r#"{ "version": "2.1", "blueprint": { malformed"#;
let result = GenomeParser::parse(json);
assert!(result.is_err());
}
#[test]
fn test_cortical_type_new_population() {
use feagi_structures::genomic::cortical_area::CoreCorticalType;
let power_id = CoreCorticalType::Power.to_cortical_id().as_base_64();
let json = format!(
r#"{{
"version": "2.1",
"blueprint": {{
"cvision1": {{
"cortical_name": "Test Custom Vision",
"cortical_type": "CUSTOM",
"block_boundaries": [10, 10, 1],
"relative_coordinate": [0, 0, 0]
}},
"cmotor01": {{
"cortical_name": "Test Custom Motor",
"cortical_type": "CUSTOM",
"block_boundaries": [5, 5, 1],
"relative_coordinate": [0, 0, 0]
}},
"{}": {{
"cortical_name": "Test Core",
"cortical_type": "CORE",
"block_boundaries": [1, 1, 1],
"relative_coordinate": [0, 0, 0]
}}
}}
}}"#,
power_id
);
let parsed = GenomeParser::parse(&json).unwrap();
assert_eq!(parsed.cortical_areas.len(), 3);
for area in &parsed.cortical_areas {
assert!(
area.cortical_id.as_cortical_type().is_ok(),
"Area {} should have cortical_type_new populated",
area.cortical_id
);
assert!(
area.properties.contains_key("cortical_group"),
"Area {} should have cortical_group property",
area.cortical_id
);
if let Some(prop_group) = area
.properties
.get("cortical_group")
.and_then(|v| v.as_str())
{
assert!(
!prop_group.is_empty(),
"Area {} should have non-empty cortical_group property",
area.cortical_id.as_base_64()
);
}
}
}
}