1use ents::{
2 DatabaseError, DraftError, Edge, EdgeDraft, EdgeValue, Ent, EntExt, Id,
3 IncomingEdgeProvider, 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 draft = E::EdgeProvider::draft(ent);
74 let mut drafted_edges = draft.check(self)?;
75 drafted_edges.sort_by(|a, b| {
76 (&a.source, &a.sort_key).cmp(&(&b.source, &b.sort_key))
77 });
78
79 if existing_edges != drafted_edges {
81 return Err(AuditError::EdgeMismatch {
82 existing: existing_edges,
83 drafted: drafted_edges,
84 });
85 }
86
87 Ok(())
90 }
91
92 fn fix_ent_edges<E: Ent>(self, id: Id) -> Result<(), AuditError>
93 where
94 Self: Sized,
95 {
96 let ent_box = self.get(id)?.ok_or(AuditError::EntityNotFound(id))?;
98
99 let ent = ent_box.as_ent::<E>().ok_or_else(|| {
100 AuditError::UnexpectedEntityType(
101 id,
102 std::any::type_name::<E>().to_string(),
103 )
104 })?;
105
106 self.remove_edges_by_dest(id)?;
108
109 let draft = E::EdgeProvider::draft(ent);
111 let edges = draft.check(&self)?;
112
113 for edge in edges {
114 self.create_edge(edge)?;
115 }
116
117 self.commit()?;
118
119 Ok(())
120 }
121
122 fn list_entities(
129 &self,
130 entity_type: &str,
131 cursor: Option<Id>,
132 limit: usize,
133 ) -> Result<Vec<Box<dyn Ent>>, DatabaseError>;
134}