use std::collections::HashMap;
use std::sync::LazyLock;
mod data;
#[derive(Debug)]
pub struct RmClass {
pub name: &'static str,
pub package: &'static str,
pub generated_type: bool,
pub is_abstract: bool,
pub ancestors: &'static [&'static str],
pub descendants: &'static [&'static str],
pub attributes: &'static [RmAttribute],
pub computed_attributes: &'static [RmAttribute],
pub is_structure_root: bool,
pub generic_params: &'static [RmGenericParam],
}
#[derive(Debug)]
pub struct RmGenericParam {
pub name: &'static str,
pub conforms_to: Option<&'static str>,
}
#[derive(Debug)]
pub struct RmAttribute {
pub name: &'static str,
pub declared_type: &'static str,
pub container: Container,
pub is_mandatory: bool,
pub type_params: &'static [RmTypeRef],
pub cardinality: Option<Cardinality>,
pub nonempty: bool,
pub ref_target: Option<&'static str>,
}
#[derive(Debug)]
pub struct RmTypeRef {
pub name: &'static str,
pub params: &'static [RmTypeRef],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Cardinality {
pub lower: u32,
pub upper: Option<u32>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Container {
None,
List,
Set,
Hash,
}
#[derive(Debug)]
pub struct RmEnumeration {
pub name: &'static str,
pub underlying_type: &'static str,
pub literals: &'static [RmEnumLiteral],
}
#[derive(Debug)]
pub struct RmEnumLiteral {
pub name: &'static str,
pub value: EnumValue,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EnumValue {
Int(i64),
Str(&'static str),
}
static INDEX: LazyLock<HashMap<&'static str, &'static RmClass>> =
LazyLock::new(|| data::CLASSES.iter().map(|c| (c.name, c)).collect());
fn find(name: &str) -> Option<&'static RmClass> {
INDEX.get(name).copied()
}
#[must_use]
pub fn class(name: &str) -> Option<&'static RmClass> {
find(name)
}
#[must_use]
pub fn attribute(class: &str, attr: &str) -> Option<&'static RmAttribute> {
find(class)?.attributes.iter().find(|a| a.name == attr)
}
#[must_use]
pub fn computed_attribute(class: &str, attr: &str) -> Option<&'static RmAttribute> {
find(class)?
.computed_attributes
.iter()
.find(|a| a.name == attr)
}
pub fn attributes(class: &str) -> impl Iterator<Item = &'static RmAttribute> {
const EMPTY: &[RmAttribute] = &[];
find(class).map_or(EMPTY, |c| c.attributes).iter()
}
#[must_use]
pub fn declared_concrete_type(parent_type: &str, field: &str) -> Option<&'static str> {
let attr = attribute(parent_type, field)?;
let class = class(attr.declared_type)?;
(!class.is_abstract).then_some(class.name)
}
#[must_use]
pub fn descendants(class: &str) -> &'static [&'static str] {
const EMPTY: &[&str] = &[];
find(class).map_or(EMPTY, |c| c.descendants)
}
#[must_use]
pub fn ancestors(class: &str) -> &'static [&'static str] {
const EMPTY: &[&str] = &[];
find(class).map_or(EMPTY, |c| c.ancestors)
}
#[must_use]
pub fn is_a(sub: &str, sup: &str) -> bool {
sub == sup || find(sub).is_some_and(|c| c.ancestors.contains(&sup))
}
pub const PRIMITIVE_TYPES_PACKAGE: &str = "org.openehr.base.foundation_types.primitive_types";
#[must_use]
pub fn is_primitive(name: &str) -> bool {
find(name).is_some_and(|c| !c.is_abstract && c.package == PRIMITIVE_TYPES_PACKAGE)
}
pub const FOUNDATION_TYPES_PACKAGE: &str = "org.openehr.base.foundation_types";
#[must_use]
pub fn is_foundation_type(name: &str) -> bool {
find(name).is_some_and(|c| {
c.package
.strip_prefix(FOUNDATION_TYPES_PACKAGE)
.is_some_and(|rest| rest.is_empty() || rest.starts_with('.'))
})
}
#[must_use]
pub fn conforms_to_ordered(name: &str) -> bool {
is_a(name, "Ordered")
}
#[must_use]
pub fn is_structure_root(class: &str) -> bool {
find(class).is_some_and(|c| c.is_structure_root)
}
pub fn classes() -> impl Iterator<Item = &'static RmClass> {
data::CLASSES.iter()
}
static ENUM_INDEX: LazyLock<HashMap<&'static str, &'static RmEnumeration>> =
LazyLock::new(|| data::ENUMERATIONS.iter().map(|e| (e.name, e)).collect());
#[must_use]
pub fn enumeration(name: &str) -> Option<&'static RmEnumeration> {
ENUM_INDEX.get(name).copied()
}