use std::collections::BTreeMap;
use std::collections::BTreeSet;
use indexmap::IndexMap;
use openehr_base::v1_3::prelude::Interval;
use openehr_base::v1_3::prelude::MultiplicityInterval;
use openehr_base::v1_3::prelude::PointInterval;
use openehr_base::v1_3::prelude::ProperInterval;
use crate::v1_1::bmm::core::bmm_class::BmmClass;
use crate::v1_1::bmm::core::bmm_class::BmmClassData;
use crate::v1_1::bmm::core::bmm_container_property::BmmContainerProperty;
use crate::v1_1::bmm::core::bmm_container_type::BmmContainerType;
use crate::v1_1::bmm::core::bmm_container_type::BmmContainerTypeData;
use crate::v1_1::bmm::core::bmm_enumeration::BmmEnumeration;
use crate::v1_1::bmm::core::bmm_enumeration::BmmEnumerationData;
use crate::v1_1::bmm::core::bmm_enumeration_integer::BmmEnumerationInteger;
use crate::v1_1::bmm::core::bmm_enumeration_string::BmmEnumerationString;
use crate::v1_1::bmm::core::bmm_generic_class::BmmGenericClass;
use crate::v1_1::bmm::core::bmm_generic_parameter::BmmGenericParameter;
use crate::v1_1::bmm::core::bmm_generic_type::BmmGenericType;
use crate::v1_1::bmm::core::bmm_indexed_container_type::BmmIndexedContainerType;
use crate::v1_1::bmm::core::bmm_model::BmmModel;
use crate::v1_1::bmm::core::bmm_open_type::BmmOpenType;
use crate::v1_1::bmm::core::bmm_package::BmmPackage;
use crate::v1_1::bmm::core::bmm_property::BmmProperty;
use crate::v1_1::bmm::core::bmm_property::BmmPropertyData;
use crate::v1_1::bmm::core::bmm_simple_type::BmmSimpleType;
use crate::v1_1::bmm::core::bmm_type::BmmType;
use crate::v1_1::bmm_persistence::error::PBmmReadError;
use crate::v1_1::bmm_persistence::generic_binding::ActualParameter;
use crate::v1_1::bmm_persistence::p_bmm_base_type::PBmmBaseType;
use crate::v1_1::bmm_persistence::p_bmm_class::PBmmClass;
use crate::v1_1::bmm_persistence::p_bmm_container_property::PBmmContainerProperty;
use crate::v1_1::bmm_persistence::p_bmm_container_type::PBmmContainerType;
use crate::v1_1::bmm_persistence::p_bmm_enumeration::PBmmEnumeration;
use crate::v1_1::bmm_persistence::p_bmm_generic_type::PBmmGenericType;
use crate::v1_1::bmm_persistence::p_bmm_package::PBmmPackage;
use crate::v1_1::bmm_persistence::p_bmm_property::PBmmProperty;
use crate::v1_1::bmm_persistence::p_bmm_schema::PBmmSchema;
use crate::v1_1::bmm_persistence::p_bmm_type::PBmmType;
use openehr_base::containers::present;
macro_rules! class_from_core {
($ty:ident, $core:expr $(, $field:ident $(: $value:expr)?)* $(,)?) => {{
let core: ClassCore = $core;
$ty {
documentation: core.documentation,
name: core.name,
ancestors: core.ancestors,
package: core.package,
properties: core.properties,
source_schema_id: core.source_schema_id,
immediate_descendants: present(core.immediate_descendants),
is_abstract: core.is_abstract,
is_primitive_type: core.is_primitive_type,
is_override: core.is_override,
$($field $(: $value)?,)*
}
}};
}
pub fn create_bmm_model(schema: &PBmmSchema) -> Result<BmmModel, PBmmReadError> {
let builder = Builder::new(schema)?;
let mut class_definitions: BTreeMap<String, BmmClass> = BTreeMap::new();
for entry in builder.classes.values() {
class_definitions.insert(
entry.class.name().to_owned(),
builder.build_class(entry, EmbedDepth::Full)?,
);
}
let packages = builder.build_packages(&schema.packages, "")?;
Ok(BmmModel {
rm_publisher: schema.rm_publisher.clone(),
rm_release: schema.rm_release.clone(),
schema_name: schema.schema_name.clone(),
schema_revision: schema.schema_revision.clone(),
schema_lifecycle_state: schema.schema_lifecycle_state.clone(),
schema_author: schema.schema_author.clone(),
schema_description: schema.schema_description.clone(),
schema_contributors: schema.schema_contributors.clone(),
archetype_parent_class: schema.archetype_parent_class.clone(),
archetype_data_value_parent_class: schema.archetype_data_value_parent_class.clone(),
archetype_rm_closure_packages: schema.archetype_rm_closure_packages.clone(),
archetype_visualise_descendants_of: schema.archetype_visualise_descendants_of.clone(),
documentation: None,
packages: if packages.is_empty() {
None
} else {
Some(packages)
},
class_definitions: if class_definitions.is_empty() {
None
} else {
Some(class_definitions)
},
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum EmbedDepth {
Full,
Ancestor,
Stub,
Bare,
}
pub(super) struct ClassEntry<'a> {
pub(super) class: &'a PBmmClass,
pub(super) is_primitive_type: bool,
}
pub(super) struct Builder<'a> {
pub(super) schema_id: String,
pub(super) classes: BTreeMap<String, ClassEntry<'a>>,
pub(super) owning_package: BTreeMap<String, String>,
pub(super) findings: std::cell::RefCell<Vec<super::validate::PBmmValidityFinding>>,
descendants: BTreeMap<String, Vec<String>>,
}
impl<'a> Builder<'a> {
pub(super) fn new(schema: &'a PBmmSchema) -> Result<Self, PBmmReadError> {
let mut classes: BTreeMap<String, ClassEntry<'a>> = BTreeMap::new();
for (class, is_primitive_type) in schema
.primitive_types
.iter()
.flat_map(BTreeMap::values)
.map(|class| (class, true))
.chain(
schema
.class_definitions
.iter()
.flat_map(BTreeMap::values)
.map(|class| (class, false)),
)
{
classes
.entry(class.name().to_uppercase())
.or_insert(ClassEntry {
class,
is_primitive_type,
});
}
let mut owning_package: BTreeMap<String, String> = BTreeMap::new();
index_packages(&schema.packages, "", &classes, &mut owning_package)?;
let mut descendants: BTreeMap<String, Vec<String>> = BTreeMap::new();
for entry in classes.values() {
for parent in entry.class.ancestor_names() {
let key = parent.to_uppercase();
if classes.contains_key(&key) {
descendants
.entry(key)
.or_default()
.push(entry.class.name().to_owned());
}
}
}
for names in descendants.values_mut() {
names.sort_unstable();
names.dedup();
}
Ok(Self {
schema_id: schema.schema_id(),
classes,
owning_package,
descendants,
findings: std::cell::RefCell::new(Vec::new()),
})
}
pub(super) fn entry(
&self,
context: &str,
name: &str,
) -> Result<&ClassEntry<'a>, PBmmReadError> {
self.classes
.get(&name.to_uppercase())
.ok_or_else(|| PBmmReadError::UnknownType {
context: context.to_owned(),
type_name: name.to_owned(),
})
}
fn package_of(&self, name: &str) -> Result<BmmPackage, PBmmReadError> {
let path = self
.owning_package
.get(&name.to_uppercase())
.ok_or_else(|| PBmmReadError::ClassNotInAnyPackage {
class: name.to_owned(),
})?;
Ok(BmmPackage {
documentation: None,
packages: None,
name: path.clone(),
classes: present(Vec::new()),
})
}
fn build_class(
&self,
entry: &ClassEntry<'a>,
depth: EmbedDepth,
) -> Result<BmmClass, PBmmReadError> {
let persisted = entry.class;
let name = persisted.name();
let core = ClassCore {
documentation: persisted.documentation().map(str::to_owned),
name: name.to_owned(),
ancestors: self.build_ancestors(persisted, depth)?,
package: self.package_of(name)?,
properties: match depth {
EmbedDepth::Stub | EmbedDepth::Bare => None,
EmbedDepth::Full | EmbedDepth::Ancestor => self.build_properties(persisted)?,
},
source_schema_id: persisted
.source_schema_id()
.unwrap_or(&self.schema_id)
.to_owned(),
immediate_descendants: self
.descendants
.get(&name.to_uppercase())
.cloned()
.unwrap_or_default(),
is_abstract: persisted.is_abstract(),
is_primitive_type: entry.is_primitive_type,
is_override: persisted.is_override(),
};
if let PBmmClass::PBmmEnumeration(enumeration) = persisted {
check_enumeration_validity(name, enumeration)?;
return Ok(BmmClass::BmmEnumeration(build_enumeration(
core,
enumeration,
)));
}
if persisted.is_generic() {
return Ok(BmmClass::BmmGenericClass(class_from_core!(
BmmGenericClass,
core,
generic_parameters: self.build_generic_parameters(persisted, depth)?,
)));
}
Ok(BmmClass::BmmClass(class_from_core!(BmmClassData, core)))
}
fn build_ancestors(
&self,
persisted: &PBmmClass,
depth: EmbedDepth,
) -> Result<Option<BTreeMap<String, BmmClass>>, PBmmReadError> {
let next = match depth {
EmbedDepth::Stub | EmbedDepth::Bare => return Ok(None),
EmbedDepth::Full => EmbedDepth::Ancestor,
EmbedDepth::Ancestor => EmbedDepth::Stub,
};
let mut out: BTreeMap<String, BmmClass> = BTreeMap::new();
for parent in persisted.ancestor_names() {
let entry = self.classes.get(&parent.to_uppercase()).ok_or_else(|| {
PBmmReadError::UnknownAncestor {
class: persisted.name().to_owned(),
ancestor: parent.clone(),
}
})?;
out.insert(
entry.class.name().to_owned(),
self.build_class(entry, next)?,
);
}
Ok(if out.is_empty() { None } else { Some(out) })
}
fn build_generic_parameters(
&self,
persisted: &PBmmClass,
depth: EmbedDepth,
) -> Result<IndexMap<String, BmmGenericParameter>, PBmmReadError> {
if depth == EmbedDepth::Bare {
return Ok(IndexMap::new());
}
let mut out = IndexMap::new();
for (key, parameter) in persisted.generic_parameter_defs().into_iter().flatten() {
out.insert(
key.clone(),
self.build_generic_parameter(persisted.name(), parameter)?,
);
}
Ok(out)
}
fn build_generic_parameter(
&self,
owner: &str,
parameter: &crate::v1_1::bmm_persistence::p_bmm_generic_parameter::PBmmGenericParameter,
) -> Result<BmmGenericParameter, PBmmReadError> {
let conforms_to_type = match parameter.conforms_to_type.as_deref() {
None => None,
Some(constrainer) => {
let context = format!("class `{owner}` generic parameter `{}`", parameter.name);
Some(self.build_class(self.entry(&context, constrainer)?, EmbedDepth::Bare)?)
}
};
Ok(BmmGenericParameter {
documentation: parameter.documentation.clone(),
name: parameter.name.clone(),
conforms_to_type,
inheritance_precursor: None,
})
}
fn build_properties(
&self,
persisted: &PBmmClass,
) -> Result<Option<BTreeMap<String, BmmProperty<BmmType>>>, PBmmReadError> {
let Some(properties) = persisted.properties() else {
return Ok(None);
};
let mut out = BTreeMap::new();
for (key, property) in properties {
out.insert(key.clone(), self.build_property(persisted, property)?);
}
Ok(if out.is_empty() { None } else { Some(out) })
}
#[expect(
clippy::too_many_lines,
reason = "one arm per P_BMM_PROPERTY subtype; splitting the dispatch would hide the five-way persisted-property → BMM_PROPERTY mapping"
)]
fn build_property(
&self,
owner: &PBmmClass,
property: &PBmmProperty,
) -> Result<BmmProperty<BmmType>, PBmmReadError> {
let class_name = owner.name();
match property {
PBmmProperty::PBmmSingleProperty(single) => {
let context = property_context(class_name, &single.name);
let r#type = match (
single.type_def.as_ref(),
single.type_ref.as_ref(),
single.r#type.as_deref(),
) {
(Some(type_def), _, _) => self.build_type(&context, type_def, owner)?,
(None, Some(type_ref), _) => {
self.build_named_type(&context, &type_ref.r#type, owner)?
}
(None, None, Some(name)) => self.build_named_type(&context, name, owner)?,
(None, None, None) => {
return Err(PBmmReadError::TypeDefinitionMissing { context });
}
};
Ok(BmmProperty::BmmProperty(BmmPropertyData {
documentation: single.documentation.clone(),
name: single.name.clone(),
is_mandatory: single.is_mandatory,
is_computed: single.is_computed,
r#type,
is_im_runtime: single.is_im_runtime,
is_im_infrastructure: single.is_im_infrastructure,
}))
}
PBmmProperty::PBmmSinglePropertyOpen(open) => {
let context = property_context(class_name, &open.name);
let parameter = match (open.type_ref.as_ref(), open.r#type.as_deref()) {
(Some(type_ref), _) => type_ref.r#type.as_str(),
(None, Some(name)) => name,
(None, None) => {
return Err(PBmmReadError::TypeDefinitionMissing { context });
}
};
let r#type =
BmmType::BmmOpenType(self.build_open_type(owner, &open.name, parameter)?);
Ok(BmmProperty::BmmProperty(BmmPropertyData {
documentation: open.documentation.clone(),
name: open.name.clone(),
is_mandatory: open.is_mandatory,
is_computed: open.is_computed,
r#type,
is_im_runtime: open.is_im_runtime,
is_im_infrastructure: open.is_im_infrastructure,
}))
}
PBmmProperty::PBmmGenericProperty(generic) => {
let context = property_context(class_name, &generic.name);
let Some(type_def) = generic.type_def.as_ref() else {
return Err(PBmmReadError::TypeDefinitionMissing { context });
};
let r#type =
BmmType::BmmGenericType(self.build_generic_type(&context, type_def, owner)?);
Ok(BmmProperty::BmmProperty(BmmPropertyData {
documentation: generic.documentation.clone(),
name: generic.name.clone(),
is_mandatory: generic.is_mandatory,
is_computed: generic.is_computed,
r#type,
is_im_runtime: generic.is_im_runtime,
is_im_infrastructure: generic.is_im_infrastructure,
}))
}
PBmmProperty::PBmmContainerProperty(PBmmContainerProperty::PBmmContainerProperty(
container,
)) => {
let context = property_context(class_name, &container.name);
let Some(type_def) = container.type_def.as_ref() else {
return Err(PBmmReadError::TypeDefinitionMissing { context });
};
let r#type = self.build_container_type(&context, type_def, owner)?;
Ok(BmmProperty::BmmContainerProperty(BmmContainerProperty {
documentation: container.documentation.clone(),
name: container.name.clone(),
is_mandatory: container.is_mandatory,
is_computed: container.is_computed,
r#type,
is_im_runtime: container.is_im_runtime,
is_im_infrastructure: container.is_im_infrastructure,
cardinality: container.cardinality.as_ref().map(multiplicity_of),
}))
}
PBmmProperty::PBmmContainerProperty(
PBmmContainerProperty::PBmmIndexedContainerProperty(indexed),
) => {
let context = property_context(class_name, &indexed.name);
let Some(type_def) = indexed.type_def.as_ref() else {
return Err(PBmmReadError::TypeDefinitionMissing { context });
};
let r#type = BmmContainerType::BmmIndexedContainerType(Box::new(
self.build_indexed_container_type(&context, type_def, owner)?,
));
Ok(BmmProperty::BmmContainerProperty(BmmContainerProperty {
documentation: indexed.documentation.clone(),
name: indexed.name.clone(),
is_mandatory: indexed.is_mandatory,
is_computed: indexed.is_computed,
r#type,
is_im_runtime: indexed.is_im_runtime,
is_im_infrastructure: indexed.is_im_infrastructure,
cardinality: indexed.cardinality.as_ref().map(multiplicity_of),
}))
}
}
}
fn build_named_type(
&self,
context: &str,
name: &str,
owner: &PBmmClass,
) -> Result<BmmType, PBmmReadError> {
if let Some(parameter) = self.find_generic_parameter(owner, name) {
return Ok(BmmType::BmmOpenType(BmmOpenType {
documentation: None,
generic_constraint: self.build_generic_parameter(owner.name(), parameter)?,
}));
}
Ok(BmmType::BmmSimpleType(BmmSimpleType {
documentation: None,
base_class: self.build_class(self.entry(context, name)?, EmbedDepth::Stub)?,
}))
}
fn build_type(
&self,
context: &str,
r#type: &PBmmType,
owner: &PBmmClass,
) -> Result<BmmType, PBmmReadError> {
match r#type {
PBmmType::PBmmSimpleType(simple) => {
self.build_named_type(context, &simple.r#type, owner)
}
PBmmType::PBmmOpenType(open) => Ok(BmmType::BmmOpenType(self.build_open_type(
owner,
context,
&open.r#type,
)?)),
PBmmType::PBmmGenericType(generic) => Ok(BmmType::BmmGenericType(
self.build_generic_type(context, generic, owner)?,
)),
PBmmType::PBmmContainerType(container) => Ok(BmmType::BmmContainerType(Box::new(
self.build_container_type(context, container, owner)?,
))),
}
}
fn build_base_type(
&self,
context: &str,
r#type: &PBmmBaseType,
owner: &PBmmClass,
) -> Result<BmmType, PBmmReadError> {
match r#type {
PBmmBaseType::PBmmSimpleType(simple) => {
self.build_named_type(context, &simple.r#type, owner)
}
PBmmBaseType::PBmmOpenType(open) => Ok(BmmType::BmmOpenType(self.build_open_type(
owner,
context,
&open.r#type,
)?)),
PBmmBaseType::PBmmGenericType(generic) => Ok(BmmType::BmmGenericType(
self.build_generic_type(context, generic, owner)?,
)),
}
}
fn build_open_type(
&self,
owner: &PBmmClass,
property: &str,
parameter: &str,
) -> Result<BmmOpenType, PBmmReadError> {
let declared = self
.find_generic_parameter(owner, parameter)
.ok_or_else(|| PBmmReadError::UndeclaredGenericParameter {
class: owner.name().to_owned(),
property: property.to_owned(),
parameter: parameter.to_owned(),
})?;
Ok(BmmOpenType {
documentation: None,
generic_constraint: self.build_generic_parameter(owner.name(), declared)?,
})
}
pub(super) fn find_generic_parameter(
&self,
owner: &PBmmClass,
parameter: &str,
) -> Option<&'a crate::v1_1::bmm_persistence::p_bmm_generic_parameter::PBmmGenericParameter>
{
let mut seen: BTreeSet<String> = BTreeSet::new();
let mut queue: Vec<String> = vec![owner.name().to_uppercase()];
while let Some(key) = queue.pop() {
if !seen.insert(key.clone()) {
continue;
}
let Some(entry) = self.classes.get(&key) else {
continue;
};
if let Some(found) = entry
.class
.generic_parameter_defs()
.and_then(|defs| defs.get(parameter))
{
return Some(found);
}
for parent in entry.class.ancestor_names() {
queue.push(parent.to_uppercase());
}
}
None
}
fn build_generic_type(
&self,
context: &str,
generic: &PBmmGenericType,
owner: &PBmmClass,
) -> Result<BmmGenericType, PBmmReadError> {
let entry = self.entry(context, &generic.root_type)?;
let BmmClass::BmmGenericClass(base_class) = self.build_class(entry, EmbedDepth::Stub)?
else {
return Err(PBmmReadError::NotAGenericClass {
context: context.to_owned(),
type_name: generic.root_type.clone(),
});
};
let formals: Vec<&str> = base_class
.generic_parameters
.keys()
.map(String::as_str)
.collect();
let mut generic_parameters: Vec<BmmType> = Vec::new();
for actual in generic.bind_parameters(context, &formals)? {
generic_parameters.push(match actual {
ActualParameter::Named(name) => self.build_named_type(context, name, owner)?,
ActualParameter::Structured(parameter) => {
self.build_type(context, parameter, owner)?
}
});
}
Ok(BmmGenericType {
documentation: None,
generic_parameters,
base_class,
})
}
fn build_container_type(
&self,
context: &str,
container: &PBmmContainerType,
owner: &PBmmClass,
) -> Result<BmmContainerType, PBmmReadError> {
match container {
PBmmContainerType::PBmmIndexedContainerType(indexed) => {
Ok(BmmContainerType::BmmIndexedContainerType(Box::new(
self.build_indexed_container_type(context, indexed, owner)?,
)))
}
PBmmContainerType::PBmmContainerType(data) => {
Ok(BmmContainerType::BmmContainerType(BmmContainerTypeData {
documentation: None,
container_type: self.build_class(
self.entry(context, &data.container_type)?,
EmbedDepth::Stub,
)?,
base_type: Box::new(self.build_container_target(
context,
data.r#type.as_deref(),
data.type_def.as_ref(),
owner,
)?),
}))
}
}
}
fn build_indexed_container_type(
&self,
context: &str,
indexed: &crate::v1_1::bmm_persistence::p_bmm_indexed_container_type::PBmmIndexedContainerType,
owner: &PBmmClass,
) -> Result<BmmIndexedContainerType, PBmmReadError> {
Ok(BmmIndexedContainerType {
documentation: None,
container_type: self.build_class(
self.entry(context, &indexed.container_type)?,
EmbedDepth::Stub,
)?,
base_type: self.build_container_target(
context,
indexed.r#type.as_deref(),
indexed.type_def.as_ref(),
owner,
)?,
index_type: BmmSimpleType {
documentation: None,
base_class: self
.build_class(self.entry(context, &indexed.index_type)?, EmbedDepth::Stub)?,
},
})
}
fn build_container_target(
&self,
context: &str,
name: Option<&str>,
type_def: Option<&PBmmBaseType>,
owner: &PBmmClass,
) -> Result<BmmType, PBmmReadError> {
match (type_def, name) {
(Some(nested), _) => self.build_base_type(context, nested, owner),
(None, Some(name)) => self.build_named_type(context, name, owner),
(None, None) => Err(PBmmReadError::ContainerTargetTypeMissing {
context: context.to_owned(),
}),
}
}
fn build_packages(
&self,
packages: &BTreeMap<String, PBmmPackage>,
prefix: &str,
) -> Result<BTreeMap<String, BmmPackage>, PBmmReadError> {
let mut out = BTreeMap::new();
for package in packages.values() {
let path = qualify(prefix, &package.name);
let mut classes = Vec::new();
for class in package.classes.iter().flatten() {
let entry = self.classes.get(&class.to_uppercase()).ok_or_else(|| {
PBmmReadError::ClassNotDefined {
package: package.name.clone(),
class: class.clone(),
}
})?;
classes.push(self.build_class(entry, EmbedDepth::Full)?);
}
let children = self.build_packages(&package.packages, &path)?;
out.insert(
package.name.to_uppercase(),
BmmPackage {
documentation: package.documentation.clone(),
packages: if children.is_empty() {
None
} else {
Some(children)
},
name: package.name.clone(),
classes: present(classes),
},
);
}
Ok(out)
}
}
struct ClassCore {
documentation: Option<String>,
name: String,
ancestors: Option<BTreeMap<String, BmmClass>>,
package: BmmPackage,
properties: Option<BTreeMap<String, BmmProperty<BmmType>>>,
source_schema_id: String,
immediate_descendants: Vec<String>,
is_abstract: bool,
is_primitive_type: bool,
is_override: bool,
}
pub(super) fn check_enumeration_validity(
name: &str,
persisted: &PBmmEnumeration,
) -> Result<(), PBmmReadError> {
let ancestors = persisted.ancestors();
if ancestors.len() > 1 {
return Err(PBmmReadError::EnumerationAncestorCount {
class: name.to_owned(),
ancestors: ancestors.to_vec(),
});
}
let names = persisted.item_names().len();
let values = persisted.item_values().len();
if values > 0 && values != names {
return Err(PBmmReadError::EnumerationItemListsNotOneToOne {
class: name.to_owned(),
names,
values,
});
}
Ok(())
}
fn build_enumeration(core: ClassCore, persisted: &PBmmEnumeration) -> BmmEnumeration {
let item_names = persisted.item_names().to_vec();
let item_values = persisted.item_values().to_vec();
let underlying_type_name = persisted.underlying_type_name().to_owned();
match persisted {
PBmmEnumeration::PBmmEnumerationInteger(_) => {
BmmEnumeration::BmmEnumerationInteger(class_from_core!(
BmmEnumerationInteger,
core,
item_names: present(item_names),
item_values: present(item_values),
underlying_type_name,
))
}
PBmmEnumeration::PBmmEnumerationString(_) => {
BmmEnumeration::BmmEnumerationString(class_from_core!(
BmmEnumerationString,
core,
item_names: present(item_names),
item_values: present(item_values),
underlying_type_name,
))
}
PBmmEnumeration::PBmmEnumeration(_) => BmmEnumeration::BmmEnumeration(class_from_core!(
BmmEnumerationData,
core,
item_names: present(item_names),
item_values: present(item_values),
underlying_type_name,
)),
}
}
fn index_packages(
packages: &BTreeMap<String, PBmmPackage>,
prefix: &str,
classes: &BTreeMap<String, ClassEntry<'_>>,
out: &mut BTreeMap<String, String>,
) -> Result<(), PBmmReadError> {
for package in packages.values() {
let path = qualify(prefix, &package.name);
for class in package.classes.iter().flatten() {
let key = class.to_uppercase();
if !classes.contains_key(&key) {
return Err(PBmmReadError::ClassNotDefined {
package: package.name.clone(),
class: class.clone(),
});
}
out.entry(key).or_insert_with(|| path.clone());
}
index_packages(&package.packages, &path, classes, out)?;
}
Ok(())
}
pub(super) fn qualify(prefix: &str, name: &str) -> String {
if prefix.is_empty() {
name.to_owned()
} else {
format!(
"{prefix}{}{name}",
crate::v1_1::bmm::core::bmm_definitions::BmmDefinitionsData::PACKAGE_NAME_DELIMITER
)
}
}
pub(super) fn property_context(class: &str, property: &str) -> String {
format!("class `{class}` property `{property}`")
}
pub(super) fn multiplicity_of(interval: &Interval<i32>) -> MultiplicityInterval {
match interval {
Interval::PointInterval(PointInterval {
lower,
upper,
lower_unbounded,
upper_unbounded,
lower_included,
upper_included,
}) => MultiplicityInterval {
lower: *lower,
upper: *upper,
lower_unbounded: *lower_unbounded,
upper_unbounded: *upper_unbounded,
lower_included: *lower_included,
upper_included: *upper_included,
},
Interval::ProperInterval(ProperInterval::MultiplicityInterval(multiplicity)) => {
multiplicity.clone()
}
Interval::ProperInterval(ProperInterval::ProperInterval(proper)) => MultiplicityInterval {
lower: proper.lower,
upper: proper.upper,
lower_unbounded: proper.lower_unbounded,
upper_unbounded: proper.upper_unbounded,
lower_included: proper.lower_included,
upper_included: proper.upper_included,
},
}
}