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 VarChar(MssqlTypeLength),
75 VarBinary(MssqlTypeLength),
77 Binary(usize),
79 Decimal {
81 precision: u8,
83 scale: i8,
85 },
86 Date,
88 Time(MssqlTimePrecision),
90 DateTime,
92 DateTime2 {
94 precision: u8,
96 },
97 DateTimeOffset {
99 precision: u8,
101 },
102}
103
104impl MssqlType {
105 pub fn to_sql(&self) -> String {
107 match self {
108 Self::Bit => "bit".to_owned(),
109 Self::TinyInt => "tinyint".to_owned(),
110 Self::SmallInt => "smallint".to_owned(),
111 Self::Int => "int".to_owned(),
112 Self::BigInt => "bigint".to_owned(),
113 Self::Real => "real".to_owned(),
114 Self::Float { precision } => format!("float({precision})"),
115 Self::NVarChar(length) => format!("nvarchar({})", length.render()),
116 Self::VarChar(length) => format!("varchar({})", length.render()),
117 Self::VarBinary(length) => format!("varbinary({})", length.render()),
118 Self::Binary(length) => format!("binary({length})"),
119 Self::Decimal { precision, scale } => format!("decimal({precision},{scale})"),
120 Self::Date => "date".to_owned(),
121 Self::Time(precision) => format!("time({})", precision.get()),
122 Self::DateTime => "datetime".to_owned(),
123 Self::DateTime2 { precision } => format!("datetime2({precision})"),
124 Self::DateTimeOffset { precision } => format!("datetimeoffset({precision})"),
125 }
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::{MssqlTimePrecision, MssqlType, MssqlTypeLength};
132
133 #[test]
134 fn renders_primitive_types() {
135 assert_eq!(MssqlType::Bit.to_sql(), "bit");
136 assert_eq!(MssqlType::TinyInt.to_sql(), "tinyint");
137 assert_eq!(MssqlType::SmallInt.to_sql(), "smallint");
138 assert_eq!(MssqlType::Int.to_sql(), "int");
139 assert_eq!(MssqlType::BigInt.to_sql(), "bigint");
140 assert_eq!(MssqlType::Real.to_sql(), "real");
141 assert_eq!(MssqlType::Float { precision: 53 }.to_sql(), "float(53)");
142 }
143
144 #[test]
145 fn renders_variable_length_types() {
146 assert_eq!(
147 MssqlType::NVarChar(MssqlTypeLength::Max).to_sql(),
148 "nvarchar(max)"
149 );
150 assert_eq!(
151 MssqlType::NVarChar(MssqlTypeLength::Bounded(128)).to_sql(),
152 "nvarchar(128)"
153 );
154 assert_eq!(
155 MssqlType::VarChar(MssqlTypeLength::Bounded(128)).to_sql(),
156 "varchar(128)"
157 );
158 assert_eq!(
159 MssqlType::VarBinary(MssqlTypeLength::Max).to_sql(),
160 "varbinary(max)"
161 );
162 assert_eq!(
163 MssqlType::VarBinary(MssqlTypeLength::Bounded(8000)).to_sql(),
164 "varbinary(8000)"
165 );
166 assert_eq!(MssqlType::Binary(16).to_sql(), "binary(16)");
167 }
168
169 #[test]
170 fn renders_decimal_and_temporal_types() {
171 assert_eq!(
172 MssqlType::Decimal {
173 precision: 38,
174 scale: 9
175 }
176 .to_sql(),
177 "decimal(38,9)"
178 );
179 assert_eq!(MssqlType::Date.to_sql(), "date");
180 assert_eq!(
181 MssqlType::Time(MssqlTimePrecision::ZERO).to_sql(),
182 "time(0)"
183 );
184 assert_eq!(
185 MssqlType::Time(MssqlTimePrecision::THREE).to_sql(),
186 "time(3)"
187 );
188 assert_eq!(MssqlType::Time(MssqlTimePrecision::SIX).to_sql(), "time(6)");
189 assert_eq!(
190 MssqlType::Time(MssqlTimePrecision::SEVEN).to_sql(),
191 "time(7)"
192 );
193 assert_eq!(
194 MssqlType::Time(MssqlTimePrecision::new(1).unwrap()).to_sql(),
195 "time(1)"
196 );
197 assert_eq!(
198 MssqlType::Time(MssqlTimePrecision::new(2).unwrap()).to_sql(),
199 "time(2)"
200 );
201 assert_eq!(
202 MssqlType::Time(MssqlTimePrecision::new(4).unwrap()).to_sql(),
203 "time(4)"
204 );
205 assert_eq!(
206 MssqlType::Time(MssqlTimePrecision::new(5).unwrap()).to_sql(),
207 "time(5)"
208 );
209 assert_eq!(MssqlType::DateTime.to_sql(), "datetime");
210 assert_eq!(
211 MssqlType::DateTime2 { precision: 7 }.to_sql(),
212 "datetime2(7)"
213 );
214 assert_eq!(
215 MssqlType::DateTimeOffset { precision: 7 }.to_sql(),
216 "datetimeoffset(7)"
217 );
218 }
219
220 #[test]
221 fn rejects_invalid_time_precision() {
222 for precision in 0..=7 {
223 assert_eq!(MssqlTimePrecision::new(precision).unwrap().get(), precision);
224 }
225 assert_eq!(MssqlTimePrecision::new(8), None);
226 assert_eq!(MssqlTimePrecision::new(u8::MAX), None);
227 }
228}