use std::collections::BTreeMap;
use crate::v1_1::bmm_persistence::error::PBmmReadError;
use crate::v1_1::bmm_persistence::p_bmm_generic_type::PBmmGenericType;
use crate::v1_1::bmm_persistence::p_bmm_type::PBmmType;
use crate::v1_1::bmm3::core::entity::bmm_effective_type::BmmEffectiveType;
use crate::v1_1::bmm3::core::entity::bmm_generic_type::BmmGenericType;
use crate::v1_1::bmm3::core::entity::bmm_parameter_type::BmmParameterType;
use crate::v1_1::bmm3::core::entity::bmm_unitary_type::BmmUnitaryType;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ActualParameter<'a> {
Named(&'a str),
Structured(&'a PBmmType),
}
impl PBmmGenericType {
pub fn bind_parameters<'a>(
&'a self,
context: &str,
formals: &[&str],
) -> Result<Vec<ActualParameter<'a>>, PBmmReadError> {
let mut bound: Vec<Option<&PBmmType>> = vec![None; formals.len()];
for (key, def) in self.generic_parameter_defs.iter().flatten() {
let (slot, formal) = formals
.iter()
.position(|formal| formal == key)
.or_else(|| positional_key(key, formals.len()))
.and_then(|position| bound.get_mut(position).zip(formals.get(position)))
.ok_or_else(|| PBmmReadError::UnknownGenericParameterKey {
context: context.to_owned(),
root_type: self.root_type.clone(),
key: key.clone(),
})?;
if slot.replace(def).is_some() {
return Err(PBmmReadError::GenericParameterBoundTwice {
context: context.to_owned(),
root_type: self.root_type.clone(),
parameter: (*formal).to_owned(),
});
}
}
let mut named = self.generic_parameters.iter().flatten();
let mut out = Vec::new();
for slot in bound {
match slot {
Some(def) => out.push(ActualParameter::Structured(def)),
None => out.extend(named.next().map(|name| ActualParameter::Named(name))),
}
}
out.extend(named.map(|name| ActualParameter::Named(name)));
Ok(out)
}
}
fn positional_key(key: &str, formals: usize) -> Option<usize> {
key.parse::<usize>()
.ok()
.and_then(|position| position.checked_sub(1))
.filter(|position| *position < formals)
}
pub(super) fn parameter_bindings(generic: &BmmGenericType) -> BTreeMap<String, BmmUnitaryType> {
generic
.base_class
.generic_parameters
.iter()
.zip(generic.generic_parameters.iter())
.filter(|((name, formal), actual)| !is_unsubstituted(name, formal, actual))
.map(|((name, _), actual)| (name.clone(), actual.clone()))
.collect()
}
fn is_unsubstituted(name: &str, formal: &BmmParameterType, actual: &BmmUnitaryType) -> bool {
let BmmUnitaryType::BmmParameterType(actual) = actual else {
return false;
};
actual.name.eq_ignore_ascii_case(name) && conformance_name(actual) == conformance_name(formal)
}
fn conformance_name(parameter: &BmmParameterType) -> Option<String> {
parameter
.flattened_conforms_to_type()
.map(BmmEffectiveType::type_name)
}