use crate::diagnostic::Diagnostic;
use crate::entity::Entity;
use crate::header::Header;
use crate::value::EntityId;
use ahash::AHashMap;
#[derive(Debug, Clone, Default)]
pub struct Model {
header: Header,
entities: AHashMap<EntityId, Entity>,
order: Vec<EntityId>,
by_type: AHashMap<String, Vec<EntityId>>,
max_id: u64,
revision: u64,
diagnostics: Vec<Diagnostic>,
}
impl Model {
pub fn new() -> Self {
Self::default()
}
pub fn header(&self) -> &Header {
&self.header
}
pub fn header_mut(&mut self) -> &mut Header {
&mut self.header
}
pub fn diagnostics(&self) -> &[Diagnostic] {
&self.diagnostics
}
pub fn is_complete(&self) -> bool {
self.diagnostics.is_empty()
}
pub fn push_diagnostic(&mut self, diagnostic: Diagnostic) {
self.diagnostics.push(diagnostic);
}
pub fn insert(&mut self, id: EntityId, entity: Entity) {
#[cfg(feature = "authored-dump")]
crate::authored_dump::record(&entity.type_name, "insert");
let key = entity.type_name.to_ascii_uppercase();
match self.entities.insert(id, entity) {
None => self.order.push(id),
Some(previous) => {
let previous_key = previous.type_name.to_ascii_uppercase();
if previous_key != key {
if let Some(ids) = self.by_type.get_mut(&previous_key) {
ids.retain(|existing| *existing != id);
}
} else {
self.max_id = self.max_id.max(id.0);
self.revision += 1;
return;
}
}
}
self.by_type.entry(key).or_default().push(id);
self.max_id = self.max_id.max(id.0);
self.revision += 1;
}
pub fn push(&mut self, entity: Entity) -> EntityId {
let id = EntityId(self.max_id + 1);
self.insert(id, entity);
id
}
pub fn revision(&self) -> u64 {
self.revision
}
pub(crate) fn bump_revision(&mut self) {
self.revision += 1;
}
pub fn next_id(&self) -> EntityId {
EntityId(self.max_id + 1)
}
pub fn get(&self, id: EntityId) -> Option<&Entity> {
self.entities.get(&id)
}
pub fn len(&self) -> usize {
self.entities.len()
}
pub fn is_empty(&self) -> bool {
self.entities.is_empty()
}
pub fn ids(&self) -> impl Iterator<Item = EntityId> + '_ {
self.order.iter().copied()
}
pub fn iter(&self) -> impl Iterator<Item = (EntityId, &Entity)> + '_ {
self.order
.iter()
.filter_map(move |id| self.entities.get(id).map(|e| (*id, e)))
}
pub fn ids_of_type(&self, type_name: &str) -> &[EntityId] {
self.by_type
.get(&type_name.to_ascii_uppercase())
.map(|v| v.as_slice())
.unwrap_or(&[])
}
pub fn of_type<'a>(&'a self, type_name: &str) -> impl Iterator<Item = (EntityId, &'a Entity)> {
self.ids_of_type(type_name)
.iter()
.filter_map(move |id| self.entities.get(id).map(|e| (*id, e)))
.collect::<Vec<_>>()
.into_iter()
}
pub fn type_histogram(&self) -> Vec<(&str, usize)> {
let mut v: Vec<_> = self
.by_type
.iter()
.map(|(k, ids)| (k.as_str(), ids.len()))
.collect();
v.sort_unstable_by(|a, b| b.1.cmp(&a.1).then(a.0.cmp(b.0)));
v
}
pub fn dangling_references(&self) -> Vec<(EntityId, EntityId)> {
let mut out = Vec::new();
for (id, entity) in self.iter() {
for target in entity.references() {
if !self.entities.contains_key(&target) {
out.push((id, target));
}
}
}
out
}
pub(crate) fn entities_mut(&mut self) -> &mut AHashMap<EntityId, Entity> {
&mut self.entities
}
pub(crate) fn by_type_mut(&mut self) -> &mut AHashMap<String, Vec<EntityId>> {
&mut self.by_type
}
pub(crate) fn order_mut(&mut self) -> &mut Vec<EntityId> {
&mut self.order
}
}