Skip to main content

corium_query/
boundary.rs

1//! Value conversion between engine values and boundary EDN.
2
3use corium_core::{EntityId, Value};
4use corium_db::Db;
5
6use crate::edn::Edn;
7use crate::exec::UNKNOWN_KEYWORD;
8
9/// Converts an engine value to its boundary EDN representation.
10///
11/// Entity ids surface as longs (as in Datomic); instants, UUIDs, and byte
12/// arrays surface as tagged elements.
13#[must_use]
14pub fn value_to_edn(db: &Db, value: &Value) -> Edn {
15    match value {
16        Value::Bool(v) => Edn::Bool(*v),
17        Value::Long(v) => Edn::Long(*v),
18        Value::Double(v) => Edn::Double(*v),
19        Value::Str(v) => Edn::Str(v.to_string()),
20        Value::Instant(ms) => Edn::Tagged("inst".into(), Box::new(Edn::Long(*ms))),
21        Value::Uuid(v) => Edn::Tagged("uuid".into(), Box::new(Edn::Str(format!("{v:032x}")))),
22        Value::Bytes(bytes) => Edn::Tagged(
23            "bytes".into(),
24            Box::new(Edn::Str(bytes.iter().fold(String::new(), |mut acc, b| {
25                use std::fmt::Write as _;
26                let _ = write!(acc, "{b:02x}");
27                acc
28            }))),
29        ),
30        Value::Keyword(id) => db.interner().resolve(*id).map_or_else(
31            || {
32                Edn::Tagged(
33                    "kw".into(),
34                    Box::new(Edn::Long(i64::try_from(*id).unwrap_or(-1))),
35                )
36            },
37            |keyword| Edn::Keyword(keyword.clone()),
38        ),
39        Value::Ref(e) => Edn::Long(i64::try_from(e.raw()).unwrap_or(i64::MAX)),
40    }
41}
42
43/// Converts a boundary EDN scalar to an engine value.
44///
45/// Keywords are resolved against the database's interner when one is
46/// supplied; unknown keywords convert to a sentinel id that never matches a
47/// stored value. Returns `None` for non-scalar forms.
48#[must_use]
49pub fn edn_to_value(db: Option<&Db>, form: &Edn) -> Option<Value> {
50    match form {
51        Edn::Bool(v) => Some(Value::Bool(*v)),
52        Edn::Long(v) => Some(Value::Long(*v)),
53        Edn::Double(v) => Some(Value::Double(*v)),
54        Edn::Str(v) => Some(Value::Str(v.as_str().into())),
55        Edn::Keyword(k) => Some(Value::Keyword(
56            db.and_then(|db| db.interner().get(k))
57                .unwrap_or(UNKNOWN_KEYWORD),
58        )),
59        Edn::Tagged(tag, value) => match (tag.as_str(), value.as_ref()) {
60            ("eid", Edn::Long(n)) => u64::try_from(*n)
61                .ok()
62                .map(|n| Value::Ref(EntityId::from_raw(n))),
63            ("tx", Edn::Long(t)) => u64::try_from(*t)
64                .ok()
65                .map(|t| Value::Ref(EntityId::new(corium_core::Partition::Tx as u32, t))),
66            ("inst", Edn::Long(ms)) => Some(Value::Instant(*ms)),
67            ("uuid", Edn::Str(hex)) => u128::from_str_radix(hex, 16).ok().map(Value::Uuid),
68            _ => None,
69        },
70        _ => None,
71    }
72}