1use anyhow::{Result, anyhow};
20use ast::Arena;
21use mangle_ast as ast;
22
23mod tablestore;
24pub use tablestore::{TableConfig, TableStoreImpl, TableStoreSchema};
25
26#[cfg(feature = "edge")]
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
33pub enum CompoundKind {
34 List,
35 Pair,
36 Map,
37 Struct,
38}
39
40#[cfg(feature = "edge")]
41#[derive(Debug, Clone)]
42pub enum Value {
43 Number(i64),
44 Float(f64),
45 String(String),
46 Time(i64),
48 Duration(i64),
50 Compound(CompoundKind, Vec<Value>),
55 Null, }
57
58#[cfg(feature = "edge")]
59impl PartialEq for Value {
60 fn eq(&self, other: &Self) -> bool {
61 match (self, other) {
62 (Value::Number(a), Value::Number(b)) => a == b,
63 (Value::Float(a), Value::Float(b)) => a.to_bits() == b.to_bits(),
64 (Value::String(a), Value::String(b)) => a == b,
65 (Value::Time(a), Value::Time(b)) => a == b,
66 (Value::Duration(a), Value::Duration(b)) => a == b,
67 (Value::Compound(ka, a), Value::Compound(kb, b)) => ka == kb && a == b,
68 (Value::Null, Value::Null) => true,
69 _ => false,
70 }
71 }
72}
73
74#[cfg(feature = "edge")]
75impl Eq for Value {}
76
77#[cfg(feature = "edge")]
78impl std::hash::Hash for Value {
79 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
80 std::mem::discriminant(self).hash(state);
81 match self {
82 Value::Number(n) => n.hash(state),
83 Value::Float(f) => f.to_bits().hash(state),
84 Value::String(s) => s.hash(state),
85 Value::Time(t) => t.hash(state),
86 Value::Duration(d) => d.hash(state),
87 Value::Compound(k, v) => {
88 k.hash(state);
89 v.hash(state);
90 }
91 Value::Null => {}
92 }
93 }
94}
95
96#[cfg(feature = "edge")]
97impl PartialOrd for Value {
98 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
99 Some(self.cmp(other))
100 }
101}
102
103#[cfg(feature = "edge")]
104impl Ord for Value {
105 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
106 match (self, other) {
107 (Value::Number(a), Value::Number(b)) => a.cmp(b),
108 (Value::Float(a), Value::Float(b)) => a.total_cmp(b),
109 (Value::Number(a), Value::Float(b)) => (*a as f64).total_cmp(b),
111 (Value::Float(a), Value::Number(b)) => a.total_cmp(&(*b as f64)),
112 (Value::String(a), Value::String(b)) => a.cmp(b),
113 (Value::Time(a), Value::Time(b)) => a.cmp(b),
114 (Value::Duration(a), Value::Duration(b)) => a.cmp(b),
115 (Value::Duration(a), Value::Number(b)) => a.cmp(b),
117 (Value::Number(a), Value::Duration(b)) => a.cmp(b),
118 (Value::Time(a), Value::Number(b)) => a.cmp(b),
120 (Value::Number(a), Value::Time(b)) => a.cmp(b),
121 (Value::Compound(ka, a), Value::Compound(kb, b)) => ka.cmp(kb).then_with(|| a.cmp(b)),
122 (Value::Null, Value::Null) => std::cmp::Ordering::Equal,
123 (Value::Number(_) | Value::Float(_), _) => std::cmp::Ordering::Less,
125 (_, Value::Number(_) | Value::Float(_)) => std::cmp::Ordering::Greater,
126 (Value::String(_), _) => std::cmp::Ordering::Less,
127 (_, Value::String(_)) => std::cmp::Ordering::Greater,
128 (Value::Time(_), _) => std::cmp::Ordering::Less,
129 (_, Value::Time(_)) => std::cmp::Ordering::Greater,
130 (Value::Duration(_), _) => std::cmp::Ordering::Less,
131 (_, Value::Duration(_)) => std::cmp::Ordering::Greater,
132 (Value::Compound(..), _) => std::cmp::Ordering::Less,
133 (_, Value::Compound(..)) => std::cmp::Ordering::Greater,
134 }
135 }
136}
137
138#[cfg(feature = "edge")]
139impl std::fmt::Display for Value {
140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141 match self {
142 Value::Number(n) => write!(f, "{n}"),
143 Value::Float(v) => write!(f, "{v}"),
144 Value::String(s) => write!(f, "{s:?}"),
145 Value::Time(nanos) => write!(f, "{}", format_time_nanos(*nanos)),
146 Value::Duration(nanos) => write!(f, "{}", format_duration_nanos(*nanos)),
147 Value::Compound(kind, elems) => match kind {
148 CompoundKind::List | CompoundKind::Pair => {
149 write!(f, "[")?;
150 for (i, e) in elems.iter().enumerate() {
151 if i > 0 {
152 write!(f, ", ")?;
153 }
154 write!(f, "{e}")?;
155 }
156 write!(f, "]")
157 }
158 CompoundKind::Map => {
159 write!(f, "[")?;
160 for (i, pair) in elems.chunks_exact(2).enumerate() {
161 if i > 0 {
162 write!(f, ", ")?;
163 }
164 write!(f, "{}: {}", pair[0], pair[1])?;
165 }
166 write!(f, "]")
167 }
168 CompoundKind::Struct => {
169 write!(f, "{{")?;
170 for (i, pair) in elems.chunks_exact(2).enumerate() {
171 if i > 0 {
172 write!(f, ", ")?;
173 }
174 write!(f, "{}: {}", pair[0], pair[1])?;
175 }
176 write!(f, "}}")
177 }
178 },
179 Value::Null => write!(f, "null"),
180 }
181 }
182}
183
184#[cfg(feature = "edge")]
185fn format_time_nanos(nanos: i64) -> String {
186 let secs = nanos.div_euclid(1_000_000_000);
187 let ns = nanos.rem_euclid(1_000_000_000) as u32;
188
189 let days = secs.div_euclid(86400);
192 let time_of_day = secs.rem_euclid(86400);
193 let hour = time_of_day / 3600;
194 let minute = (time_of_day % 3600) / 60;
195 let second = time_of_day % 60;
196
197 let z = days + 719468;
199 let era = (if z >= 0 { z } else { z - 146096 }) / 146097;
200 let doe = (z - era * 146097) as u32;
201 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
202 let y = yoe as i64 + era * 400;
203 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
204 let mp = (5 * doy + 2) / 153;
205 let d = doy - (153 * mp + 2) / 5 + 1;
206 let m = if mp < 10 { mp + 3 } else { mp - 9 };
207 let y = if m <= 2 { y + 1 } else { y };
208
209 if ns == 0 {
210 format!("{y:04}-{m:02}-{d:02}T{hour:02}:{minute:02}:{second:02}Z")
211 } else {
212 let mut frac = format!("{ns:09}");
214 frac = frac.trim_end_matches('0').to_string();
215 format!("{y:04}-{m:02}-{d:02}T{hour:02}:{minute:02}:{second:02}.{frac}Z")
216 }
217}
218
219#[cfg(feature = "edge")]
222fn format_duration_nanos(nanos: i64) -> String {
223 if nanos == 0 {
224 return "0s".to_string();
225 }
226
227 let mut result = String::new();
228 let mut remaining = if nanos < 0 {
229 result.push('-');
230 nanos.unsigned_abs()
231 } else {
232 nanos as u64
233 };
234
235 const NANOS_PER_NS: u64 = 1;
236 const NANOS_PER_US: u64 = 1_000;
237 const NANOS_PER_MS: u64 = 1_000_000;
238 const NANOS_PER_S: u64 = 1_000_000_000;
239 const NANOS_PER_M: u64 = 60 * NANOS_PER_S;
240 const NANOS_PER_H: u64 = 60 * NANOS_PER_M;
241
242 if remaining >= NANOS_PER_S {
244 let hours = remaining / NANOS_PER_H;
245 remaining %= NANOS_PER_H;
246 let minutes = remaining / NANOS_PER_M;
247 remaining %= NANOS_PER_M;
248 let seconds = remaining / NANOS_PER_S;
249 let sub_second_nanos = remaining % NANOS_PER_S;
250
251 if hours > 0 {
252 result.push_str(&format!("{hours}h"));
253 }
254 if minutes > 0 || hours > 0 {
255 result.push_str(&format!("{minutes}m"));
256 }
257 if sub_second_nanos == 0 {
258 result.push_str(&format!("{seconds}s"));
259 } else {
260 let frac = format!("{sub_second_nanos:09}");
262 let frac = frac.trim_end_matches('0');
263 result.push_str(&format!("{seconds}.{frac}s"));
264 }
265 } else if remaining >= NANOS_PER_MS {
266 let ms = remaining / NANOS_PER_MS;
268 let sub = remaining % NANOS_PER_MS;
269 if sub == 0 {
270 result.push_str(&format!("{ms}ms"));
271 } else {
272 let frac = format!("{sub:06}");
273 let frac = frac.trim_end_matches('0');
274 result.push_str(&format!("{ms}.{frac}ms"));
275 }
276 } else if remaining >= NANOS_PER_US {
277 let us = remaining / NANOS_PER_US;
279 let sub = remaining % NANOS_PER_US;
280 if sub == 0 {
281 result.push_str(&format!("{us}µs"));
282 } else {
283 let frac = format!("{sub:03}");
284 let frac = frac.trim_end_matches('0');
285 result.push_str(&format!("{us}.{frac}µs"));
286 }
287 } else {
288 result.push_str(&format!("{}ns", remaining));
290 }
291
292 result
293}
294
295#[cfg(feature = "edge")]
307pub trait Store {
308 fn scan(&self, relation: &str) -> Result<Box<dyn Iterator<Item = Vec<Value>> + '_>>;
311
312 fn scan_delta(&self, relation: &str) -> Result<Box<dyn Iterator<Item = Vec<Value>> + '_>>;
314
315 fn scan_next_delta(&self, relation: &str) -> Result<Box<dyn Iterator<Item = Vec<Value>> + '_>>;
317
318 fn scan_index(
320 &self,
321 relation: &str,
322 col_idx: usize,
323 key: &Value,
324 ) -> Result<Box<dyn Iterator<Item = Vec<Value>> + '_>>;
325
326 fn scan_delta_index(
328 &self,
329 relation: &str,
330 col_idx: usize,
331 key: &Value,
332 ) -> Result<Box<dyn Iterator<Item = Vec<Value>> + '_>>;
333
334 fn insert(&mut self, relation: &str, tuple: Vec<Value>) -> Result<bool>;
337
338 fn merge_deltas(&mut self);
340
341 fn create_relation(&mut self, relation: &str);
343
344 fn retract(&mut self, relation: &str, tuple: &[Value]) -> Result<bool>;
347
348 fn clear(&mut self, relation: &str);
350
351 fn relation_names(&self) -> Vec<String>;
353
354 fn coalesce_temporal(&mut self, _relation: &str) {}
359}
360
361#[cfg(feature = "server")]
364#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
365pub struct HostVal(pub u32);
366
367#[cfg(feature = "server")]
373pub trait Host {
374 fn scan_start(&mut self, rel_id: i32) -> i32;
376 fn scan_delta_start(&mut self, rel_id: i32) -> i32;
377 fn scan_next(&mut self, iter_id: i32) -> i32;
378 fn merge_deltas(&mut self) -> i32;
380 fn scan_aggregate_start(&mut self, rel_id: i32, description: Vec<i32>) -> i32;
381 fn scan_index_start(&mut self, rel_id: i32, col_idx: i32, val: HostVal) -> i32;
382
383 fn get_col(&mut self, tuple_ptr: i32, col_idx: i32) -> HostVal;
385
386 fn insert_begin(&mut self, rel_id: i32);
388 fn insert_push(&mut self, val: HostVal);
389 fn insert_end(&mut self);
390
391 fn const_number(&mut self, n: i64) -> HostVal;
393 fn const_float(&mut self, bits: i64) -> HostVal;
394 fn const_string(&mut self, id: i32) -> HostVal;
395 fn const_name(&mut self, id: i32) -> HostVal;
396 fn const_time(&mut self, nanos: i64) -> HostVal;
397 fn const_duration(&mut self, nanos: i64) -> HostVal;
398
399 fn val_add(&mut self, a: HostVal, b: HostVal) -> HostVal;
401 fn val_sub(&mut self, a: HostVal, b: HostVal) -> HostVal;
402 fn val_mul(&mut self, a: HostVal, b: HostVal) -> HostVal;
403 fn val_div(&mut self, a: HostVal, b: HostVal) -> HostVal;
404 fn val_sqrt(&mut self, a: HostVal) -> HostVal;
405
406 fn val_eq(&mut self, a: HostVal, b: HostVal) -> i32;
408 fn val_neq(&mut self, a: HostVal, b: HostVal) -> i32;
409 fn val_lt(&mut self, a: HostVal, b: HostVal) -> i32;
410 fn val_le(&mut self, a: HostVal, b: HostVal) -> i32;
411 fn val_gt(&mut self, a: HostVal, b: HostVal) -> i32;
412 fn val_ge(&mut self, a: HostVal, b: HostVal) -> i32;
413
414 fn str_concat(&mut self, a: HostVal, b: HostVal) -> HostVal;
416 fn str_replace(&mut self, s: HostVal, old: HostVal, new: HostVal, count: HostVal) -> HostVal;
417 fn val_to_string(&mut self, val: HostVal) -> HostVal;
418
419 fn compound_begin(&mut self, kind: i32);
422 fn compound_push(&mut self, val: HostVal);
423 fn compound_end(&mut self) -> HostVal;
424 fn compound_get(&mut self, compound: HostVal, key: HostVal) -> HostVal;
426 fn compound_len(&mut self, compound: HostVal) -> HostVal;
428 fn pair_first(&mut self, compound: HostVal) -> HostVal;
429 fn pair_second(&mut self, compound: HostVal) -> HostVal;
430
431 fn debuglog(&mut self, val: HostVal);
433}
434
435pub trait Receiver<'a> {
438 fn next(&self, item: &'a ast::Atom<'a>) -> Result<()>;
439}
440
441impl<'a, Closure: Fn(&'a ast::Atom<'a>) -> Result<()>> Receiver<'a> for Closure {
442 fn next(&self, item: &'a ast::Atom<'a>) -> Result<()> {
443 (*self)(item)
444 }
445}
446
447pub trait ReadOnlyFactStore<'a> {
449 fn arena(&'a self) -> &'a Arena;
450
451 fn contains<'src>(&'a self, src: &'src Arena, fact: &'src ast::Atom<'src>) -> Result<bool>;
452
453 fn get<'query, R: Receiver<'a>>(
456 &'a self,
457 query_sym: ast::PredicateIndex,
458 query_args: &'query [&'query ast::BaseTerm<'query>],
459 cb: &R,
460 ) -> Result<()>;
461
462 fn predicates(&'a self) -> Vec<ast::PredicateIndex>;
465
466 fn estimate_fact_count(&self) -> u32;
468}
469
470pub trait FactStore<'a>: ReadOnlyFactStore<'a> {
473 fn add<'src>(&'a self, src: &'src Arena, fact: &'src ast::Atom<'src>) -> Result<bool>;
476
477 fn merge<'src, S>(&'a self, src: &'src Arena, store: &'src S)
479 where
480 S: ReadOnlyFactStore<'src>;
481}
482
483pub fn get_all_facts<'a, S, R: Receiver<'a>>(store: &'a S, cb: &R) -> Result<()>
485where
486 S: ReadOnlyFactStore<'a> + 'a,
487{
488 let arena = Arena::new_with_global_interner();
489 let preds = store.predicates();
490
491 for pred in preds {
492 arena.copy_predicate_sym(store.arena(), pred);
493 store.get(pred, arena.new_query(pred).args, cb)?;
494 }
495 Ok(())
496}