1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub enum MssqlTypeLength {
6 Bounded(usize),
8 Max,
10}
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct MssqlTimePrecision(u8);
15
16impl MssqlTimePrecision {
17 pub const ZERO: Self = Self(0);
19 pub const THREE: Self = Self(3);
21 pub const SIX: Self = Self(6);
23 pub const SEVEN: Self = Self(7);
25
26 pub const fn new(precision: u8) -> Option<Self> {
28 if precision <= 7 {
29 Some(Self(precision))
30 } else {
31 None
32 }
33 }
34
35 pub const fn get(self) -> u8 {
37 self.0
38 }
39}
40
41impl MssqlTypeLength {
42 fn render(self) -> String {
43 match self {
44 Self::Bounded(length) => length.to_string(),
45 Self::Max => "max".to_owned(),
46 }
47 }
48}
49
50#[derive(Debug, Clone, PartialEq, Eq, Hash)]
52#[non_exhaustive]
53pub enum MssqlType {
54 Bit,
56 TinyInt,
58 SmallInt,
60 Int,
62 BigInt,
64 Real,
66 Float {
68 precision: u8,
70 },
71 NVarChar(MssqlTypeLength),
73 VarBinary(MssqlTypeLength),
75 Binary(usize),
77 Decimal {
79 precision: u8,
81 scale: i8,
83 },
84 Date,
86 Time(MssqlTimePrecision),
88 DateTime,
90 DateTime2 {
92 precision: u8,
94 },
95 DateTimeOffset {
97 precision: u8,
99 },
100}
101
102impl MssqlType {
103 pub fn to_sql(&self) -> String {
105 match self {
106 Self::Bit => "bit".to_owned(),
107 Self::TinyInt => "tinyint".to_owned(),
108 Self::SmallInt => "smallint".to_owned(),
109 Self::Int => "int".to_owned(),
110 Self::BigInt => "bigint".to_owned(),
111 Self::Real => "real".to_owned(),
112 Self::Float { precision } => format!("float({precision})"),
113 Self::NVarChar(length) => format!("nvarchar({})", length.render()),
114 Self::VarBinary(length) => format!("varbinary({})", length.render()),
115 Self::Binary(length) => format!("binary({length})"),
116 Self::Decimal { precision, scale } => format!("decimal({precision},{scale})"),
117 Self::Date => "date".to_owned(),
118 Self::Time(precision) => format!("time({})", precision.get()),
119 Self::DateTime => "datetime".to_owned(),
120 Self::DateTime2 { precision } => format!("datetime2({precision})"),
121 Self::DateTimeOffset { precision } => format!("datetimeoffset({precision})"),
122 }
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::{MssqlTimePrecision, MssqlType, MssqlTypeLength};
129
130 #[test]
131 fn renders_primitive_types() {
132 assert_eq!(MssqlType::Bit.to_sql(), "bit");
133 assert_eq!(MssqlType::TinyInt.to_sql(), "tinyint");
134 assert_eq!(MssqlType::SmallInt.to_sql(), "smallint");
135 assert_eq!(MssqlType::Int.to_sql(), "int");
136 assert_eq!(MssqlType::BigInt.to_sql(), "bigint");
137 assert_eq!(MssqlType::Real.to_sql(), "real");
138 assert_eq!(MssqlType::Float { precision: 53 }.to_sql(), "float(53)");
139 }
140
141 #[test]
142 fn renders_variable_length_types() {
143 assert_eq!(
144 MssqlType::NVarChar(MssqlTypeLength::Max).to_sql(),
145 "nvarchar(max)"
146 );
147 assert_eq!(
148 MssqlType::NVarChar(MssqlTypeLength::Bounded(128)).to_sql(),
149 "nvarchar(128)"
150 );
151 assert_eq!(
152 MssqlType::VarBinary(MssqlTypeLength::Max).to_sql(),
153 "varbinary(max)"
154 );
155 assert_eq!(
156 MssqlType::VarBinary(MssqlTypeLength::Bounded(8000)).to_sql(),
157 "varbinary(8000)"
158 );
159 assert_eq!(MssqlType::Binary(16).to_sql(), "binary(16)");
160 }
161
162 #[test]
163 fn renders_decimal_and_temporal_types() {
164 assert_eq!(
165 MssqlType::Decimal {
166 precision: 38,
167 scale: 9
168 }
169 .to_sql(),
170 "decimal(38,9)"
171 );
172 assert_eq!(MssqlType::Date.to_sql(), "date");
173 assert_eq!(
174 MssqlType::Time(MssqlTimePrecision::ZERO).to_sql(),
175 "time(0)"
176 );
177 assert_eq!(
178 MssqlType::Time(MssqlTimePrecision::THREE).to_sql(),
179 "time(3)"
180 );
181 assert_eq!(MssqlType::Time(MssqlTimePrecision::SIX).to_sql(), "time(6)");
182 assert_eq!(
183 MssqlType::Time(MssqlTimePrecision::SEVEN).to_sql(),
184 "time(7)"
185 );
186 assert_eq!(
187 MssqlType::Time(MssqlTimePrecision::new(1).unwrap()).to_sql(),
188 "time(1)"
189 );
190 assert_eq!(
191 MssqlType::Time(MssqlTimePrecision::new(2).unwrap()).to_sql(),
192 "time(2)"
193 );
194 assert_eq!(
195 MssqlType::Time(MssqlTimePrecision::new(4).unwrap()).to_sql(),
196 "time(4)"
197 );
198 assert_eq!(
199 MssqlType::Time(MssqlTimePrecision::new(5).unwrap()).to_sql(),
200 "time(5)"
201 );
202 assert_eq!(MssqlType::DateTime.to_sql(), "datetime");
203 assert_eq!(
204 MssqlType::DateTime2 { precision: 7 }.to_sql(),
205 "datetime2(7)"
206 );
207 assert_eq!(
208 MssqlType::DateTimeOffset { precision: 7 }.to_sql(),
209 "datetimeoffset(7)"
210 );
211 }
212
213 #[test]
214 fn rejects_invalid_time_precision() {
215 for precision in 0..=7 {
216 assert_eq!(MssqlTimePrecision::new(precision).unwrap().get(), precision);
217 }
218 assert_eq!(MssqlTimePrecision::new(8), None);
219 assert_eq!(MssqlTimePrecision::new(u8::MAX), None);
220 }
221}