1#![allow(async_fn_in_trait)]
2
3use std::time::SystemTime;
4use teaql_core::{
5 DeleteCommand, InsertCommand, Record, RecoverCommand, SelectQuery, TraceNode, UpdateCommand,
6};
7
8#[derive(Debug, Clone, Default)]
9pub struct DataServiceCapabilities {
10 pub query: bool,
11 pub mutation: bool,
12 pub transaction: bool,
13 pub schema: bool,
14 pub id_generation: bool,
15 pub batch_mutation: bool,
16 pub returning: bool,
17}
18
19#[derive(Debug, Clone)]
20pub struct QueryRequest {
21 pub query: SelectQuery,
22 pub trace_chain: Vec<TraceNode>,
23 pub comment: Option<String>,
24}
25
26#[derive(Debug, Clone)]
27pub struct QueryResult {
28 pub rows: Vec<Record>,
29 pub metadata: ExecutionMetadata,
30}
31
32#[derive(Debug, Clone)]
33pub enum MutationRequest {
34 Insert(InsertCommand),
35 Update(UpdateCommand),
36 Delete(DeleteCommand),
37 Recover(RecoverCommand),
38 Batch(Vec<MutationRequest>),
39}
40
41impl MutationRequest {
42 pub fn trace_chain(&self) -> &[teaql_core::TraceNode] {
43 match self {
44 MutationRequest::Insert(cmd) => &cmd.trace_chain,
45 MutationRequest::Update(cmd) => &cmd.trace_chain,
46 MutationRequest::Delete(cmd) => &cmd.trace_chain,
47 MutationRequest::Recover(cmd) => &cmd.trace_chain,
48 MutationRequest::Batch(_) => &[], }
50 }
51
52 pub fn comment(&self) -> Option<&str> {
53 match self {
54 MutationRequest::Insert(cmd) => cmd.trace_chain.last().map(|n| n.comment.as_str()),
55 MutationRequest::Update(cmd) => cmd.trace_chain.last().map(|n| n.comment.as_str()),
56 MutationRequest::Delete(cmd) => cmd.trace_chain.last().map(|n| n.comment.as_str()),
57 MutationRequest::Recover(cmd) => cmd.trace_chain.last().map(|n| n.comment.as_str()),
58 MutationRequest::Batch(_) => None,
59 }
60 }
61}
62
63#[derive(Debug, Clone)]
64pub struct MutationResult {
65 pub affected_rows: u64,
66 pub generated_values: Record,
67 pub metadata: ExecutionMetadata,
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum DataServiceOperation {
72 Query,
73 Insert,
74 Update,
75 Delete,
76 Recover,
77 Batch,
78 Schema,
79}
80
81#[derive(Debug, Clone)]
82pub struct ExecutionMetadata {
83 pub backend: String,
84 pub operation: DataServiceOperation,
85 pub started_at: SystemTime,
86 pub ended_at: SystemTime,
87 pub affected_rows: Option<u64>,
88 pub result_count: Option<usize>,
89 pub trace_chain: Vec<TraceNode>,
90 pub comment: Option<String>,
91 pub backend_request_id: Option<String>,
92 pub debug_query: Option<String>,
93}
94
95pub trait DataServiceExecutor {
96 type Error: std::error::Error + Send + Sync + 'static;
97
98 fn capabilities(&self) -> DataServiceCapabilities;
99}
100
101pub trait QueryExecutor: DataServiceExecutor {
102 fn query(
103 &self,
104 request: QueryRequest,
105 ) -> impl std::future::Future<Output = Result<QueryResult, Self::Error>> + Send;
106}
107
108#[derive(Debug, Clone)]
110pub struct StreamChunk {
111 pub rows: Vec<Record>,
112 pub chunk_index: usize,
113 pub is_last: bool,
114}
115
116pub trait StreamQueryExecutor: DataServiceExecutor {
118 fn query_stream(
119 &self,
120 request: QueryRequest,
121 chunk_size: usize,
122 ) -> impl std::future::Future<Output = Result<Vec<StreamChunk>, Self::Error>> + Send;
123}
124
125pub trait MutationExecutor: DataServiceExecutor {
126 fn mutate(
127 &self,
128 request: MutationRequest,
129 ) -> impl std::future::Future<Output = Result<MutationResult, Self::Error>> + Send;
130}
131
132pub trait TransactionExecutor: DataServiceExecutor {
133 type Tx<'a>: QueryExecutor<Error = Self::Error>
134 + MutationExecutor<Error = Self::Error>
135 + Transaction<Error = Self::Error>
136 where
137 Self: 'a;
138
139 fn begin(&self) -> impl std::future::Future<Output = Result<Self::Tx<'_>, Self::Error>> + Send;
140}
141
142pub trait Transaction {
143 type Error: std::error::Error + Send + Sync + 'static;
144
145 fn commit(self) -> impl std::future::Future<Output = Result<(), Self::Error>> + Send;
146 fn rollback(self) -> impl std::future::Future<Output = Result<(), Self::Error>> + Send;
147}
148
149#[derive(Debug, Clone)]
150pub struct SchemaRequest {
151 pub entity_name: String,
152}
153
154#[derive(Debug, Clone)]
155pub struct SchemaResult {
156 pub changed: bool,
157}
158
159pub trait SchemaExecutor: DataServiceExecutor {
160 fn ensure_schema(
161 &self,
162 request: SchemaRequest,
163 ) -> impl std::future::Future<Output = Result<SchemaResult, Self::Error>> + Send;
164}
165
166pub trait IdGeneratorExecutor: DataServiceExecutor {
167 fn next_id(
168 &self,
169 entity: &str,
170 ) -> impl std::future::Future<Output = Result<u64, Self::Error>> + Send;
171}
172
173pub trait SchemaProvider: Send + Sync {
174 fn get_entity(&self, name: &str) -> Option<std::sync::Arc<teaql_core::EntityDescriptor>>;
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn test_mutation_request_trace_and_comment_accessors() {
183 let trace1 = TraceNode {
184 entity_type: "User".to_string(),
185 entity_id: Some(1),
186 comment: "Create User".to_string(),
187 };
188 let trace2 = TraceNode {
189 entity_type: "Profile".to_string(),
190 entity_id: None,
191 comment: "Create Profile".to_string(),
192 };
193 let trace_chain = vec![trace1.clone(), trace2.clone()];
194
195 let insert_cmd = InsertCommand {
197 entity: "User".to_string(),
198 values: Record::new(),
199 trace_chain: trace_chain.clone(),
200 };
201 let req_insert = MutationRequest::Insert(insert_cmd);
202 assert_eq!(req_insert.trace_chain().len(), 2);
203 assert_eq!(req_insert.trace_chain()[1], trace2);
204 assert_eq!(req_insert.comment(), Some("Create Profile"));
205
206 let update_cmd = UpdateCommand {
208 entity: "User".to_string(),
209 id: teaql_core::Value::I64(1),
210 values: Record::new(),
211 expected_version: None,
212 old_values: None,
213 trace_chain: trace_chain.clone(),
214 };
215 let req_update = MutationRequest::Update(update_cmd);
216 assert_eq!(req_update.trace_chain().len(), 2);
217 assert_eq!(req_update.comment(), Some("Create Profile"));
218
219 let delete_cmd = DeleteCommand {
221 entity: "User".to_string(),
222 id: teaql_core::Value::I64(1),
223 expected_version: None,
224 soft_delete: true,
225 trace_chain: trace_chain.clone(),
226 };
227 let req_delete = MutationRequest::Delete(delete_cmd);
228 assert_eq!(req_delete.trace_chain().len(), 2);
229 assert_eq!(req_delete.comment(), Some("Create Profile"));
230
231 let recover_cmd = RecoverCommand {
233 entity: "User".to_string(),
234 id: teaql_core::Value::I64(1),
235 expected_version: 1,
236 trace_chain: trace_chain.clone(),
237 };
238 let req_recover = MutationRequest::Recover(recover_cmd);
239 assert_eq!(req_recover.trace_chain().len(), 2);
240 assert_eq!(req_recover.comment(), Some("Create Profile"));
241
242 let req_batch = MutationRequest::Batch(vec![req_insert, req_update]);
244 assert_eq!(req_batch.trace_chain().len(), 0);
245 assert_eq!(req_batch.comment(), None);
246
247 let insert_empty = InsertCommand {
249 entity: "User".to_string(),
250 values: Record::new(),
251 trace_chain: vec![],
252 };
253 let req_empty = MutationRequest::Insert(insert_empty);
254 assert_eq!(req_empty.trace_chain().len(), 0);
255 assert_eq!(req_empty.comment(), None);
256 }
257}