use std::collections::{HashMap, HashSet};
use std::sync::{Arc, RwLock};
use crate::descriptor::{EntityDescriptor, RelationDescriptor, TypeDescriptor, TypeDescriptorRef};
use crate::error::{OrmError, Result};
#[derive(Debug, Default)]
pub struct DescriptorRegistry {
descriptors: RwLock<HashMap<String, TypeDescriptorRef>>,
}
impl DescriptorRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register_entity(&self, descriptor: EntityDescriptor) -> Result<Arc<EntityDescriptor>> {
validate_entity_descriptor(&descriptor)?;
let mut descriptors = self.descriptors.write().map_err(lock_error)?;
match descriptors.get(&descriptor.type_name) {
Some(TypeDescriptorRef::Entity(existing)) if existing.as_ref() == &descriptor => {
Ok(Arc::clone(existing))
}
Some(TypeDescriptorRef::Entity(_)) => Err(OrmError::DescriptorConflict {
type_name: descriptor.type_name,
message: "entity descriptor shape differs from registered descriptor".into(),
}),
Some(TypeDescriptorRef::Relation(_)) => Err(OrmError::DescriptorConflict {
type_name: descriptor.type_name,
message: "type name is already registered as a relation".into(),
}),
None => {
let descriptor = Arc::new(descriptor);
descriptors.insert(
descriptor.type_name.clone(),
TypeDescriptorRef::Entity(Arc::clone(&descriptor)),
);
Ok(descriptor)
}
}
}
pub fn register_relation(
&self,
descriptor: RelationDescriptor,
) -> Result<Arc<RelationDescriptor>> {
validate_relation_descriptor(&descriptor)?;
let mut descriptors = self.descriptors.write().map_err(lock_error)?;
match descriptors.get(&descriptor.type_name) {
Some(TypeDescriptorRef::Relation(existing)) if existing.as_ref() == &descriptor => {
Ok(Arc::clone(existing))
}
Some(TypeDescriptorRef::Relation(_)) => Err(OrmError::DescriptorConflict {
type_name: descriptor.type_name,
message: "relation descriptor shape differs from registered descriptor".into(),
}),
Some(TypeDescriptorRef::Entity(_)) => Err(OrmError::DescriptorConflict {
type_name: descriptor.type_name,
message: "type name is already registered as an entity".into(),
}),
None => {
let descriptor = Arc::new(descriptor);
descriptors.insert(
descriptor.type_name.clone(),
TypeDescriptorRef::Relation(Arc::clone(&descriptor)),
);
Ok(descriptor)
}
}
}
pub fn entity(&self, type_name: &str) -> Result<Arc<EntityDescriptor>> {
match self.get(type_name) {
Some(TypeDescriptorRef::Entity(descriptor)) => Ok(descriptor),
Some(TypeDescriptorRef::Relation(_)) => Err(OrmError::DescriptorConflict {
type_name: type_name.to_string(),
message: "requested entity but descriptor is a relation".into(),
}),
None => Err(OrmError::DescriptorNotFound(type_name.to_string())),
}
}
pub fn relation(&self, type_name: &str) -> Result<Arc<RelationDescriptor>> {
match self.get(type_name) {
Some(TypeDescriptorRef::Relation(descriptor)) => Ok(descriptor),
Some(TypeDescriptorRef::Entity(_)) => Err(OrmError::DescriptorConflict {
type_name: type_name.to_string(),
message: "requested relation but descriptor is an entity".into(),
}),
None => Err(OrmError::DescriptorNotFound(type_name.to_string())),
}
}
pub fn get(&self, type_name: &str) -> Option<TypeDescriptorRef> {
self.descriptors
.read()
.ok()
.and_then(|descriptors| descriptors.get(type_name).cloned())
}
pub fn snapshot(&self) -> Vec<TypeDescriptor> {
let mut descriptors: Vec<_> = self
.descriptors
.read()
.map(|descriptors| {
descriptors
.values()
.map(TypeDescriptorRef::to_owned_descriptor)
.collect()
})
.unwrap_or_default();
descriptors.sort_by(|left, right| left.type_name().cmp(right.type_name()));
descriptors
}
}
fn validate_entity_descriptor(descriptor: &EntityDescriptor) -> Result<()> {
validate_type_name(&descriptor.type_name)?;
if let Some(parent_type) = &descriptor.parent_type {
validate_type_name(parent_type)?;
}
validate_attributes(&descriptor.type_name, &descriptor.owned_attributes)
}
fn validate_relation_descriptor(descriptor: &RelationDescriptor) -> Result<()> {
validate_type_name(&descriptor.type_name)?;
if let Some(parent_type) = &descriptor.parent_type {
validate_type_name(parent_type)?;
}
validate_attributes(&descriptor.type_name, &descriptor.owned_attributes)?;
let mut role_names = HashSet::new();
for role in &descriptor.roles {
validate_non_empty(&descriptor.type_name, "role name", &role.role_name)?;
if !role_names.insert(role.role_name.as_str()) {
return Err(OrmError::DescriptorValidation {
type_name: descriptor.type_name.clone(),
message: format!("duplicate role name '{}'", role.role_name),
});
}
for player_type_name in &role.player_type_names {
validate_type_name(player_type_name)?;
}
}
Ok(())
}
fn validate_attributes(
type_name: &str,
attributes: &[crate::descriptor::OwnedAttributeDescriptor],
) -> Result<()> {
let mut field_names = HashSet::new();
let mut attr_names = HashSet::new();
for attr in attributes {
validate_non_empty(type_name, "field name", &attr.field_name)?;
validate_non_empty(type_name, "attribute name", &attr.attr_name)?;
if !field_names.insert(attr.field_name.as_str()) {
return Err(OrmError::DescriptorValidation {
type_name: type_name.to_string(),
message: format!("duplicate field name '{}'", attr.field_name),
});
}
if !attr_names.insert(attr.attr_name.as_str()) {
return Err(OrmError::DescriptorValidation {
type_name: type_name.to_string(),
message: format!("duplicate attribute name '{}'", attr.attr_name),
});
}
}
Ok(())
}
fn validate_type_name(type_name: &str) -> Result<()> {
validate_non_empty(type_name, "type name", type_name)
}
fn validate_non_empty(type_name: &str, label: &str, value: &str) -> Result<()> {
if value.trim().is_empty() {
return Err(OrmError::DescriptorValidation {
type_name: type_name.to_string(),
message: format!("{label} cannot be empty"),
});
}
Ok(())
}
fn lock_error<T>(_: std::sync::PoisonError<T>) -> OrmError {
OrmError::DescriptorValidation {
type_name: "<registry>".into(),
message: "descriptor registry lock is poisoned".into(),
}
}