use ents::{
check_incoming_edges, DatabaseError, DraftError, Edge, EdgeValue, Ent,
EntExt, Id, Transactional,
};
#[derive(Debug, thiserror::Error)]
pub enum AuditError {
#[error("Entity not found: {0}")]
EntityNotFound(Id),
#[error("Unexpected entity type: {0} is not {1} type")]
UnexpectedEntityType(Id, String),
#[error("Edge mismatch: existing edges differ from drafted edges")]
EdgeMismatch {
existing: Vec<EdgeValue>,
drafted: Vec<EdgeValue>,
},
#[error("Draft error: {0}")]
Draft(#[from] DraftError),
#[error("Database error: {0}")]
Database(#[from] DatabaseError),
}
pub trait AdminEnt: Transactional {
fn find_edges_by_dest(&self, dest: Id) -> Result<Vec<Edge>, DatabaseError>;
fn remove_edges_by_dest(&self, dest: Id) -> Result<(), DatabaseError>;
fn create_dyn(&self, ent: Box<dyn Ent>) -> Result<Id, DatabaseError>;
fn update_dyn(&self, ent: Box<dyn Ent>) -> Result<(), DatabaseError>;
fn audit_ent_edges<E: Ent>(&self, id: Id) -> Result<(), AuditError>
where
Self: Sized,
{
let ent_box = self.get(id)?.ok_or(AuditError::EntityNotFound(id))?;
let ent = ent_box.as_ent::<E>().ok_or_else(|| {
AuditError::UnexpectedEntityType(
id,
std::any::type_name::<E>().to_string(),
)
})?;
let mut existing_edges: Vec<EdgeValue> = self
.find_edges_by_dest(id)?
.into_iter()
.map(|e| EdgeValue::new(e.source, e.sort_key, e.dest))
.collect();
existing_edges.sort_by(|a, b| {
(&a.source, &a.sort_key).cmp(&(&b.source, &b.sort_key))
});
self.remove_edges_by_dest(id)?;
let mut drafted_edges = check_incoming_edges(ent, self)?;
drafted_edges.sort_by(|a, b| {
(&a.source, &a.sort_key).cmp(&(&b.source, &b.sort_key))
});
if existing_edges != drafted_edges {
return Err(AuditError::EdgeMismatch {
existing: existing_edges,
drafted: drafted_edges,
});
}
Ok(())
}
fn fix_ent_edges<E: Ent>(self, id: Id) -> Result<(), AuditError>
where
Self: Sized,
{
let ent_box = self.get(id)?.ok_or(AuditError::EntityNotFound(id))?;
let ent = ent_box.as_ent::<E>().ok_or_else(|| {
AuditError::UnexpectedEntityType(
id,
std::any::type_name::<E>().to_string(),
)
})?;
self.remove_edges_by_dest(id)?;
let edges = check_incoming_edges(ent, &self)?;
for edge in edges {
self.create_edge(edge)?;
}
self.commit()?;
Ok(())
}
fn list_entities(
&self,
entity_type: &str,
cursor: Option<Id>,
limit: usize,
) -> Result<Vec<Box<dyn Ent>>, DatabaseError>;
}