teaql_core/entity_graph.rs
1use crate::{Entity, Record, TeaqlEntity};
2
3/// Operation hint for an entity graph node.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum EntityGraphOperation {
6 /// Upsert: insert if new, update if exists (default).
7 Save,
8 /// Delete: soft-delete the entity.
9 Delete,
10}
11
12/// A single node in an annotated entity graph.
13///
14/// Carries the entity's record data, an optional business-intent comment,
15/// and child nodes keyed by relation name.
16#[derive(Debug, Clone)]
17pub struct EntityGraphNode {
18 pub entity_type: String,
19 pub record: Record,
20 pub comment: Option<String>,
21 pub operation: EntityGraphOperation,
22 pub children: Vec<(String, EntityGraphNode)>,
23}
24
25/// Builder for constructing an annotated entity graph that preserves
26/// comment trace chains through the save pipeline.
27///
28/// # Example
29///
30/// ```ignore
31/// let graph = EntityGraph::new(task)
32/// .comment("Create task 'Deploy v2'")
33/// .child("task_execution_log_list",
34/// EntityGraph::new(log)
35/// .comment("Create task 'Deploy v2'"))
36/// .build();
37/// ```
38pub struct EntityGraphBuilder {
39 node: EntityGraphNode,
40}
41
42impl EntityGraphBuilder {
43 /// Set a business-intent comment on this node.
44 /// The comment will appear in SQL debug logs and audit trails
45 /// as part of the hierarchical trace chain.
46 pub fn comment(mut self, comment: impl Into<String>) -> Self {
47 self.node.comment = Some(comment.into());
48 self
49 }
50
51 /// Mark this node for deletion instead of save.
52 pub fn delete(mut self) -> Self {
53 self.node.operation = EntityGraphOperation::Delete;
54 self
55 }
56
57 /// Attach a child entity under the given relation name.
58 pub fn child(mut self, relation: impl Into<String>, child: EntityGraphBuilder) -> Self {
59 self.node.children.push((relation.into(), child.node));
60 self
61 }
62
63 /// Finalize and produce the `EntityGraph`.
64 pub fn build(self) -> EntityGraph {
65 EntityGraph { root: self.node }
66 }
67}
68
69/// An annotated entity graph ready for saving.
70///
71/// Unlike raw `entity.save()`, this structure preserves business-intent
72/// comments at every hop in the graph, producing proper trace chains
73/// in SQL logs and audit trails.
74pub struct EntityGraph {
75 pub root: EntityGraphNode,
76}
77
78impl EntityGraph {
79 /// Start building from an entity.
80 pub fn new<T: Entity + TeaqlEntity>(entity: T) -> EntityGraphBuilder {
81 EntityGraphBuilder {
82 node: EntityGraphNode {
83 entity_type: T::entity_descriptor().name.clone(),
84 record: entity.into_record(),
85 comment: None,
86 operation: EntityGraphOperation::Save,
87 children: Vec::new(),
88 },
89 }
90 }
91}