use std::sync::atomic::{AtomicU64, Ordering};
use serde::{Deserialize, Serialize};
use ulid::Ulid;
use web_time::{SystemTime, UNIX_EPOCH};
static ULID_STATE: AtomicU64 = AtomicU64::new(0);
pub(crate) fn new_ulid_pub() -> Ulid {
new_ulid()
}
fn new_ulid() -> Ulid {
let ms = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0);
let seq = loop {
let cur = ULID_STATE.load(Ordering::Relaxed);
let cur_ms = cur >> 16;
let cur_seq = (cur & 0xFFFF) as u16;
let (next_ms, next_seq) = if ms == cur_ms {
(cur_ms, cur_seq.wrapping_add(1))
} else {
(ms, 0u16)
};
let packed = (next_ms << 16) | u64::from(next_seq);
if ULID_STATE
.compare_exchange_weak(cur, packed, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
break next_seq;
}
};
let mut buf = [0u8; 10];
getrandom::fill(&mut buf)
.expect("taladb: system entropy source failed while generating a ULID");
let rand_lo = u64::from_le_bytes(buf[..8].try_into().unwrap());
let random = (u128::from(seq) << 64) | u128::from(rand_lo);
Ulid::from_parts(ms, random)
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Value {
Null,
Bool(bool),
Int(i64),
Float(f64),
Str(String),
Bytes(Vec<u8>),
Array(Vec<Self>),
Object(Vec<(String, Self)>),
}
impl Value {
pub fn type_name(&self) -> &'static str {
match self {
Self::Null => "null",
Self::Bool(_) => "bool",
Self::Int(_) => "int",
Self::Float(_) => "float",
Self::Str(_) => "string",
Self::Bytes(_) => "bytes",
Self::Array(_) => "array",
Self::Object(_) => "object",
}
}
pub fn as_bool(&self) -> Option<bool> {
if let Self::Bool(b) = self {
Some(*b)
} else {
None
}
}
pub fn as_int(&self) -> Option<i64> {
if let Self::Int(n) = self {
Some(*n)
} else {
None
}
}
pub fn as_float(&self) -> Option<f64> {
if let Self::Float(f) = self {
Some(*f)
} else {
None
}
}
pub fn as_str(&self) -> Option<&str> {
if let Self::Str(s) = self {
Some(s.as_str())
} else {
None
}
}
pub fn as_bytes(&self) -> Option<&[u8]> {
if let Self::Bytes(b) = self {
Some(b.as_slice())
} else {
None
}
}
pub fn as_array(&self) -> Option<&[Self]> {
if let Self::Array(a) = self {
Some(a.as_slice())
} else {
None
}
}
pub fn as_object(&self) -> Option<&[(String, Self)]> {
if let Self::Object(o) = self {
Some(o.as_slice())
} else {
None
}
}
pub fn get(&self, key: &str) -> Option<&Self> {
self.as_object()?
.iter()
.find(|(k, _)| k == key)
.map(|(_, v)| v)
}
pub fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
pub fn partial_cmp_numeric(&self, other: &Self) -> Option<std::cmp::Ordering> {
match (self, other) {
(Self::Int(a), Self::Int(b)) => a.partial_cmp(b),
(Self::Float(a), Self::Float(b)) => a.partial_cmp(b),
(Self::Int(a), Self::Float(b)) => cmp_i64_f64(*a, *b),
(Self::Float(a), Self::Int(b)) => cmp_i64_f64(*b, *a).map(std::cmp::Ordering::reverse),
(Self::Str(a), Self::Str(b)) => a.partial_cmp(b),
(Self::Bool(a), Self::Bool(b)) => a.partial_cmp(b),
_ => None,
}
}
}
macro_rules! value_from {
($($ty:ty => $variant:ident $(as $cast:ty)?),* $(,)?) => {
$(
impl From<$ty> for Value {
fn from(v: $ty) -> Self {
Value::$variant(v $(as $cast)?)
}
}
)*
};
}
value_from! {
bool => Bool,
i8 => Int as i64,
i16 => Int as i64,
i32 => Int as i64,
i64 => Int,
u8 => Int as i64,
u16 => Int as i64,
u32 => Int as i64,
f32 => Float as f64,
f64 => Float,
String => Str,
Vec<u8> => Bytes,
Vec<Value> => Array,
Vec<(String, Value)> => Object,
}
impl From<&str> for Value {
fn from(v: &str) -> Self {
Self::Str(v.to_owned())
}
}
impl From<&[u8]> for Value {
fn from(v: &[u8]) -> Self {
Self::Bytes(v.to_vec())
}
}
impl<T: Into<Self>> From<Option<T>> for Value {
fn from(v: Option<T>) -> Self {
v.map_or(Self::Null, Into::into)
}
}
impl<T: Into<Self>> FromIterator<T> for Value {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Self::Array(iter.into_iter().map(Into::into).collect())
}
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Null => f.write_str("null"),
Self::Bool(b) => write!(f, "{b}"),
Self::Int(n) => write!(f, "{n}"),
Self::Float(x) => write!(f, "{x}"),
Self::Str(s) => f.write_str(s),
Self::Bytes(b) => write!(f, "<{} bytes>", b.len()),
Self::Array(items) => {
f.write_str("[")?;
for (i, item) in items.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
write!(f, "{item}")?;
}
f.write_str("]")
}
Self::Object(fields) => {
f.write_str("{")?;
for (i, (key, value)) in fields.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
write!(f, "{key}: {value}")?;
}
f.write_str("}")
}
}
}
}
fn cmp_i64_f64(a: i64, b: f64) -> Option<std::cmp::Ordering> {
use std::cmp::Ordering;
if b.is_nan() {
return None;
}
const TWO_POW_63: f64 = 9_223_372_036_854_775_808.0;
if b >= TWO_POW_63 {
return Some(Ordering::Less);
}
if b < -TWO_POW_63 {
return Some(Ordering::Greater);
}
let truncated = b.trunc();
let whole = truncated as i64;
match a.cmp(&whole) {
Ordering::Equal => Some(if b > truncated {
Ordering::Less
} else if b < truncated {
Ordering::Greater
} else {
Ordering::Equal
}),
ord => Some(ord),
}
}
const FNV1A128_OFFSET_BASIS: u128 = 0x6c62_272e_07bb_0142_62b8_2175_6295_c58d;
const FNV1A128_PRIME: u128 = 0x0000_0000_0100_0000_0000_0000_0000_013b;
pub fn derive_doc_id(collection: &str, key: &str) -> Ulid {
const SEPARATOR: [u8; 1] = [0u8];
let mut hash = FNV1A128_OFFSET_BASIS;
for &byte in collection
.as_bytes()
.iter()
.chain(SEPARATOR.iter())
.chain(key.as_bytes().iter())
{
hash ^= u128::from(byte);
hash = hash.wrapping_mul(FNV1A128_PRIME);
}
Ulid::from_bytes(hash.to_be_bytes())
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Document {
pub id: Ulid,
pub fields: Vec<(String, Value)>,
}
impl Document {
pub fn new(fields: Vec<(String, Value)>) -> Self {
Self {
id: new_ulid(),
fields: dedupe_fields(fields),
}
}
pub fn with_id(id: Ulid, fields: Vec<(String, Value)>) -> Self {
Self {
id,
fields: dedupe_fields(fields),
}
}
pub fn get(&self, key: &str) -> Option<&Value> {
if let Some(dot) = key.find('.') {
let (head, tail) = (&key[..dot], &key[dot + 1..]);
let parent = self
.fields
.iter()
.find(|(k, _)| k == head)
.map(|(_, v)| v)?;
value_get_nested(parent, tail)
} else {
self.fields.iter().find(|(k, _)| k == key).map(|(_, v)| v)
}
}
pub fn set(&mut self, key: impl Into<String>, value: Value) {
let key = key.into();
if let Some(entry) = self.fields.iter_mut().find(|(k, _)| k == &key) {
entry.1 = value;
} else {
self.fields.push((key, value));
}
}
pub fn remove(&mut self, key: &str) -> Option<Value> {
if let Some(pos) = self.fields.iter().position(|(k, _)| k == key) {
Some(self.fields.remove(pos).1)
} else {
None
}
}
pub fn contains_key(&self, key: &str) -> bool {
self.get(key).is_some()
}
}
fn dedupe_fields(fields: Vec<(String, Value)>) -> Vec<(String, Value)> {
let has_dup = fields
.iter()
.enumerate()
.any(|(i, (k, _))| fields[..i].iter().any(|(k2, _)| k2 == k));
if !has_dup {
return fields;
}
let mut out: Vec<(String, Value)> = Vec::with_capacity(fields.len());
for (k, v) in fields {
if !out.iter().any(|(k2, _)| k2 == &k) {
out.push((k, v));
}
}
out
}
pub fn value_get_nested<'a>(val: &'a Value, path: &str) -> Option<&'a Value> {
value_get_nested_depth(val, path, 0)
}
fn value_get_nested_depth<'a>(val: &'a Value, path: &str, depth: u8) -> Option<&'a Value> {
if depth >= 64 {
return None;
}
if let Some(dot) = path.find('.') {
let (head, tail) = (&path[..dot], &path[dot + 1..]);
match val {
Value::Object(fields) => {
let child = fields.iter().find(|(k, _)| k == head).map(|(_, v)| v)?;
value_get_nested_depth(child, tail, depth + 1)
}
_ => None,
}
} else {
match val {
Value::Object(fields) => fields.iter().find(|(k, _)| k == path).map(|(_, v)| v),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cmp::Ordering;
#[test]
fn int_float_comparison_is_exact_beyond_2_pow_53() {
let big = Value::Int(9_007_199_254_740_993);
let as_float = Value::Float(9_007_199_254_740_992.0);
assert_eq!(big.partial_cmp_numeric(&as_float), Some(Ordering::Greater));
assert_eq!(as_float.partial_cmp_numeric(&big), Some(Ordering::Less));
let small = Value::Int(-9_007_199_254_740_993);
let small_float = Value::Float(-9_007_199_254_740_992.0);
assert_eq!(
small.partial_cmp_numeric(&small_float),
Some(Ordering::Less)
);
assert_eq!(
small_float.partial_cmp_numeric(&small),
Some(Ordering::Greater)
);
}
#[test]
fn int_float_comparison_handles_out_of_range_floats() {
let max = Value::Int(i64::MAX);
let min = Value::Int(i64::MIN);
assert_eq!(
max.partial_cmp_numeric(&Value::Float(1e300)),
Some(Ordering::Less)
);
assert_eq!(
min.partial_cmp_numeric(&Value::Float(-1e300)),
Some(Ordering::Greater)
);
assert_eq!(
max.partial_cmp_numeric(&Value::Float(f64::INFINITY)),
Some(Ordering::Less)
);
assert_eq!(
min.partial_cmp_numeric(&Value::Float(f64::NEG_INFINITY)),
Some(Ordering::Greater)
);
}
#[test]
fn int_float_comparison_handles_fractions_and_nan() {
assert_eq!(
Value::Int(3).partial_cmp_numeric(&Value::Float(3.5)),
Some(Ordering::Less)
);
assert_eq!(
Value::Int(-3).partial_cmp_numeric(&Value::Float(-3.5)),
Some(Ordering::Greater)
);
assert_eq!(
Value::Int(3).partial_cmp_numeric(&Value::Float(3.0)),
Some(Ordering::Equal)
);
assert_eq!(
Value::Int(1).partial_cmp_numeric(&Value::Float(f64::NAN)),
None
);
assert_eq!(
Value::Float(f64::NAN).partial_cmp_numeric(&Value::Int(1)),
None
);
}
#[test]
fn value_from_impls_cover_the_rust_types_that_map_onto_it() {
assert_eq!(Value::from(true), Value::Bool(true));
assert_eq!(Value::from(42i32), Value::Int(42));
assert_eq!(Value::from(42u8), Value::Int(42));
assert_eq!(Value::from(1.5f32), Value::Float(1.5));
assert_eq!(Value::from("hi"), Value::Str("hi".into()));
assert_eq!(Value::from(String::from("hi")), Value::Str("hi".into()));
assert_eq!(Value::from(vec![1u8, 2]), Value::Bytes(vec![1, 2]));
assert_eq!(Value::from(None::<i64>), Value::Null);
assert_eq!(Value::from(Some(7i64)), Value::Int(7));
let arr: Value = [1i64, 2, 3].into_iter().collect();
assert_eq!(
arr,
Value::Array(vec![Value::Int(1), Value::Int(2), Value::Int(3)])
);
}
#[test]
fn value_accessors_cover_every_variant() {
assert_eq!(Value::Bool(true).as_bool(), Some(true));
assert_eq!(Value::Int(1).as_int(), Some(1));
assert_eq!(Value::Float(1.5).as_float(), Some(1.5));
assert_eq!(Value::Str("s".into()).as_str(), Some("s"));
assert_eq!(Value::Bytes(vec![1, 2]).as_bytes(), Some(&[1u8, 2][..]));
assert_eq!(
Value::Array(vec![Value::Int(1)]).as_array().unwrap().len(),
1
);
assert!(Value::Null.is_null());
let obj = Value::Object(vec![("a".into(), Value::Int(1))]);
assert_eq!(obj.as_object().unwrap().len(), 1);
assert_eq!(obj.get("a"), Some(&Value::Int(1)));
assert_eq!(obj.get("missing"), None);
assert_eq!(Value::Int(1).as_str(), None);
assert_eq!(Value::Str("1".into()).as_int(), None);
assert_eq!(Value::Int(1).get("a"), None);
}
#[test]
fn value_display_is_readable_and_hides_byte_contents() {
assert_eq!(Value::Null.to_string(), "null");
assert_eq!(Value::Int(1887).to_string(), "1887");
assert_eq!(Value::Str("Rizal".into()).to_string(), "Rizal");
assert_eq!(Value::Bytes(vec![0; 500]).to_string(), "<500 bytes>");
assert_eq!(
Value::Array(vec![Value::Int(1), Value::Str("a".into())]).to_string(),
"[1, a]"
);
assert_eq!(
Value::Object(vec![
("a".into(), Value::Int(1)),
("b".into(), Value::Bool(false))
])
.to_string(),
"{a: 1, b: false}"
);
}
#[test]
fn round_trip_postcard() {
let doc = Document::new(vec![
("name".into(), Value::Str("Alice".into())),
("age".into(), Value::Int(30)),
("active".into(), Value::Bool(true)),
("score".into(), Value::Float(9.5)),
(
"tags".into(),
Value::Array(vec![Value::Str("rust".into()), Value::Str("db".into())]),
),
]);
let bytes = postcard::to_allocvec(&doc).unwrap();
let decoded: Document = postcard::from_bytes(&bytes).unwrap();
assert_eq!(doc.id, decoded.id);
assert_eq!(doc.fields, decoded.fields);
}
#[test]
fn document_get_set_remove() {
let mut doc = Document::new(vec![("x".into(), Value::Int(1))]);
assert_eq!(doc.get("x"), Some(&Value::Int(1)));
doc.set("x", Value::Int(2));
assert_eq!(doc.get("x"), Some(&Value::Int(2)));
doc.set("y", Value::Bool(true));
assert_eq!(doc.get("y"), Some(&Value::Bool(true)));
doc.remove("x");
assert_eq!(doc.get("x"), None);
}
#[test]
fn derive_doc_id_is_deterministic() {
assert_eq!(
derive_doc_id("products", "sku-123"),
derive_doc_id("products", "sku-123")
);
}
#[test]
fn derive_doc_id_separates_collections() {
assert_ne!(derive_doc_id("products", "1"), derive_doc_id("orders", "1"));
}
#[test]
fn derive_doc_id_separator_prevents_boundary_collision() {
assert_ne!(derive_doc_id("ab", "c"), derive_doc_id("a", "bc"));
}
#[test]
fn derive_doc_id_cross_language_vectors() {
let vectors: &[(&str, &str, &str)] = &[
("products", "sku-123", "56GC678DQYWW1Z98HPYJ90WVKH"),
("products", "1", "7Z6Y6H8NG96ZGN4PJVDP18CSY2"),
("orders", "1", "5VYD63GDV5KCDXV2A0SYRWSKVZ"),
("", "", "6J535PJ40THJQQH49BE174M53Z"),
("products", "sku-ñ-💡", "5NZ0PGNM2CF0BTHN0AAX8CWPAA"),
];
for (collection, key, expected) in vectors {
assert_eq!(
derive_doc_id(collection, key).to_string(),
*expected,
"derive_doc_id({collection:?}, {key:?}) drifted — \
the TypeScript implementation must match these exactly"
);
}
}
}