1use corium_query::edn::Edn;
22
23use crate::value::IntoEdn;
24
25#[must_use]
28pub fn tempid(name: impl Into<String>) -> Edn {
29 Edn::Str(name.into())
30}
31
32#[must_use]
35pub fn lookup(attr: &str, value: impl IntoEdn) -> Edn {
36 Edn::Vector(vec![Edn::keyword(attr), value.into_edn()])
37}
38
39#[must_use]
42pub fn eid(id: impl IntoEdn) -> Edn {
43 Edn::Tagged("eid".into(), Box::new(id.into_edn()))
44}
45
46#[derive(Clone, Debug, Default, Eq, PartialEq)]
49pub struct EntityMap {
50 id: Option<Edn>,
51 pairs: Vec<(Edn, Edn)>,
52}
53
54impl EntityMap {
55 #[must_use]
57 pub fn new() -> Self {
58 Self::default()
59 }
60
61 #[must_use]
64 pub fn with_id(id: impl IntoEdn) -> Self {
65 Self {
66 id: Some(id.into_edn()),
67 pairs: Vec::new(),
68 }
69 }
70
71 #[must_use]
73 pub fn set(mut self, attr: &str, value: impl IntoEdn) -> Self {
74 self.pairs.push((Edn::keyword(attr), value.into_edn()));
75 self
76 }
77
78 #[must_use]
80 pub fn set_many<V: IntoEdn>(mut self, attr: &str, values: impl IntoIterator<Item = V>) -> Self {
81 let values = values.into_iter().map(IntoEdn::into_edn).collect();
82 self.pairs.push((Edn::keyword(attr), Edn::Vector(values)));
83 self
84 }
85
86 fn into_edn(self) -> Edn {
87 let mut pairs = Vec::with_capacity(self.pairs.len() + 1);
88 if let Some(id) = self.id {
89 pairs.push((Edn::keyword("db/id"), id));
90 }
91 pairs.extend(self.pairs);
92 Edn::Map(pairs)
93 }
94}
95
96#[derive(Clone, Debug, Default, Eq, PartialEq)]
99pub struct TxData(Vec<Edn>);
100
101impl TxData {
102 #[must_use]
104 pub fn forms(&self) -> &[Edn] {
105 &self.0
106 }
107
108 #[must_use]
110 pub fn into_forms(self) -> Vec<Edn> {
111 self.0
112 }
113}
114
115impl From<Vec<Edn>> for TxData {
116 fn from(forms: Vec<Edn>) -> Self {
117 Self(forms)
118 }
119}
120
121#[derive(Clone, Debug, Default)]
123pub struct TxBuilder {
124 forms: Vec<Edn>,
125}
126
127impl TxBuilder {
128 #[must_use]
130 pub fn new() -> Self {
131 Self::default()
132 }
133
134 #[must_use]
136 pub fn entity(mut self, entity: EntityMap) -> Self {
137 self.forms.push(entity.into_edn());
138 self
139 }
140
141 #[must_use]
143 pub fn add(mut self, e: impl IntoEdn, a: &str, v: impl IntoEdn) -> Self {
144 self.forms.push(Edn::Vector(vec![
145 Edn::keyword("db/add"),
146 e.into_edn(),
147 Edn::keyword(a),
148 v.into_edn(),
149 ]));
150 self
151 }
152
153 #[must_use]
155 pub fn retract(mut self, e: impl IntoEdn, a: &str, v: impl IntoEdn) -> Self {
156 self.forms.push(Edn::Vector(vec![
157 Edn::keyword("db/retract"),
158 e.into_edn(),
159 Edn::keyword(a),
160 v.into_edn(),
161 ]));
162 self
163 }
164
165 #[must_use]
167 pub fn cas(mut self, e: impl IntoEdn, a: &str, old: impl IntoEdn, new: impl IntoEdn) -> Self {
168 self.forms.push(Edn::Vector(vec![
169 Edn::keyword("db/cas"),
170 e.into_edn(),
171 Edn::keyword(a),
172 old.into_edn(),
173 new.into_edn(),
174 ]));
175 self
176 }
177
178 #[must_use]
180 pub fn retract_entity(mut self, e: impl IntoEdn) -> Self {
181 self.forms.push(Edn::Vector(vec![
182 Edn::keyword("db/retractEntity"),
183 e.into_edn(),
184 ]));
185 self
186 }
187
188 #[must_use]
191 pub fn form(mut self, form: Edn) -> Self {
192 self.forms.push(form);
193 self
194 }
195
196 #[must_use]
198 pub fn build(self) -> TxData {
199 TxData(self.forms)
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 #[test]
208 fn map_and_list_forms_render() {
209 let tx = TxBuilder::new()
210 .entity(
211 EntityMap::with_id(tempid("alice"))
212 .set("person/name", "Alice")
213 .set_many("person/aliases", ["Al", "Ali"]),
214 )
215 .add(1_i64, "person/age", 40_i64)
216 .retract(
217 lookup("person/email", "bob@example.com"),
218 "person/age",
219 39_i64,
220 )
221 .retract_entity(eid(2_i64))
222 .build();
223 let forms = tx.into_forms();
224 assert_eq!(forms.len(), 4);
225 assert_eq!(
226 forms[0].to_string(),
227 "{:db/id \"alice\", :person/name \"Alice\", :person/aliases [\"Al\" \"Ali\"]}"
228 );
229 assert_eq!(forms[1].to_string(), "[:db/add 1 :person/age 40]");
230 assert_eq!(
231 forms[2].to_string(),
232 "[:db/retract [:person/email \"bob@example.com\"] :person/age 39]"
233 );
234 assert_eq!(forms[3].to_string(), "[:db/retractEntity #eid 2]");
235 }
236}