1use ents::{
2 check_incoming_edges, DatabaseError, DraftError, Edge, EdgeValue, Ent,
3 EntExt, Id, Transactional,
4};
5
6#[derive(Debug, thiserror::Error)]
8pub enum AuditError {
9 #[error("Entity not found: {0}")]
10 EntityNotFound(Id),
11
12 #[error("Unexpected entity type: {0} is not {1} type")]
13 UnexpectedEntityType(Id, String),
14
15 #[error("Edge mismatch: existing edges differ from drafted edges")]
16 EdgeMismatch {
17 existing: Vec<EdgeValue>,
18 drafted: Vec<EdgeValue>,
19 },
20
21 #[error("Draft error: {0}")]
22 Draft(#[from] DraftError),
23
24 #[error("Database error: {0}")]
25 Database(#[from] DatabaseError),
26}
27
28pub trait AdminEnt: Transactional {
29 fn find_edges_by_dest(&self, dest: Id) -> Result<Vec<Edge>, DatabaseError>;
30
31 fn remove_edges_by_dest(&self, dest: Id) -> Result<(), DatabaseError>;
32
33 fn create_dyn(&self, ent: Box<dyn Ent>) -> Result<Id, DatabaseError>;
38
39 fn update_dyn(&self, ent: Box<dyn Ent>) -> Result<(), DatabaseError>;
44
45 fn audit_ent_edges<E: Ent>(&self, id: Id) -> Result<(), AuditError>
46 where
47 Self: Sized,
48 {
49 let ent_box = self.get(id)?.ok_or(AuditError::EntityNotFound(id))?;
51
52 let ent = ent_box.as_ent::<E>().ok_or_else(|| {
53 AuditError::UnexpectedEntityType(
54 id,
55 std::any::type_name::<E>().to_string(),
56 )
57 })?;
58
59 let mut existing_edges: Vec<EdgeValue> = self
61 .find_edges_by_dest(id)?
62 .into_iter()
63 .map(|e| EdgeValue::new(e.source, e.sort_key, e.dest))
64 .collect();
65 existing_edges.sort_by(|a, b| {
66 (&a.source, &a.sort_key).cmp(&(&b.source, &b.sort_key))
67 });
68
69 self.remove_edges_by_dest(id)?;
71
72 let mut drafted_edges = check_incoming_edges(ent, self)?;
74 drafted_edges.sort_by(|a, b| {
75 (&a.source, &a.sort_key).cmp(&(&b.source, &b.sort_key))
76 });
77
78 if existing_edges != drafted_edges {
80 return Err(AuditError::EdgeMismatch {
81 existing: existing_edges,
82 drafted: drafted_edges,
83 });
84 }
85
86 Ok(())
89 }
90
91 fn fix_ent_edges<E: Ent>(self, id: Id) -> Result<(), AuditError>
92 where
93 Self: Sized,
94 {
95 let ent_box = self.get(id)?.ok_or(AuditError::EntityNotFound(id))?;
97
98 let ent = ent_box.as_ent::<E>().ok_or_else(|| {
99 AuditError::UnexpectedEntityType(
100 id,
101 std::any::type_name::<E>().to_string(),
102 )
103 })?;
104
105 self.remove_edges_by_dest(id)?;
107
108 let edges = check_incoming_edges(ent, &self)?;
110
111 for edge in edges {
112 self.create_edge(edge)?;
113 }
114
115 self.commit()?;
116
117 Ok(())
118 }
119
120 fn list_entities(
127 &self,
128 entity_type: &str,
129 cursor: Option<Id>,
130 limit: usize,
131 ) -> Result<Vec<Box<dyn Ent>>, DatabaseError>;
132}