Skip to main content

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 static precision/scale.
10///
11/// `WIDTH` = total digits, `SCALE` = fractional digits.
12#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
13pub struct FixedDecimal<const WIDTH: u8, const SCALE: u8>(pub Decimal);
14
15impl<const W: u8, const S: u8> From<Decimal> for FixedDecimal<W, S> {
16    fn from(value: Decimal) -> Self {
17        Self(value)
18    }
19}
20
21impl<const W: u8, const S: u8> From<FixedDecimal<W, S>> for Decimal {
22    fn from(value: FixedDecimal<W, S>) -> Self {
23        value.0
24    }
25}
26
27/// Value wrapper for optional persistence.
28///
29/// - `Set(T)`: Value is actively written.
30/// - `NotSet`: Value is omitted (DB default used).
31#[derive(Debug, Default)]
32pub enum Passive<T: AsValue> {
33    /// Active value.
34    Set(T),
35    /// Omitted value.
36    #[default]
37    NotSet,
38}
39
40impl<T: AsValue> Passive<T> {
41    pub fn new(value: T) -> Self {
42        Passive::Set(value)
43    }
44    pub fn expect(self, msg: &str) -> T {
45        match self {
46            Passive::Set(v) => v,
47            Passive::NotSet => panic!("{msg}"),
48        }
49    }
50    pub fn unwrap(self) -> T {
51        match self {
52            Passive::Set(v) => v,
53            Passive::NotSet => panic!("called `Passive::unwrap()` on a `NotSet` value"),
54        }
55    }
56}
57
58impl<T: PartialEq + AsValue> PartialEq for Passive<T> {
59    fn eq(&self, other: &Self) -> bool {
60        match (self, other) {
61            (Self::Set(lhs), Self::Set(rhs)) => lhs == rhs,
62            _ => mem::discriminant(self) == mem::discriminant(other),
63        }
64    }
65}
66
67impl<T: Eq + AsValue> Eq for Passive<T> {}
68
69impl<T: Clone + AsValue> Clone for Passive<T>
70where
71    T: Clone,
72{
73    fn clone(&self) -> Self {
74        match self {
75            Self::Set(v) => Self::Set(v.clone()),
76            Self::NotSet => Self::NotSet,
77        }
78    }
79}
80
81impl<T: AsValue> From<T> for Passive<T> {
82    fn from(value: T) -> Self {
83        Self::Set(value)
84    }
85}
86
87impl<T: Hash + AsValue> Hash for Passive<T> {
88    fn hash<H: Hasher>(&self, state: &mut H) {
89        mem::discriminant(self).hash(state);
90        if let Passive::Set(v) = self {
91            v.hash(state);
92        }
93    }
94}
95
96/// Represents a foreign key constraint to another entity.
97pub struct References<T: Entity> {
98    entity: PhantomData<T>,
99    columns: Box<[ColumnDef]>,
100}
101
102impl<T: Entity> References<T> {
103    pub fn new(columns: Box<[ColumnDef]>) -> Self {
104        Self {
105            columns,
106            entity: Default::default(),
107        }
108    }
109    pub fn table_ref(&self) -> TableRef {
110        T::table().clone()
111    }
112    pub fn columns(&self) -> &[ColumnDef] {
113        &self.columns
114    }
115}