tank_core/
relations.rs

1use crate::{AsValue, ColumnDef, Entity, TableRef};
2use rust_decimal::Decimal;
3use std::{
4    hash::{Hash, Hasher},
5    marker::PhantomData,
6    mem,
7};
8
9/// Decimal wrapper enforcing compile-time width/scale.
10#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub struct FixedDecimal<const WIDTH: u8, const SCALE: u8>(pub Decimal);
12
13impl<const W: u8, const S: u8> From<Decimal> for FixedDecimal<W, S> {
14    fn from(value: Decimal) -> Self {
15        Self(value)
16    }
17}
18
19impl<const W: u8, const S: u8> From<FixedDecimal<W, S>> for Decimal {
20    fn from(value: FixedDecimal<W, S>) -> Self {
21        value.0
22    }
23}
24
25/// Wrapper marking whether a column should be considered set or skipped (passive) on INSERT.
26#[derive(Debug, Default)]
27pub enum Passive<T: AsValue> {
28    /// Active value.
29    Set(T),
30    /// Skip during value emission (DEFAULT used by `SqlWriter`).
31    #[default]
32    NotSet,
33}
34
35impl<T: AsValue> Passive<T> {
36    pub fn new(value: T) -> Self {
37        Passive::Set(value)
38    }
39    pub fn expect(self, msg: &str) -> T {
40        match self {
41            Passive::Set(v) => v,
42            Passive::NotSet => panic!("{msg}"),
43        }
44    }
45    pub fn unwrap(self) -> T {
46        match self {
47            Passive::Set(v) => v,
48            Passive::NotSet => panic!("called `Passive::unwrap()` on a `NotSet` value"),
49        }
50    }
51}
52
53impl<T: PartialEq + AsValue> PartialEq for Passive<T> {
54    fn eq(&self, other: &Self) -> bool {
55        match (self, other) {
56            (Self::Set(lhs), Self::Set(rhs)) => lhs == rhs,
57            _ => mem::discriminant(self) == mem::discriminant(other),
58        }
59    }
60}
61
62impl<T: Eq + AsValue> Eq for Passive<T> {}
63
64impl<T: Clone + AsValue> Clone for Passive<T>
65where
66    T: Clone,
67{
68    fn clone(&self) -> Self {
69        match self {
70            Self::Set(v) => Self::Set(v.clone()),
71            Self::NotSet => Self::NotSet,
72        }
73    }
74}
75
76impl<T: AsValue> From<T> for Passive<T> {
77    fn from(value: T) -> Self {
78        Self::Set(value)
79    }
80}
81
82impl<T: Hash + AsValue> Hash for Passive<T> {
83    fn hash<H: Hasher>(&self, state: &mut H) {
84        mem::discriminant(self).hash(state);
85        if let Passive::Set(v) = self {
86            v.hash(state);
87        }
88    }
89}
90
91/// Foreign key reference to another Entity's columns.
92pub struct References<T: Entity> {
93    entity: PhantomData<T>,
94    columns: Box<[ColumnDef]>,
95}
96
97impl<T: Entity> References<T> {
98    pub fn new(columns: Box<[ColumnDef]>) -> Self {
99        Self {
100            columns,
101            entity: Default::default(),
102        }
103    }
104    pub fn table_ref(&self) -> TableRef {
105        T::table().clone()
106    }
107    pub fn columns(&self) -> &[ColumnDef] {
108        &self.columns
109    }
110}