1use std::{fmt, time::Duration};
2
3use sim_kernel::{CapabilityName, Datum, Symbol};
4use sim_relation_core::{Cell, Row, RowType};
5use sim_relation_migrate::CheckedProgram;
6use sim_relation_plan::{CheckedMutation, CheckedQuery};
7
8#[derive(Clone, Debug, PartialEq, Eq)]
12pub enum StorageLocator {
13 Memory,
15 Preopened {
17 reference: Symbol,
19 access: StorageAccess,
21 },
22}
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub enum StorageAccess {
27 ReadOnly,
29 ReadWrite,
31}
32
33impl StorageLocator {
34 pub fn from_datum(value: &Datum) -> Result<Self, SiteError> {
36 let Datum::Node { tag, fields } = value else {
37 return Err(SiteError::Locator);
38 };
39 if tag == &Symbol::qualified("relation", "memory") && fields.is_empty() {
40 return Ok(Self::Memory);
41 }
42 if tag != &Symbol::qualified("relation", "preopened") {
43 return Err(SiteError::Locator);
44 }
45 let field = |name: &str| {
46 fields
47 .iter()
48 .find(|(key, _)| key == &Symbol::new(name))
49 .map(|(_, value)| value)
50 };
51 if fields.len() != 2 {
52 return Err(SiteError::Locator);
53 }
54 let Some(Datum::Symbol(reference)) = field("ref") else {
55 return Err(SiteError::Locator);
56 };
57 let access = match field("access") {
58 Some(Datum::Symbol(value)) if value == &Symbol::new("read-only") => {
59 StorageAccess::ReadOnly
60 }
61 Some(Datum::Symbol(value)) if value == &Symbol::new("read-write") => {
62 StorageAccess::ReadWrite
63 }
64 _ => return Err(SiteError::Locator),
65 };
66 Ok(Self::Preopened {
67 reference: reference.clone(),
68 access,
69 })
70 }
71}
72
73#[derive(Clone, Debug, PartialEq, Eq)]
75pub struct DriverManifest {
76 pub site: Symbol,
78 pub provider: Symbol,
80}
81impl DriverManifest {
82 pub fn sqlite(site: Symbol, provider: Symbol) -> Result<Self, SiteError> {
84 if site != Symbol::qualified("relation/site", "sqlite")
85 || provider != Symbol::qualified("relation/provider", "sqlite")
86 {
87 return Err(SiteError::Registration);
88 }
89 Ok(Self { site, provider })
90 }
91}
92
93#[derive(Clone, Debug, PartialEq, Eq)]
95pub struct RelationPlacement {
96 pub(crate) site: Symbol,
97 pub(crate) locator: Datum,
98}
99impl RelationPlacement {
100 pub fn new(site: Symbol, locator: Datum) -> Self {
102 Self { site, locator }
103 }
104 pub fn site(&self) -> &Symbol {
106 &self.site
107 }
108 pub fn locator(&self) -> &Datum {
110 &self.locator
111 }
112}
113
114#[derive(Clone, Copy, Debug, PartialEq, Eq)]
116pub struct Limits {
117 pub rows: u64,
119 pub cells: u64,
121 pub bytes: u64,
123 pub work: u64,
125 pub deadline: Option<Duration>,
127 pub buffer_bytes: Option<u64>,
129}
130impl Limits {
131 pub fn new(rows: u64, cells: u64, bytes: u64, work: u64) -> Result<Self, SiteError> {
133 if [rows, cells, bytes, work].contains(&0) {
134 return Err(SiteError::InvalidLimits);
135 }
136 Ok(Self {
137 rows,
138 cells,
139 bytes,
140 work,
141 deadline: None,
142 buffer_bytes: None,
143 })
144 }
145}
146
147#[derive(Clone, Debug, PartialEq, Eq)]
149pub struct Bindings(Row);
150impl Bindings {
151 pub fn new(
153 expected: &RowType,
154 cells: impl IntoIterator<Item = Cell>,
155 ) -> Result<Self, SiteError> {
156 Row::new(expected.clone(), cells)
157 .map(Self)
158 .map_err(|e| SiteError::Bindings(e.to_string()))
159 }
160 pub fn row(&self) -> &Row {
162 &self.0
163 }
164}
165
166pub trait RowSink {
168 fn push(&mut self, row: Row) -> Result<(), SiteError>;
170}
171
172#[derive(Clone, Debug, Default, PartialEq, Eq)]
177pub struct VecRowSink {
178 rows: Vec<Row>,
179}
180
181impl VecRowSink {
182 pub fn rows(&self) -> &[Row] {
184 &self.rows
185 }
186
187 pub fn into_rows(self) -> Vec<Row> {
189 self.rows
190 }
191}
192
193impl RowSink for VecRowSink {
194 fn push(&mut self, row: Row) -> Result<(), SiteError> {
195 self.rows.push(row);
196 Ok(())
197 }
198}
199
200#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
202pub struct ProviderStats {
203 pub work: u64,
205 pub affected: u64,
207}
208
209#[derive(Clone, Copy, Debug, PartialEq, Eq)]
211pub enum LimitKind {
212 Rows,
214 Cells,
216 Bytes,
218 Work,
220 Deadline,
222}
223
224#[derive(Clone, Debug, PartialEq, Eq)]
226pub struct Receipt {
227 pub operation_id: String,
229 pub operation: Operation,
231 pub rows: u64,
233 pub cells: u64,
235 pub bytes: u64,
237 pub work: u64,
239 pub affected: u64,
241}
242
243#[derive(Clone, Copy, Debug, PartialEq, Eq)]
245pub enum Operation {
246 Read,
248 Write,
250 Schema,
252 Migrate,
254 Transaction,
256 Attach,
258}
259impl Operation {
260 fn label(self) -> &'static str {
261 match self {
262 Self::Read => "read",
263 Self::Write => "write",
264 Self::Schema => "schema",
265 Self::Migrate => "migrate",
266 Self::Transaction => "transaction",
267 Self::Attach => "attach",
268 }
269 }
270 pub(crate) fn capability(self) -> CapabilityName {
271 CapabilityName::new(format!("relation.{}", self.label()))
272 }
273 pub(crate) fn effect(self) -> Symbol {
274 Symbol::qualified("effect/relation", self.label())
275 }
276}
277
278#[derive(Clone, Debug, PartialEq, Eq)]
280pub enum SiteError {
281 InvalidLimits,
283 Bindings(String),
285 RowType,
287 Limit(LimitKind),
289 Locator,
291 Registration,
293 Constraint,
295 Locked,
297 ReadOnly,
299 Interrupted,
301 Corruption,
303 Conversion,
305 Drift,
307 Provider,
309 Kernel(String),
311}
312impl fmt::Display for SiteError {
313 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314 write!(f, "{self:?}")
315 }
316}
317impl std::error::Error for SiteError {}
318
319pub trait Session {
321 fn query(
323 &mut self,
324 plan: &CheckedQuery,
325 bindings: &Bindings,
326 limits: &Limits,
327 sink: &mut dyn RowSink,
328 ) -> Result<ProviderStats, SiteError>;
329 fn mutate(
331 &mut self,
332 plan: &CheckedMutation,
333 bindings: &Bindings,
334 limits: &Limits,
335 sink: &mut dyn RowSink,
336 ) -> Result<ProviderStats, SiteError>;
337 fn migrate(
339 &mut self,
340 program: &CheckedProgram,
341 limits: &Limits,
342 ) -> Result<ProviderStats, SiteError>;
343 fn schema(
345 &mut self,
346 program: &CheckedProgram,
347 limits: &Limits,
348 ) -> Result<ProviderStats, SiteError>;
349 fn transaction(
351 &mut self,
352 body: &mut dyn FnMut(&mut dyn Transaction) -> Result<(), SiteError>,
353 ) -> Result<(), SiteError>;
354 fn attach(&mut self, locator: &Datum, limits: &Limits) -> Result<ProviderStats, SiteError>;
356}
357
358pub trait Transaction: Session {
360 fn savepoint(
362 &mut self,
363 body: &mut dyn FnMut(&mut dyn Transaction) -> Result<(), SiteError>,
364 ) -> Result<(), SiteError>;
365}
366
367pub trait Driver: Send + Sync {
369 fn connect(&self, locator: &Datum, limits: &Limits) -> Result<Box<dyn Session>, SiteError>;
371}