use corium_query::edn::Edn;
use crate::value::IntoEdn;
#[must_use]
pub fn tempid(name: impl Into<String>) -> Edn {
Edn::Str(name.into())
}
#[must_use]
pub fn lookup(attr: &str, value: impl IntoEdn) -> Edn {
Edn::Vector(vec![Edn::keyword(attr), value.into_edn()])
}
#[must_use]
pub fn eid(id: impl IntoEdn) -> Edn {
Edn::Tagged("eid".into(), Box::new(id.into_edn()))
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct EntityMap {
id: Option<Edn>,
pairs: Vec<(Edn, Edn)>,
}
impl EntityMap {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_id(id: impl IntoEdn) -> Self {
Self {
id: Some(id.into_edn()),
pairs: Vec::new(),
}
}
#[must_use]
pub fn set(mut self, attr: &str, value: impl IntoEdn) -> Self {
self.pairs.push((Edn::keyword(attr), value.into_edn()));
self
}
#[must_use]
pub fn set_many<V: IntoEdn>(mut self, attr: &str, values: impl IntoIterator<Item = V>) -> Self {
let values = values.into_iter().map(IntoEdn::into_edn).collect();
self.pairs.push((Edn::keyword(attr), Edn::Vector(values)));
self
}
fn into_edn(self) -> Edn {
let mut pairs = Vec::with_capacity(self.pairs.len() + 1);
if let Some(id) = self.id {
pairs.push((Edn::keyword("db/id"), id));
}
pairs.extend(self.pairs);
Edn::Map(pairs)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct TxData(Vec<Edn>);
impl TxData {
#[must_use]
pub fn forms(&self) -> &[Edn] {
&self.0
}
#[must_use]
pub fn into_forms(self) -> Vec<Edn> {
self.0
}
}
impl From<Vec<Edn>> for TxData {
fn from(forms: Vec<Edn>) -> Self {
Self(forms)
}
}
#[derive(Clone, Debug, Default)]
pub struct TxBuilder {
forms: Vec<Edn>,
}
impl TxBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn entity(mut self, entity: EntityMap) -> Self {
self.forms.push(entity.into_edn());
self
}
#[must_use]
pub fn add(mut self, e: impl IntoEdn, a: &str, v: impl IntoEdn) -> Self {
self.forms.push(Edn::Vector(vec![
Edn::keyword("db/add"),
e.into_edn(),
Edn::keyword(a),
v.into_edn(),
]));
self
}
#[must_use]
pub fn retract(mut self, e: impl IntoEdn, a: &str, v: impl IntoEdn) -> Self {
self.forms.push(Edn::Vector(vec![
Edn::keyword("db/retract"),
e.into_edn(),
Edn::keyword(a),
v.into_edn(),
]));
self
}
#[must_use]
pub fn cas(mut self, e: impl IntoEdn, a: &str, old: impl IntoEdn, new: impl IntoEdn) -> Self {
self.forms.push(Edn::Vector(vec![
Edn::keyword("db/cas"),
e.into_edn(),
Edn::keyword(a),
old.into_edn(),
new.into_edn(),
]));
self
}
#[must_use]
pub fn retract_entity(mut self, e: impl IntoEdn) -> Self {
self.forms.push(Edn::Vector(vec![
Edn::keyword("db/retractEntity"),
e.into_edn(),
]));
self
}
#[must_use]
pub fn form(mut self, form: Edn) -> Self {
self.forms.push(form);
self
}
#[must_use]
pub fn build(self) -> TxData {
TxData(self.forms)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn map_and_list_forms_render() {
let tx = TxBuilder::new()
.entity(
EntityMap::with_id(tempid("alice"))
.set("person/name", "Alice")
.set_many("person/aliases", ["Al", "Ali"]),
)
.add(1_i64, "person/age", 40_i64)
.retract(
lookup("person/email", "bob@example.com"),
"person/age",
39_i64,
)
.retract_entity(eid(2_i64))
.build();
let forms = tx.into_forms();
assert_eq!(forms.len(), 4);
assert_eq!(
forms[0].to_string(),
"{:db/id \"alice\", :person/name \"Alice\", :person/aliases [\"Al\" \"Ali\"]}"
);
assert_eq!(forms[1].to_string(), "[:db/add 1 :person/age 40]");
assert_eq!(
forms[2].to_string(),
"[:db/retract [:person/email \"bob@example.com\"] :person/age 39]"
);
assert_eq!(forms[3].to_string(), "[:db/retractEntity #eid 2]");
}
}