1use crate::delete::DeleteBuilder;
7use crate::escape_all;
8use crate::field_mapper::{FieldMapperFunc, default_field_mapper};
9use crate::flavor::Flavor;
10use crate::insert::InsertBuilder;
11use crate::select::SelectBuilder;
12use crate::select_cols;
13use crate::update::UpdateBuilder;
14use std::any::Any;
15use std::collections::HashSet;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum FieldOpt {
19 WithQuote,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct FieldMeta {
24 pub rust: &'static str,
26 pub orig: &'static str,
28 pub db: &'static str,
30 pub as_: Option<&'static str>,
32 pub tags: &'static [&'static str],
34 pub omitempty_tags: &'static [&'static str],
36 pub with_quote: bool,
37}
38
39impl FieldMeta {
40 pub fn name_for_select(&self, flavor: Flavor, alias: &str) -> String {
41 let base = if self.with_quote {
42 flavor.quote(alias)
43 } else {
44 alias.to_string()
45 };
46 if let Some(as_) = self.as_ {
47 format!("{base} AS {as_}")
48 } else {
49 base
50 }
51 }
52}
53
54fn is_ignored(fm: &FieldMeta) -> bool {
55 fm.db == "-"
57}
58
59pub trait SqlStruct: Sized {
61 const FIELDS: &'static [FieldMeta];
62
63 fn values(&self) -> Vec<crate::modifiers::Arg>;
65
66 fn is_empty_field(&self, rust_field: &'static str) -> bool;
68
69 fn addr_cells<'a>(
73 &'a mut self,
74 rust_fields: &[&'static str],
75 ) -> Option<Vec<crate::scan::ScanCell<'a>>>;
76}
77
78pub trait IsEmpty {
80 fn is_empty_value(&self) -> bool;
81}
82
83impl IsEmpty for String {
84 fn is_empty_value(&self) -> bool {
85 self.is_empty()
86 }
87}
88
89impl IsEmpty for &str {
90 fn is_empty_value(&self) -> bool {
91 self.is_empty()
92 }
93}
94
95impl IsEmpty for bool {
96 fn is_empty_value(&self) -> bool {
97 !*self
98 }
99}
100
101macro_rules! empty_num {
102 ($($t:ty),+ $(,)?) => {
103 $(impl IsEmpty for $t {
104 fn is_empty_value(&self) -> bool {
105 *self == 0 as $t
106 }
107 })+
108 };
109}
110
111empty_num!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
112
113impl IsEmpty for f64 {
114 fn is_empty_value(&self) -> bool {
115 self.to_bits() == 0
117 }
118}
119
120impl<T: IsEmpty> IsEmpty for Option<T> {
121 fn is_empty_value(&self) -> bool {
122 match self {
123 None => true,
124 Some(v) => v.is_empty_value(),
125 }
126 }
127}
128
129impl<T> IsEmpty for Vec<T> {
130 fn is_empty_value(&self) -> bool {
131 self.is_empty()
132 }
133}
134
135impl IsEmpty for Box<dyn crate::valuer::SqlValuer> {
136 fn is_empty_value(&self) -> bool {
137 false
139 }
140}
141
142pub struct Struct<T: SqlStruct> {
143 pub flavor: Flavor,
144 mapper: FieldMapperFunc,
145 with_tags: Vec<&'static str>,
146 without_tags: Vec<&'static str>,
147 _phantom: std::marker::PhantomData<T>,
148}
149
150impl<T: SqlStruct> Clone for Struct<T> {
151 fn clone(&self) -> Self {
152 Self {
153 flavor: self.flavor,
154 mapper: self.mapper.clone(),
155 with_tags: self.with_tags.clone(),
156 without_tags: self.without_tags.clone(),
157 _phantom: std::marker::PhantomData,
158 }
159 }
160}
161
162impl<T: SqlStruct> std::fmt::Debug for Struct<T> {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 f.debug_struct("Struct")
166 .field("flavor", &self.flavor)
167 .field("with_tags", &self.with_tags)
168 .field("without_tags", &self.without_tags)
169 .finish()
170 }
171}
172
173impl<T: SqlStruct> Default for Struct<T> {
174 fn default() -> Self {
175 Self {
176 flavor: crate::default_flavor(),
177 mapper: default_field_mapper(),
178 with_tags: Vec::new(),
179 without_tags: Vec::new(),
180 _phantom: std::marker::PhantomData,
181 }
182 }
183}
184
185impl<T: SqlStruct> Struct<T> {
186 pub fn new() -> Self {
187 Self::default()
188 }
189
190 pub fn with_field_mapper(&self, mapper: FieldMapperFunc) -> Self {
194 let mut c = self.clone();
195 c.mapper = mapper;
196 c
197 }
198
199 fn has_defined_tag(tag: &str) -> bool {
200 if tag.is_empty() {
201 return false;
202 }
203 T::FIELDS
204 .iter()
205 .any(|f| !is_ignored(f) && f.tags.contains(&tag))
206 }
207
208 pub fn for_flavor(&self, flavor: Flavor) -> Self {
210 let mut c = self.clone();
211 c.flavor = flavor;
212 c
213 }
214
215 pub fn with_tag(&self, tags: impl IntoIterator<Item = &'static str>) -> Self {
217 let mut c = self.clone();
218 for t in tags {
219 if t.is_empty() {
220 continue;
221 }
222 if !c.with_tags.contains(&t) {
223 c.with_tags.push(t);
224 }
225 }
226 c.with_tags.sort_unstable();
227 c.with_tags.dedup();
228 c
229 }
230
231 pub fn without_tag(&self, tags: impl IntoIterator<Item = &'static str>) -> Self {
233 let mut c = self.clone();
234 for t in tags {
235 if t.is_empty() {
236 continue;
237 }
238 if !c.without_tags.contains(&t) {
239 c.without_tags.push(t);
240 }
241 }
242 c.without_tags.sort_unstable();
243 c.without_tags.dedup();
244 c.with_tags.retain(|t| !c.without_tags.contains(t));
246 c
247 }
248
249 fn should_omit_empty(&self, fm: &FieldMeta) -> bool {
250 let omit = fm.omitempty_tags;
254 if omit.is_empty() {
255 return false;
256 }
257 if omit.contains(&"") {
258 return true;
259 }
260 self.with_tags.iter().any(|t| omit.contains(t))
261 }
262
263 fn excluded_by_without(&self, fm: &FieldMeta) -> bool {
264 self.without_tags.iter().any(|t| fm.tags.contains(t))
265 }
266
267 fn alias_of(&self, fm: &FieldMeta) -> String {
268 if is_ignored(fm) {
269 return String::new();
270 }
271
272 if !fm.db.is_empty() {
273 return fm.db.to_string();
274 }
275
276 let mapped = (self.mapper)(fm.orig);
277 if mapped.is_empty() {
278 fm.orig.to_string()
279 } else {
280 mapped
281 }
282 }
283
284 fn read_key_of(&self, fm: &FieldMeta) -> String {
285 if let Some(as_) = fm.as_ {
287 return as_.to_string();
288 }
289 let a = self.alias_of(fm);
290 if a.is_empty() { fm.rust.to_string() } else { a }
291 }
292
293 fn write_key_of(&self, fm: &FieldMeta) -> String {
294 let a = self.alias_of(fm);
296 if a.is_empty() { fm.rust.to_string() } else { a }
297 }
298
299 fn fields_for_read(&self) -> Vec<&'static FieldMeta> {
300 self.fields_filtered(true)
301 }
302
303 fn fields_for_write(&self) -> Vec<&'static FieldMeta> {
304 self.fields_filtered(false)
305 }
306
307 fn fields_filtered(&self, for_read: bool) -> Vec<&'static FieldMeta> {
308 let mut out = Vec::new();
309 let mut seen = HashSet::<String>::new();
310
311 let push_field = |out: &mut Vec<&'static FieldMeta>,
312 seen: &mut HashSet<String>,
313 fm: &'static FieldMeta,
314 for_read: bool| {
315 if is_ignored(fm) {
316 return;
317 }
318 if self.excluded_by_without(fm) {
319 return;
320 }
321 let key = if for_read {
322 self.read_key_of(fm)
323 } else {
324 self.write_key_of(fm)
325 };
326 if seen.insert(key) {
327 out.push(fm);
328 }
329 };
330
331 if self.with_tags.is_empty() {
332 for fm in T::FIELDS {
333 push_field(&mut out, &mut seen, fm, for_read);
334 }
335 return out;
336 }
337
338 for tag in &self.with_tags {
340 for fm in T::FIELDS {
341 if fm.tags.contains(tag) {
342 push_field(&mut out, &mut seen, fm, for_read);
343 }
344 }
345 }
346
347 out
348 }
349
350 fn parse_table_alias(table: &str) -> &str {
351 table.rsplit_once(' ').map(|(_, a)| a).unwrap_or(table)
353 }
354
355 pub fn columns(&self) -> Vec<String> {
357 self.fields_for_write()
358 .into_iter()
359 .map(|f| self.alias_of(f))
360 .collect()
361 }
362
363 pub fn columns_for_tag(&self, tag: &str) -> Option<Vec<String>> {
365 if !Self::has_defined_tag(tag) {
366 return None;
367 }
368 let tag: &'static str = Box::leak(tag.to_string().into_boxed_str());
370 Some(self.with_tag([tag]).columns())
371 }
372
373 pub fn values(&self, v: &T) -> Vec<crate::modifiers::Arg> {
375 let all = v.values();
376 let mut out = Vec::new();
377 for (fm, arg) in T::FIELDS.iter().zip(all) {
378 if is_ignored(fm) || self.excluded_by_without(fm) {
379 continue;
380 }
381 if self.with_tags.is_empty() || self.with_tags.iter().any(|t| fm.tags.contains(t)) {
382 out.push(arg);
383 }
384 }
385 let mut map = std::collections::HashMap::<&'static str, crate::modifiers::Arg>::new();
387 for (fm, arg) in T::FIELDS.iter().zip(v.values()) {
388 map.insert(fm.rust, arg);
389 }
390 self.fields_for_write()
391 .into_iter()
392 .filter_map(|fm| map.get(fm.rust).cloned())
393 .collect()
394 }
395
396 pub fn values_for_tag(&self, tag: &str, v: &T) -> Option<Vec<crate::modifiers::Arg>> {
398 if !Self::has_defined_tag(tag) {
399 return None;
400 }
401 let tag: &'static str = Box::leak(tag.to_string().into_boxed_str());
402 Some(self.with_tag([tag]).values(v))
403 }
404
405 pub fn foreach_read(&self, mut trans: impl FnMut(&str, bool, &FieldMeta)) {
411 for fm in self.fields_for_read() {
412 trans(fm.db, fm.with_quote, fm);
413 }
414 }
415
416 pub fn foreach_write(&self, mut trans: impl FnMut(&str, bool, &FieldMeta)) {
418 for fm in self.fields_for_write() {
419 trans(fm.db, fm.with_quote, fm);
420 }
421 }
422
423 pub fn addr<'a>(&self, st: &'a mut T) -> Vec<crate::scan::ScanCell<'a>> {
425 let rust_fields: Vec<&'static str> = self
426 .fields_for_read()
427 .into_iter()
428 .map(|fm| fm.rust)
429 .collect();
430 st.addr_cells(&rust_fields).unwrap_or_default()
431 }
432
433 pub fn addr_for_tag<'a>(
435 &self,
436 tag: &str,
437 st: &'a mut T,
438 ) -> Option<Vec<crate::scan::ScanCell<'a>>> {
439 if !Self::has_defined_tag(tag) {
440 return None;
441 }
442 let tag: &'static str = Box::leak(tag.to_string().into_boxed_str());
443 Some(self.with_tag([tag]).addr(st))
444 }
445
446 pub fn addr_with_cols<'a>(
448 &self,
449 cols: &[&str],
450 st: &'a mut T,
451 ) -> Option<Vec<crate::scan::ScanCell<'a>>> {
452 let fields = self.fields_for_read();
453 let mut map = std::collections::HashMap::<String, &'static str>::new();
454 for fm in fields {
455 let key = self.read_key_of(fm);
456 map.insert(key, fm.rust);
457 }
458
459 let mut rust_fields = Vec::with_capacity(cols.len());
460 for &c in cols {
461 rust_fields.push(*map.get(c)?);
462 }
463 st.addr_cells(&rust_fields)
464 }
465
466 pub fn select_from(&self, table: &str) -> SelectBuilder {
467 let mut sb = SelectBuilder::new();
468 sb.set_flavor(self.flavor);
469 sb.from([table.to_string()]);
470
471 let alias = Self::parse_table_alias(table);
472 let cols: Vec<String> = self
473 .fields_for_read()
474 .into_iter()
475 .map(|f| {
476 let field_alias = self.alias_of(f);
477 let mut c = String::new();
478 if self.flavor != Flavor::CQL && !field_alias.contains('.') {
480 c.push_str(alias);
481 c.push('.');
482 }
483 c.push_str(&f.name_for_select(self.flavor, &field_alias));
484 c
485 })
486 .collect();
487
488 if cols.is_empty() {
489 select_cols!(sb, "*");
490 } else {
491 sb.select(cols);
492 }
493 sb
494 }
495
496 pub fn select_from_for_tag(&self, table: &str, tag: &str) -> SelectBuilder {
498 let tag: &'static str = Box::leak(tag.to_string().into_boxed_str());
500 self.with_tag([tag]).select_from(table)
501 }
502
503 pub fn update(&self, table: &str, value: &T) -> UpdateBuilder {
504 let mut ub = UpdateBuilder::new();
505 ub.set_flavor(self.flavor);
506 ub.update([table.to_string()]);
507
508 let mut assigns = Vec::new();
509
510 let mut map = std::collections::HashMap::<&'static str, crate::modifiers::Arg>::new();
511 for (fm, arg) in T::FIELDS.iter().zip(value.values()) {
512 map.insert(fm.rust, arg);
513 }
514
515 for fm in self.fields_for_write() {
516 if self.should_omit_empty(fm) && value.is_empty_field(fm.rust) {
517 continue;
518 }
519 let field_alias = self.alias_of(fm);
521 let col = if fm.with_quote {
522 self.flavor.quote(&field_alias)
523 } else {
524 field_alias
525 };
526 if let Some(v) = map.get(fm.rust).cloned() {
527 assigns.push(ub.assign(&col, v));
528 }
529 }
530
531 ub.set(assigns);
532 ub
533 }
534
535 pub fn update_for_tag(&self, table: &str, tag: &str, value: &T) -> UpdateBuilder {
537 let tag: &'static str = Box::leak(tag.to_string().into_boxed_str());
538 self.with_tag([tag]).update(table, value)
539 }
540
541 pub fn delete_from(&self, table: &str) -> DeleteBuilder {
542 let mut db = DeleteBuilder::new();
543 db.set_flavor(self.flavor);
544 db.delete_from([table.to_string()]);
545 db
546 }
547
548 pub fn insert_into<'a>(
549 &self,
550 table: &str,
551 rows: impl IntoIterator<Item = &'a T>,
552 ) -> InsertBuilder
553 where
554 T: 'a,
555 {
556 self.insert_internal(table, rows, InsertVerb::Insert)
557 }
558
559 pub fn insert_into_for_tag<'a>(
561 &self,
562 table: &str,
563 tag: &str,
564 rows: impl IntoIterator<Item = &'a T>,
565 ) -> InsertBuilder
566 where
567 T: 'a,
568 {
569 let tag: &'static str = Box::leak(tag.to_string().into_boxed_str());
570 self.with_tag([tag]).insert_into(table, rows)
571 }
572
573 pub fn insert_ignore_into_for_tag<'a>(
574 &self,
575 table: &str,
576 tag: &str,
577 rows: impl IntoIterator<Item = &'a T>,
578 ) -> InsertBuilder
579 where
580 T: 'a,
581 {
582 let tag: &'static str = Box::leak(tag.to_string().into_boxed_str());
583 self.with_tag([tag]).insert_ignore_into(table, rows)
584 }
585
586 pub fn replace_into_for_tag<'a>(
587 &self,
588 table: &str,
589 tag: &str,
590 rows: impl IntoIterator<Item = &'a T>,
591 ) -> InsertBuilder
592 where
593 T: 'a,
594 {
595 let tag: &'static str = Box::leak(tag.to_string().into_boxed_str());
596 self.with_tag([tag]).replace_into(table, rows)
597 }
598
599 fn filter_rows_any<'a>(values: impl IntoIterator<Item = &'a dyn Any>) -> Vec<&'a T>
600 where
601 T: 'static,
602 {
603 values
604 .into_iter()
605 .filter_map(|v| v.downcast_ref::<T>())
606 .collect()
607 }
608
609 pub fn insert_into_any<'a>(
611 &self,
612 table: &str,
613 values: impl IntoIterator<Item = &'a dyn Any>,
614 ) -> InsertBuilder
615 where
616 T: 'static,
617 {
618 let rows = Self::filter_rows_any(values);
619 self.insert_into(table, rows)
620 }
621
622 pub fn insert_ignore_into_any<'a>(
623 &self,
624 table: &str,
625 values: impl IntoIterator<Item = &'a dyn Any>,
626 ) -> InsertBuilder
627 where
628 T: 'static,
629 {
630 let rows = Self::filter_rows_any(values);
631 self.insert_ignore_into(table, rows)
632 }
633
634 pub fn replace_into_any<'a>(
635 &self,
636 table: &str,
637 values: impl IntoIterator<Item = &'a dyn Any>,
638 ) -> InsertBuilder
639 where
640 T: 'static,
641 {
642 let rows = Self::filter_rows_any(values);
643 self.replace_into(table, rows)
644 }
645
646 pub fn insert_into_for_tag_any<'a>(
647 &self,
648 table: &str,
649 tag: &str,
650 values: impl IntoIterator<Item = &'a dyn Any>,
651 ) -> InsertBuilder
652 where
653 T: 'static,
654 {
655 let tag: &'static str = Box::leak(tag.to_string().into_boxed_str());
656 let rows = Self::filter_rows_any(values);
657 self.with_tag([tag]).insert_into(table, rows)
658 }
659
660 pub fn insert_ignore_into_for_tag_any<'a>(
661 &self,
662 table: &str,
663 tag: &str,
664 values: impl IntoIterator<Item = &'a dyn Any>,
665 ) -> InsertBuilder
666 where
667 T: 'static,
668 {
669 let tag: &'static str = Box::leak(tag.to_string().into_boxed_str());
670 let rows = Self::filter_rows_any(values);
671 self.with_tag([tag]).insert_ignore_into(table, rows)
672 }
673
674 pub fn replace_into_for_tag_any<'a>(
675 &self,
676 table: &str,
677 tag: &str,
678 values: impl IntoIterator<Item = &'a dyn Any>,
679 ) -> InsertBuilder
680 where
681 T: 'static,
682 {
683 let tag: &'static str = Box::leak(tag.to_string().into_boxed_str());
684 let rows = Self::filter_rows_any(values);
685 self.with_tag([tag]).replace_into(table, rows)
686 }
687
688 pub fn insert_ignore_into<'a>(
689 &self,
690 table: &str,
691 rows: impl IntoIterator<Item = &'a T>,
692 ) -> InsertBuilder
693 where
694 T: 'a,
695 {
696 self.insert_internal(table, rows, InsertVerb::InsertIgnore)
697 }
698
699 pub fn replace_into<'a>(
700 &self,
701 table: &str,
702 rows: impl IntoIterator<Item = &'a T>,
703 ) -> InsertBuilder
704 where
705 T: 'a,
706 {
707 self.insert_internal(table, rows, InsertVerb::Replace)
708 }
709
710 fn insert_internal<'a>(
711 &self,
712 table: &str,
713 rows: impl IntoIterator<Item = &'a T>,
714 verb: InsertVerb,
715 ) -> InsertBuilder
716 where
717 T: 'a,
718 {
719 let mut ib = InsertBuilder::new();
720 ib.set_flavor(self.flavor);
721 match verb {
722 InsertVerb::Insert => {
723 ib.insert_into(table);
724 }
725 InsertVerb::InsertIgnore => {
726 ib.insert_ignore_into(table);
727 }
728 InsertVerb::Replace => {
729 ib.replace_into(table);
730 }
731 }
732
733 let rows: Vec<&T> = rows.into_iter().collect();
734 if rows.is_empty() {
735 return ib;
737 }
738
739 let fields = self.fields_for_write();
740
741 let mut nil_cnt = vec![0_usize; fields.len()];
743 for (fi, fm) in fields.iter().enumerate() {
744 let should_omit = self.should_omit_empty(fm);
745 if !should_omit {
746 continue;
747 }
748 for r in &rows {
749 if r.is_empty_field(fm.rust) {
750 nil_cnt[fi] += 1;
751 }
752 }
753 }
754
755 let mut kept = Vec::<usize>::new();
756 for (i, cnt) in nil_cnt.into_iter().enumerate() {
757 if cnt == rows.len() {
758 continue;
759 }
760 kept.push(i);
761 }
762
763 let cols: Vec<String> = kept
764 .iter()
765 .map(|&i| {
766 let fm = fields[i];
767 let field_alias = self.alias_of(fm);
768 if fm.with_quote {
769 self.flavor.quote(&field_alias)
770 } else {
771 field_alias
772 }
773 })
774 .collect();
775 ib.cols(escape_all(cols));
776
777 for r in rows {
778 let mut map = std::collections::HashMap::<&'static str, crate::modifiers::Arg>::new();
779 for (fm, arg) in T::FIELDS.iter().zip(r.values()) {
780 map.insert(fm.rust, arg);
781 }
782 let mut row_args = Vec::new();
783 for &i in &kept {
784 let fm = fields[i];
785 row_args.push(
786 map.get(fm.rust)
787 .cloned()
788 .unwrap_or_else(|| crate::SqlValue::Null.into()),
789 );
790 }
791 ib.values(row_args);
792 }
793
794 ib
795 }
796}
797
798#[derive(Debug, Clone, Copy)]
799enum InsertVerb {
800 Insert,
801 InsertIgnore,
802 Replace,
803}
804
805#[macro_export]
821macro_rules! sql_struct {
822 (
823 impl $ty:ty {
824 $(
825 $field:ident : { db: $db:literal, $(orig: $orig:literal,)? tags: [ $($tag:literal),* $(,)? ], omitempty: [ $($omit:literal),* $(,)? ], quote: $quote:literal, as: $as:expr }
826 ),* $(,)?
827 }
828 ) => {
829 impl $crate::structs::SqlStruct for $ty {
830 const FIELDS: &'static [$crate::structs::FieldMeta] = &[
831 $(
832 $crate::structs::FieldMeta{
833 rust: stringify!($field),
834 orig: $crate::__sql_struct_orig!(stringify!($field) $(, $orig)?),
835 db: $db,
836 as_: $as,
837 tags: &[ $($tag),* ],
838 omitempty_tags: &[ $($omit),* ],
839 with_quote: $quote,
840 }
841 ),*
842 ];
843
844 fn values(&self) -> Vec<$crate::modifiers::Arg> {
845 vec![
846 $(
847 $crate::modifiers::Arg::from(self.$field.clone())
848 ),*
849 ]
850 }
851
852 fn is_empty_field(&self, rust_field: &'static str) -> bool {
853 match rust_field {
854 $(
855 stringify!($field) => $crate::structs::IsEmpty::is_empty_value(&self.$field),
856 )*
857 _ => false,
858 }
859 }
860
861 fn addr_cells<'a>(
862 &'a mut self,
863 rust_fields: &[&'static str],
864 ) -> Option<Vec<$crate::scan::ScanCell<'a>>> {
865 let mut out = Vec::with_capacity(rust_fields.len());
866 for &rf in rust_fields {
867 match rf {
868 $(
869 stringify!($field) => {
870 out.push($crate::scan::ScanCell::from_ptr(std::ptr::addr_of_mut!(self.$field)));
871 }
872 )*
873 _ => return None,
874 }
875 }
876 Some(out)
877 }
878 }
879 };
880}
881
882#[doc(hidden)]
884#[macro_export]
885macro_rules! __sql_struct_orig {
886 ($default:expr) => {
887 $default
888 };
889 ($default:expr, $custom:expr) => {
890 $custom
891 };
892}