1use crate::filter::FilterValue;
50use serde::{Deserialize, Serialize};
51use std::collections::HashMap;
52
53pub trait CreateData: Send + Sync {
55 fn into_fields(self) -> HashMap<String, FieldValue>;
57
58 fn model_name() -> &'static str;
60}
61
62pub trait UpdateData: Send + Sync {
64 fn into_fields(self) -> HashMap<String, FieldValue>;
66
67 fn model_name() -> &'static str;
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
73#[serde(untagged)]
74pub enum FieldValue {
75 Null,
77 Bool(bool),
79 Int(i64),
81 Float(f64),
83 String(String),
85 Json(serde_json::Value),
87 Bytes(Vec<u8>),
89 DateTime(String),
91 Uuid(String),
93 Array(Vec<FieldValue>),
95 Nested(Box<DataBuilder>),
97 Connect(ConnectData),
99 Disconnect,
101 Default,
103 Increment(i64),
105 Decrement(i64),
107 Multiply(f64),
109 Divide(f64),
111 Push(Box<FieldValue>),
113 Unset,
115}
116
117impl FieldValue {
118 pub fn to_filter_value(&self) -> Option<FilterValue> {
120 match self {
121 Self::Null => Some(FilterValue::Null),
122 Self::Bool(b) => Some(FilterValue::Bool(*b)),
123 Self::Int(i) => Some(FilterValue::Int(*i)),
124 Self::Float(f) => Some(FilterValue::Float(*f)),
125 Self::String(s) => Some(FilterValue::String(s.clone())),
126 Self::Json(j) => Some(FilterValue::Json(j.clone())),
127 Self::DateTime(s) => Some(FilterValue::String(s.clone())),
128 Self::Uuid(s) => Some(FilterValue::String(s.clone())),
129 _ => None,
130 }
131 }
132}
133
134impl From<bool> for FieldValue {
136 fn from(v: bool) -> Self {
137 Self::Bool(v)
138 }
139}
140
141impl From<i32> for FieldValue {
142 fn from(v: i32) -> Self {
143 Self::Int(v as i64)
144 }
145}
146
147impl From<i64> for FieldValue {
148 fn from(v: i64) -> Self {
149 Self::Int(v)
150 }
151}
152
153impl From<f32> for FieldValue {
154 fn from(v: f32) -> Self {
155 Self::Float(v as f64)
156 }
157}
158
159impl From<f64> for FieldValue {
160 fn from(v: f64) -> Self {
161 Self::Float(v)
162 }
163}
164
165impl From<String> for FieldValue {
166 fn from(v: String) -> Self {
167 Self::String(v)
168 }
169}
170
171impl From<&str> for FieldValue {
172 fn from(v: &str) -> Self {
173 Self::String(v.to_string())
174 }
175}
176
177impl From<serde_json::Value> for FieldValue {
178 fn from(v: serde_json::Value) -> Self {
179 Self::Json(v)
180 }
181}
182
183impl<T: Into<FieldValue>> From<Option<T>> for FieldValue {
184 fn from(v: Option<T>) -> Self {
185 match v {
186 Some(val) => val.into(),
187 None => Self::Null,
188 }
189 }
190}
191
192impl<T: Into<FieldValue>> From<Vec<T>> for FieldValue {
193 fn from(v: Vec<T>) -> Self {
194 Self::Array(v.into_iter().map(Into::into).collect())
195 }
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct ConnectData {
201 pub field: String,
203 pub value: Box<FieldValue>,
205}
206
207impl ConnectData {
208 pub fn id(id: impl Into<FieldValue>) -> Self {
210 Self {
211 field: "id".to_string(),
212 value: Box::new(id.into()),
213 }
214 }
215
216 pub fn by(field: impl Into<String>, value: impl Into<FieldValue>) -> Self {
218 Self {
219 field: field.into(),
220 value: Box::new(value.into()),
221 }
222 }
223}
224
225#[derive(Debug, Clone, Default, Serialize, Deserialize)]
230pub struct DataBuilder {
231 fields: HashMap<String, FieldValue>,
232}
233
234impl DataBuilder {
235 pub fn new() -> Self {
237 Self::default()
238 }
239
240 pub fn set(mut self, field: impl Into<String>, value: impl Into<FieldValue>) -> Self {
242 self.fields.insert(field.into(), value.into());
243 self
244 }
245
246 pub fn set_null(mut self, field: impl Into<String>) -> Self {
248 self.fields.insert(field.into(), FieldValue::Null);
249 self
250 }
251
252 pub fn set_default(mut self, field: impl Into<String>) -> Self {
254 self.fields.insert(field.into(), FieldValue::Default);
255 self
256 }
257
258 pub fn unset(mut self, field: impl Into<String>) -> Self {
260 self.fields.insert(field.into(), FieldValue::Unset);
261 self
262 }
263
264 pub fn increment(mut self, field: impl Into<String>, by: i64) -> Self {
266 self.fields.insert(field.into(), FieldValue::Increment(by));
267 self
268 }
269
270 pub fn decrement(mut self, field: impl Into<String>, by: i64) -> Self {
272 self.fields.insert(field.into(), FieldValue::Decrement(by));
273 self
274 }
275
276 pub fn multiply(mut self, field: impl Into<String>, by: f64) -> Self {
278 self.fields.insert(field.into(), FieldValue::Multiply(by));
279 self
280 }
281
282 pub fn divide(mut self, field: impl Into<String>, by: f64) -> Self {
284 self.fields.insert(field.into(), FieldValue::Divide(by));
285 self
286 }
287
288 pub fn push(mut self, field: impl Into<String>, value: impl Into<FieldValue>) -> Self {
290 self.fields.insert(field.into(), FieldValue::Push(Box::new(value.into())));
291 self
292 }
293
294 pub fn connect(mut self, relation: impl Into<String>, id: impl Into<FieldValue>) -> Self {
296 self.fields.insert(relation.into(), FieldValue::Connect(ConnectData::id(id)));
297 self
298 }
299
300 pub fn connect_by(
302 mut self,
303 relation: impl Into<String>,
304 field: impl Into<String>,
305 value: impl Into<FieldValue>,
306 ) -> Self {
307 self.fields.insert(
308 relation.into(),
309 FieldValue::Connect(ConnectData::by(field, value)),
310 );
311 self
312 }
313
314 pub fn disconnect(mut self, relation: impl Into<String>) -> Self {
316 self.fields.insert(relation.into(), FieldValue::Disconnect);
317 self
318 }
319
320 pub fn create_nested(mut self, relation: impl Into<String>, data: DataBuilder) -> Self {
322 self.fields.insert(relation.into(), FieldValue::Nested(Box::new(data)));
323 self
324 }
325
326 pub fn into_fields(self) -> HashMap<String, FieldValue> {
328 self.fields
329 }
330
331 pub fn has(&self, field: &str) -> bool {
333 self.fields.contains_key(field)
334 }
335
336 pub fn get(&self, field: &str) -> Option<&FieldValue> {
338 self.fields.get(field)
339 }
340
341 pub fn len(&self) -> usize {
343 self.fields.len()
344 }
345
346 pub fn is_empty(&self) -> bool {
348 self.fields.is_empty()
349 }
350}
351
352pub trait IntoData {
354 fn into_data(self) -> DataBuilder;
356}
357
358impl IntoData for DataBuilder {
359 fn into_data(self) -> DataBuilder {
360 self
361 }
362}
363
364impl IntoData for HashMap<String, FieldValue> {
365 fn into_data(self) -> DataBuilder {
366 DataBuilder { fields: self }
367 }
368}
369
370impl IntoData for serde_json::Value {
371 fn into_data(self) -> DataBuilder {
372 match self {
373 serde_json::Value::Object(map) => {
374 let fields = map
375 .into_iter()
376 .map(|(k, v)| (k, json_to_field_value(v)))
377 .collect();
378 DataBuilder { fields }
379 }
380 _ => DataBuilder::new(),
381 }
382 }
383}
384
385fn json_to_field_value(value: serde_json::Value) -> FieldValue {
386 match value {
387 serde_json::Value::Null => FieldValue::Null,
388 serde_json::Value::Bool(b) => FieldValue::Bool(b),
389 serde_json::Value::Number(n) => {
390 if let Some(i) = n.as_i64() {
391 FieldValue::Int(i)
392 } else if let Some(f) = n.as_f64() {
393 FieldValue::Float(f)
394 } else {
395 FieldValue::Json(serde_json::Value::Number(n))
396 }
397 }
398 serde_json::Value::String(s) => FieldValue::String(s),
399 serde_json::Value::Array(arr) => {
400 FieldValue::Array(arr.into_iter().map(json_to_field_value).collect())
401 }
402 serde_json::Value::Object(_) => FieldValue::Json(value),
403 }
404}
405
406#[macro_export]
435macro_rules! data {
436 () => {
438 $crate::data::DataBuilder::new()
439 };
440
441 ($($field:ident : $value:expr),* $(,)?) => {{
443 let mut builder = $crate::data::DataBuilder::new();
444 $(
445 builder = builder.set(stringify!($field), $value);
446 )*
447 builder
448 }};
449}
450
451#[macro_export]
453macro_rules! connect {
454 (id: $id:expr) => {
455 $crate::data::FieldValue::Connect($crate::data::ConnectData::id($id))
456 };
457 ($field:ident : $value:expr) => {
458 $crate::data::FieldValue::Connect($crate::data::ConnectData::by(
459 stringify!($field),
460 $value,
461 ))
462 };
463}
464
465#[macro_export]
467macro_rules! increment {
468 ($value:expr) => {
469 $crate::data::FieldValue::Increment($value)
470 };
471}
472
473#[macro_export]
475macro_rules! decrement {
476 ($value:expr) => {
477 $crate::data::FieldValue::Decrement($value)
478 };
479}
480
481#[derive(Debug, Clone, Default)]
483pub struct BatchCreate<T> {
484 items: Vec<T>,
485 skip_duplicates: bool,
486}
487
488impl<T> BatchCreate<T> {
489 pub fn new(items: Vec<T>) -> Self {
491 Self {
492 items,
493 skip_duplicates: false,
494 }
495 }
496
497 pub fn skip_duplicates(mut self) -> Self {
499 self.skip_duplicates = true;
500 self
501 }
502
503 pub fn into_items(self) -> Vec<T> {
505 self.items
506 }
507
508 pub fn should_skip_duplicates(&self) -> bool {
510 self.skip_duplicates
511 }
512
513 pub fn len(&self) -> usize {
515 self.items.len()
516 }
517
518 pub fn is_empty(&self) -> bool {
520 self.items.is_empty()
521 }
522}
523
524impl<T> From<Vec<T>> for BatchCreate<T> {
525 fn from(items: Vec<T>) -> Self {
526 Self::new(items)
527 }
528}
529
530pub trait TypedCreateBuilder: Sized {
550 type Output: CreateData;
552
553 fn build(self) -> Self::Output;
555}
556
557pub trait TypedUpdateBuilder: Sized {
559 type Output: UpdateData;
561
562 fn build(self) -> Self::Output;
564}
565
566#[cfg(test)]
567mod tests {
568 use super::*;
569
570 #[test]
571 fn test_data_builder_basic() {
572 let data = DataBuilder::new()
573 .set("name", "Bob")
574 .set("age", 30)
575 .set("active", true);
576
577 assert_eq!(data.len(), 3);
578 assert!(data.has("name"));
579 assert!(data.has("age"));
580 assert!(data.has("active"));
581 }
582
583 #[test]
584 fn test_data_builder_null_and_default() {
585 let data = DataBuilder::new()
586 .set_null("deleted_at")
587 .set_default("created_at");
588
589 assert!(matches!(data.get("deleted_at"), Some(FieldValue::Null)));
590 assert!(matches!(data.get("created_at"), Some(FieldValue::Default)));
591 }
592
593 #[test]
594 fn test_data_builder_numeric_operations() {
595 let data = DataBuilder::new()
596 .increment("views", 1)
597 .decrement("stock", 5)
598 .multiply("price", 1.1)
599 .divide("score", 2.0);
600
601 assert!(matches!(data.get("views"), Some(FieldValue::Increment(1))));
602 assert!(matches!(data.get("stock"), Some(FieldValue::Decrement(5))));
603 }
604
605 #[test]
606 fn test_data_builder_connect() {
607 let data = DataBuilder::new()
608 .connect("author", 1)
609 .connect_by("category", "slug", "tech");
610
611 assert!(matches!(data.get("author"), Some(FieldValue::Connect(_))));
612 assert!(matches!(data.get("category"), Some(FieldValue::Connect(_))));
613 }
614
615 #[test]
616 fn test_data_macro() {
617 let data = data! {
618 name: "Bob",
619 email: "bob@example.com",
620 age: 30,
621 };
622
623 assert_eq!(data.len(), 3);
624 assert!(matches!(data.get("name"), Some(FieldValue::String(s)) if s == "Bob"));
625 }
626
627 #[test]
628 fn test_field_value_conversions() {
629 let _: FieldValue = true.into();
630 let _: FieldValue = 42_i32.into();
631 let _: FieldValue = 42_i64.into();
632 let _: FieldValue = 3.14_f64.into();
633 let _: FieldValue = "hello".into();
634 let _: FieldValue = String::from("hello").into();
635 let _: FieldValue = Some("optional").into();
636 let _: FieldValue = None::<String>.into();
637 let _: FieldValue = vec!["a", "b"].into();
638 }
639
640 #[test]
641 fn test_batch_create() {
642 let batch: BatchCreate<DataBuilder> = vec![
643 data! { name: "Alice" },
644 data! { name: "Bob" },
645 ].into();
646
647 assert_eq!(batch.len(), 2);
648 assert!(!batch.should_skip_duplicates());
649
650 let batch = batch.skip_duplicates();
651 assert!(batch.should_skip_duplicates());
652 }
653
654 #[test]
655 fn test_json_to_data() {
656 let json = serde_json::json!({
657 "name": "Bob",
658 "age": 30,
659 "active": true,
660 "tags": ["a", "b"]
661 });
662
663 let data: DataBuilder = json.into_data();
664 assert_eq!(data.len(), 4);
665 }
666
667 #[test]
668 fn test_connect_data() {
669 let by_id = ConnectData::id(1);
670 assert_eq!(by_id.field, "id");
671
672 let by_email = ConnectData::by("email", "bob@example.com");
673 assert_eq!(by_email.field, "email");
674 }
675}
676
677