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 audit_ent_edges<E: Ent>(&self, id: Id) -> Result<(), AuditError>
34 where
35 Self: Sized,
36 {
37 let ent_box = self.get(id)?.ok_or(AuditError::EntityNotFound(id))?;
39
40 let ent = ent_box.as_ent::<E>().ok_or_else(|| {
41 AuditError::UnexpectedEntityType(
42 id,
43 std::any::type_name::<E>().to_string(),
44 )
45 })?;
46
47 let mut existing_edges: Vec<EdgeValue> = self
49 .find_edges_by_dest(id)?
50 .into_iter()
51 .map(|e| EdgeValue::new(e.source, e.sort_key, e.dest))
52 .collect();
53 existing_edges.sort_by(|a, b| {
54 (&a.source, &a.sort_key).cmp(&(&b.source, &b.sort_key))
55 });
56
57 self.remove_edges_by_dest(id)?;
59
60 let draft = E::EdgeProvider::draft(ent);
62 let mut drafted_edges = draft.check(self)?;
63 drafted_edges.sort_by(|a, b| {
64 (&a.source, &a.sort_key).cmp(&(&b.source, &b.sort_key))
65 });
66
67 if existing_edges != drafted_edges {
69 return Err(AuditError::EdgeMismatch {
70 existing: existing_edges,
71 drafted: drafted_edges,
72 });
73 }
74
75 Ok(())
78 }
79
80 fn fix_ent_edges<E: Ent>(self, id: Id) -> Result<(), AuditError>
81 where
82 Self: Sized,
83 {
84 let ent_box = self.get(id)?.ok_or(AuditError::EntityNotFound(id))?;
86
87 let ent = ent_box.as_ent::<E>().ok_or_else(|| {
88 AuditError::UnexpectedEntityType(
89 id,
90 std::any::type_name::<E>().to_string(),
91 )
92 })?;
93
94 self.remove_edges_by_dest(id)?;
96
97 let draft = E::EdgeProvider::draft(ent);
99 let edges = draft.check(&self)?;
100
101 for edge in edges {
102 self.create_edge(edge)?;
103 }
104
105 self.commit()?;
106
107 Ok(())
108 }
109
110 fn list_entities(
117 &self,
118 entity_type: &str,
119 cursor: Option<Id>,
120 limit: usize,
121 ) -> Result<Vec<Box<dyn Ent>>, DatabaseError>;
122}