use anyhow::{Result, anyhow};
use ast::Arena;
use mangle_ast as ast;
mod tablestore;
pub use tablestore::{TableConfig, TableStoreImpl, TableStoreSchema};
#[cfg(feature = "edge")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum CompoundKind {
List,
Pair,
Map,
Struct,
}
#[cfg(feature = "edge")]
#[derive(Debug, Clone)]
pub enum Value {
Number(i64),
Float(f64),
String(String),
Name(String),
Time(i64),
Duration(i64),
Compound(CompoundKind, Vec<Value>),
Null, }
#[cfg(feature = "edge")]
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::Number(a), Value::Number(b)) => a == b,
(Value::Float(a), Value::Float(b)) => a.to_bits() == b.to_bits(),
(Value::String(a), Value::String(b)) => a == b,
(Value::Name(a), Value::Name(b)) => a == b,
(Value::Time(a), Value::Time(b)) => a == b,
(Value::Duration(a), Value::Duration(b)) => a == b,
(Value::Compound(ka, a), Value::Compound(kb, b)) => ka == kb && a == b,
(Value::Null, Value::Null) => true,
_ => false,
}
}
}
#[cfg(feature = "edge")]
impl Eq for Value {}
#[cfg(feature = "edge")]
impl std::hash::Hash for Value {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state);
match self {
Value::Number(n) => n.hash(state),
Value::Float(f) => f.to_bits().hash(state),
Value::String(s) => s.hash(state),
Value::Name(s) => s.hash(state),
Value::Time(t) => t.hash(state),
Value::Duration(d) => d.hash(state),
Value::Compound(k, v) => {
k.hash(state);
v.hash(state);
}
Value::Null => {}
}
}
}
#[cfg(feature = "edge")]
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[cfg(feature = "edge")]
impl Ord for Value {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match (self, other) {
(Value::Number(a), Value::Number(b)) => a.cmp(b),
(Value::Float(a), Value::Float(b)) => a.total_cmp(b),
(Value::Number(a), Value::Float(b)) => (*a as f64).total_cmp(b),
(Value::Float(a), Value::Number(b)) => a.total_cmp(&(*b as f64)),
(Value::String(a), Value::String(b)) => a.cmp(b),
(Value::Name(a), Value::Name(b)) => a.cmp(b),
(Value::Time(a), Value::Time(b)) => a.cmp(b),
(Value::Duration(a), Value::Duration(b)) => a.cmp(b),
(Value::Duration(a), Value::Number(b)) => a.cmp(b),
(Value::Number(a), Value::Duration(b)) => a.cmp(b),
(Value::Time(a), Value::Number(b)) => a.cmp(b),
(Value::Number(a), Value::Time(b)) => a.cmp(b),
(Value::Compound(ka, a), Value::Compound(kb, b)) => ka.cmp(kb).then_with(|| a.cmp(b)),
(Value::Null, Value::Null) => std::cmp::Ordering::Equal,
(Value::Number(_) | Value::Float(_), _) => std::cmp::Ordering::Less,
(_, Value::Number(_) | Value::Float(_)) => std::cmp::Ordering::Greater,
(Value::String(_), _) => std::cmp::Ordering::Less,
(_, Value::String(_)) => std::cmp::Ordering::Greater,
(Value::Name(_), _) => std::cmp::Ordering::Less,
(_, Value::Name(_)) => std::cmp::Ordering::Greater,
(Value::Time(_), _) => std::cmp::Ordering::Less,
(_, Value::Time(_)) => std::cmp::Ordering::Greater,
(Value::Duration(_), _) => std::cmp::Ordering::Less,
(_, Value::Duration(_)) => std::cmp::Ordering::Greater,
(Value::Compound(..), _) => std::cmp::Ordering::Less,
(_, Value::Compound(..)) => std::cmp::Ordering::Greater,
}
}
}
#[cfg(feature = "edge")]
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Number(n) => write!(f, "{n}"),
Value::Float(v) => write!(f, "{v}"),
Value::String(s) => write!(f, "{s:?}"),
Value::Name(s) => write!(f, "{s}"),
Value::Time(nanos) => write!(f, "{}", format_time_nanos(*nanos)),
Value::Duration(nanos) => write!(f, "{}", format_duration_nanos(*nanos)),
Value::Compound(kind, elems) => match kind {
CompoundKind::List | CompoundKind::Pair => {
write!(f, "[")?;
for (i, e) in elems.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{e}")?;
}
write!(f, "]")
}
CompoundKind::Map => {
write!(f, "[")?;
for (i, pair) in elems.chunks_exact(2).enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}: {}", pair[0], pair[1])?;
}
write!(f, "]")
}
CompoundKind::Struct => {
write!(f, "{{")?;
for (i, pair) in elems.chunks_exact(2).enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}: {}", pair[0], pair[1])?;
}
write!(f, "}}")
}
},
Value::Null => write!(f, "null"),
}
}
}
#[cfg(feature = "edge")]
fn format_time_nanos(nanos: i64) -> String {
let secs = nanos.div_euclid(1_000_000_000);
let ns = nanos.rem_euclid(1_000_000_000) as u32;
let days = secs.div_euclid(86400);
let time_of_day = secs.rem_euclid(86400);
let hour = time_of_day / 3600;
let minute = (time_of_day % 3600) / 60;
let second = time_of_day % 60;
let z = days + 719468;
let era = (if z >= 0 { z } else { z - 146096 }) / 146097;
let doe = (z - era * 146097) as u32;
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
let y = yoe as i64 + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = doy - (153 * mp + 2) / 5 + 1;
let m = if mp < 10 { mp + 3 } else { mp - 9 };
let y = if m <= 2 { y + 1 } else { y };
if ns == 0 {
format!("{y:04}-{m:02}-{d:02}T{hour:02}:{minute:02}:{second:02}Z")
} else {
let mut frac = format!("{ns:09}");
frac = frac.trim_end_matches('0').to_string();
format!("{y:04}-{m:02}-{d:02}T{hour:02}:{minute:02}:{second:02}.{frac}Z")
}
}
#[cfg(feature = "edge")]
fn format_duration_nanos(nanos: i64) -> String {
if nanos == 0 {
return "0s".to_string();
}
let mut result = String::new();
let mut remaining = if nanos < 0 {
result.push('-');
nanos.unsigned_abs()
} else {
nanos as u64
};
const NANOS_PER_US: u64 = 1_000;
const NANOS_PER_MS: u64 = 1_000_000;
const NANOS_PER_S: u64 = 1_000_000_000;
const NANOS_PER_M: u64 = 60 * NANOS_PER_S;
const NANOS_PER_H: u64 = 60 * NANOS_PER_M;
if remaining >= NANOS_PER_S {
let hours = remaining / NANOS_PER_H;
remaining %= NANOS_PER_H;
let minutes = remaining / NANOS_PER_M;
remaining %= NANOS_PER_M;
let seconds = remaining / NANOS_PER_S;
let sub_second_nanos = remaining % NANOS_PER_S;
if hours > 0 {
result.push_str(&format!("{hours}h"));
}
if minutes > 0 || hours > 0 {
result.push_str(&format!("{minutes}m"));
}
if sub_second_nanos == 0 {
result.push_str(&format!("{seconds}s"));
} else {
let frac = format!("{sub_second_nanos:09}");
let frac = frac.trim_end_matches('0');
result.push_str(&format!("{seconds}.{frac}s"));
}
} else if remaining >= NANOS_PER_MS {
let ms = remaining / NANOS_PER_MS;
let sub = remaining % NANOS_PER_MS;
if sub == 0 {
result.push_str(&format!("{ms}ms"));
} else {
let frac = format!("{sub:06}");
let frac = frac.trim_end_matches('0');
result.push_str(&format!("{ms}.{frac}ms"));
}
} else if remaining >= NANOS_PER_US {
let us = remaining / NANOS_PER_US;
let sub = remaining % NANOS_PER_US;
if sub == 0 {
result.push_str(&format!("{us}µs"));
} else {
let frac = format!("{sub:03}");
let frac = frac.trim_end_matches('0');
result.push_str(&format!("{us}.{frac}µs"));
}
} else {
result.push_str(&format!("{}ns", remaining));
}
result
}
#[cfg(feature = "edge")]
pub trait Store {
fn scan(&self, relation: &str) -> Result<Box<dyn Iterator<Item = Vec<Value>> + '_>>;
fn scan_delta(&self, relation: &str) -> Result<Box<dyn Iterator<Item = Vec<Value>> + '_>>;
fn scan_next_delta(&self, relation: &str) -> Result<Box<dyn Iterator<Item = Vec<Value>> + '_>>;
fn scan_index(
&self,
relation: &str,
col_idx: usize,
key: &Value,
) -> Result<Box<dyn Iterator<Item = Vec<Value>> + '_>>;
fn scan_delta_index(
&self,
relation: &str,
col_idx: usize,
key: &Value,
) -> Result<Box<dyn Iterator<Item = Vec<Value>> + '_>>;
fn insert(&mut self, relation: &str, tuple: Vec<Value>) -> Result<bool>;
fn merge_deltas(&mut self);
fn create_relation(&mut self, relation: &str);
fn retract(&mut self, relation: &str, tuple: &[Value]) -> Result<bool>;
fn clear(&mut self, relation: &str);
fn relation_names(&self) -> Vec<String>;
fn coalesce_temporal(&mut self, _relation: &str) {}
}
#[cfg(feature = "server")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HostVal(pub u32);
#[cfg(feature = "server")]
pub trait Host {
fn scan_start(&mut self, rel_id: i32) -> i32;
fn scan_delta_start(&mut self, rel_id: i32) -> i32;
fn scan_next(&mut self, iter_id: i32) -> i32;
fn merge_deltas(&mut self) -> i32;
fn scan_aggregate_start(&mut self, rel_id: i32, description: Vec<i32>) -> i32;
fn scan_index_start(&mut self, rel_id: i32, col_idx: i32, val: HostVal) -> i32;
fn get_col(&mut self, tuple_ptr: i32, col_idx: i32) -> HostVal;
fn insert_begin(&mut self, rel_id: i32);
fn insert_push(&mut self, val: HostVal);
fn insert_end(&mut self);
fn const_number(&mut self, n: i64) -> HostVal;
fn const_float(&mut self, bits: i64) -> HostVal;
fn const_string(&mut self, id: i32) -> HostVal;
fn const_name(&mut self, id: i32) -> HostVal;
fn const_time(&mut self, nanos: i64) -> HostVal;
fn const_duration(&mut self, nanos: i64) -> HostVal;
fn val_add(&mut self, a: HostVal, b: HostVal) -> HostVal;
fn val_sub(&mut self, a: HostVal, b: HostVal) -> HostVal;
fn val_mul(&mut self, a: HostVal, b: HostVal) -> HostVal;
fn val_div(&mut self, a: HostVal, b: HostVal) -> HostVal;
fn val_sqrt(&mut self, a: HostVal) -> HostVal;
fn val_eq(&mut self, a: HostVal, b: HostVal) -> i32;
fn val_neq(&mut self, a: HostVal, b: HostVal) -> i32;
fn val_lt(&mut self, a: HostVal, b: HostVal) -> i32;
fn val_le(&mut self, a: HostVal, b: HostVal) -> i32;
fn val_gt(&mut self, a: HostVal, b: HostVal) -> i32;
fn val_ge(&mut self, a: HostVal, b: HostVal) -> i32;
fn str_concat(&mut self, a: HostVal, b: HostVal) -> HostVal;
fn str_replace(&mut self, s: HostVal, old: HostVal, new: HostVal, count: HostVal) -> HostVal;
fn val_to_string(&mut self, val: HostVal) -> HostVal;
fn compound_begin(&mut self, kind: i32);
fn compound_push(&mut self, val: HostVal);
fn compound_end(&mut self) -> HostVal;
fn compound_get(&mut self, compound: HostVal, key: HostVal) -> HostVal;
fn compound_len(&mut self, compound: HostVal) -> HostVal;
fn pair_first(&mut self, compound: HostVal) -> HostVal;
fn pair_second(&mut self, compound: HostVal) -> HostVal;
fn debuglog(&mut self, val: HostVal);
fn hash_join_begin(&mut self, _join_id: i32) {
unimplemented!("hash_join_begin");
}
fn hash_join_push(&mut self, _val: HostVal) {
unimplemented!("hash_join_push");
}
fn hash_join_commit_build(&mut self, _join_id: i32, _n_keys: i32) {
unimplemented!("hash_join_commit_build");
}
fn hash_join_probe(&mut self, _join_id: i32) -> i32 {
unimplemented!("hash_join_probe");
}
fn hash_join_end(&mut self, _join_id: i32) {
unimplemented!("hash_join_end");
}
}
pub trait Receiver<'a> {
fn next(&self, item: &'a ast::Atom<'a>) -> Result<()>;
}
impl<'a, Closure: Fn(&'a ast::Atom<'a>) -> Result<()>> Receiver<'a> for Closure {
fn next(&self, item: &'a ast::Atom<'a>) -> Result<()> {
(*self)(item)
}
}
pub trait ReadOnlyFactStore<'a> {
fn arena(&'a self) -> &'a Arena;
fn contains<'src>(&'a self, src: &'src Arena, fact: &'src ast::Atom<'src>) -> Result<bool>;
fn get<'query, R: Receiver<'a>>(
&'a self,
query_sym: ast::PredicateIndex,
query_args: &'query [&'query ast::BaseTerm<'query>],
cb: &R,
) -> Result<()>;
fn predicates(&'a self) -> Vec<ast::PredicateIndex>;
fn estimate_fact_count(&self) -> u32;
}
pub trait FactStore<'a>: ReadOnlyFactStore<'a> {
fn add<'src>(&'a self, src: &'src Arena, fact: &'src ast::Atom<'src>) -> Result<bool>;
fn merge<'src, S>(&'a self, src: &'src Arena, store: &'src S)
where
S: ReadOnlyFactStore<'src>;
}
pub fn get_all_facts<'a, S, R: Receiver<'a>>(store: &'a S, cb: &R) -> Result<()>
where
S: ReadOnlyFactStore<'a> + 'a,
{
let arena = Arena::new_with_global_interner();
let preds = store.predicates();
for pred in preds {
arena.copy_predicate_sym(store.arena(), pred);
store.get(pred, arena.new_query(pred).args, cb)?;
}
Ok(())
}