use std::{collections::BTreeMap, sync::Arc};
use ifc_model::{EntityId, Model};
use super::assignment::assigned_sets;
use super::release::{validate_model, Release};
use super::set::load_source_set;
use super::{ExactProperty, ExactPropertyError, ExactSource};
#[derive(Debug, Clone, PartialEq)]
pub struct ExactPropertyEntry {
pub name: Arc<str>,
pub property: ExactProperty,
}
pub fn exact_properties(
model: &Model,
object: EntityId,
) -> Result<Vec<ExactPropertyEntry>, ExactPropertyError> {
exact_properties_where(model, object, |_| true, |_| true)
}
pub fn exact_properties_where<S, P>(
model: &Model,
object: EntityId,
mut select_set: S,
mut select_property: P,
) -> Result<Vec<ExactPropertyEntry>, ExactPropertyError>
where
S: FnMut(&str) -> bool,
P: FnMut(&str) -> bool,
{
let release = validate_model(model)?;
let assigned = assigned_sets(model, release, object)?;
let mut selector = Selector {
set: &mut select_set,
property: &mut select_property,
};
let mut entries = collect(
model,
release,
&assigned.occurrence_sets,
ExactSource::Occurrence,
&mut selector,
)?;
if let Some((type_id, sets)) = &assigned.type_sets {
let inherited = collect(
model,
release,
sets,
ExactSource::Type(*type_id),
&mut selector,
)?;
let overridden: Vec<(Arc<str>, Arc<str>)> = entries
.iter()
.map(|entry| (entry.property.property_set.clone(), entry.name.clone()))
.collect();
entries.extend(inherited.into_iter().filter(|entry| {
!overridden
.iter()
.any(|(set, name)| *set == entry.property.property_set && *name == entry.name)
}));
}
Ok(entries)
}
pub(super) struct Selector<'a> {
pub(super) set: &'a mut dyn FnMut(&str) -> bool,
pub(super) property: &'a mut dyn FnMut(&str) -> bool,
}
pub(super) fn collect(
model: &Model,
release: Release,
sets: &[EntityId],
source: ExactSource,
select: &mut Selector<'_>,
) -> Result<Vec<ExactPropertyEntry>, ExactPropertyError> {
let mut entries = Vec::new();
let mut selected_sets: BTreeMap<&str, EntityId> = BTreeMap::new();
for &set_id in sets {
let set = load_source_set(model, release, source, set_id)?;
if !(select.set)(set.name) {
set.refuse_unselected(release, &mut *select.property)?;
continue;
}
if let Some(first) = set.shares_name(&mut selected_sets) {
return Err(ExactPropertyError::DuplicateMatchingSets {
source,
first,
second: set_id,
});
}
let mut chosen: BTreeMap<&str, EntityId> = BTreeMap::new();
let mut in_order = Vec::new();
for (member, name) in set.members(model, release)? {
if !(select.property)(name) {
continue;
}
if let Some(first) = chosen.insert(name, set.member_id(member)) {
return Err(ExactPropertyError::DuplicateMatchingProperties {
set: set_id,
first,
second: set.member_id(member),
});
}
in_order.push((member, name));
}
for (member, name) in in_order {
let resolved = set.value(model, release, member)?;
let earlier = entries.iter().find(|entry: &&ExactPropertyEntry| {
entry.property.property_set.as_ref() == set.name && entry.name.as_ref() == name
});
if let Some(earlier) = earlier {
return Err(ExactPropertyError::DuplicateMatchingSets {
source,
first: earlier.property.set_id,
second: set_id,
});
}
entries.push(ExactPropertyEntry {
name: Arc::from(name),
property: set.exact(source, member, resolved),
});
}
}
Ok(entries)
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct ExactPropertySetEntry {
pub name: Arc<str>,
pub set_id: EntityId,
pub source: ExactSource,
pub members: usize,
}
pub fn exact_property_sets_where<S>(
model: &Model,
object: EntityId,
mut select_set: S,
) -> Result<Vec<ExactPropertySetEntry>, ExactPropertyError>
where
S: FnMut(&str) -> bool,
{
let release = validate_model(model)?;
let assigned = assigned_sets(model, release, object)?;
let mut entries = list_sets(
model,
release,
&assigned.occurrence_sets,
ExactSource::Occurrence,
&mut select_set,
)?;
if let Some((type_id, sets)) = &assigned.type_sets {
entries.extend(list_sets(
model,
release,
sets,
ExactSource::Type(*type_id),
&mut select_set,
)?);
}
Ok(entries)
}
pub(super) fn list_sets(
model: &Model,
release: Release,
sets: &[EntityId],
source: ExactSource,
select_set: &mut dyn FnMut(&str) -> bool,
) -> Result<Vec<ExactPropertySetEntry>, ExactPropertyError> {
let mut entries = Vec::new();
let mut selected: BTreeMap<&str, EntityId> = BTreeMap::new();
for &set_id in sets {
let set = load_source_set(model, release, source, set_id)?;
if !select_set(set.name) {
continue;
}
if let Some(first) = set.shares_name(&mut selected) {
return Err(ExactPropertyError::DuplicateMatchingSets {
source,
first,
second: set_id,
});
}
let members = if set.member_list_is_empty(release) {
0
} else {
set.members(model, release)?.len()
};
entries.push(ExactPropertySetEntry {
name: Arc::from(set.name),
set_id,
source,
members,
});
}
Ok(entries)
}