1use std::{
5 fmt::{Display, Formatter},
6 str::FromStr,
7};
8
9use serde::{Deserialize, Serialize};
10
11pub mod get;
12pub mod promote;
13
14use std::fmt;
15
16use crate::value::Value;
17
18#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
20pub enum Type {
21 Boolean,
23 Float4,
25 Float8,
27 Int1,
29 Int2,
31 Int4,
33 Int8,
35 Int16,
37 Utf8,
39 Uint1,
41 Uint2,
43 Uint4,
45 Uint8,
47 Uint16,
49 Date,
51 DateTime,
53 Time,
55 Duration,
57 IdentityId,
59 Uuid4,
61 Uuid7,
63 Blob,
65 Int,
67 Uint,
69 Decimal,
71 Option(Box<Type>),
73 Any,
75 DictionaryId,
77 List(Box<Type>),
79}
80
81impl Type {
82 pub fn list_of(ty: Type) -> Self {
83 Type::List(Box::new(ty))
84 }
85
86 pub fn is_number(&self) -> bool {
87 match self {
88 Type::Option(inner) => inner.is_number(),
89 _ => matches!(
90 self,
91 Type::Float4
92 | Type::Float8 | Type::Int1 | Type::Int2
93 | Type::Int4 | Type::Int8 | Type::Int16
94 | Type::Uint1 | Type::Uint2 | Type::Uint4
95 | Type::Uint8 | Type::Uint16 | Type::Int
96 | Type::Uint | Type::Decimal
97 ),
98 }
99 }
100
101 pub fn is_bool(&self) -> bool {
102 match self {
103 Type::Option(inner) => inner.is_bool(),
104 _ => matches!(self, Type::Boolean),
105 }
106 }
107
108 pub fn is_signed_integer(&self) -> bool {
109 match self {
110 Type::Option(inner) => inner.is_signed_integer(),
111 _ => matches!(
112 self,
113 Type::Int1 | Type::Int2 | Type::Int4 | Type::Int8 | Type::Int16 | Type::Int
114 ),
115 }
116 }
117
118 pub fn is_unsigned_integer(&self) -> bool {
119 match self {
120 Type::Option(inner) => inner.is_unsigned_integer(),
121 _ => matches!(
122 self,
123 Type::Uint1 | Type::Uint2 | Type::Uint4 | Type::Uint8 | Type::Uint16 | Type::Uint
124 ),
125 }
126 }
127
128 pub fn is_integer(&self) -> bool {
129 self.is_signed_integer() || self.is_unsigned_integer()
130 }
131
132 pub fn is_floating_point(&self) -> bool {
133 match self {
134 Type::Option(inner) => inner.is_floating_point(),
135 _ => matches!(self, Type::Float4 | Type::Float8),
136 }
137 }
138
139 pub fn is_utf8(&self) -> bool {
140 match self {
141 Type::Option(inner) => inner.is_utf8(),
142 _ => matches!(self, Type::Utf8),
143 }
144 }
145
146 pub fn is_temporal(&self) -> bool {
147 match self {
148 Type::Option(inner) => inner.is_temporal(),
149 _ => matches!(self, Type::Date | Type::DateTime | Type::Time | Type::Duration),
150 }
151 }
152
153 pub fn is_uuid(&self) -> bool {
154 match self {
155 Type::Option(inner) => inner.is_uuid(),
156 _ => matches!(self, Type::Uuid4 | Type::Uuid7),
157 }
158 }
159
160 pub fn is_blob(&self) -> bool {
161 match self {
162 Type::Option(inner) => inner.is_blob(),
163 _ => matches!(self, Type::Blob),
164 }
165 }
166
167 pub fn is_option(&self) -> bool {
168 matches!(self, Type::Option(_))
169 }
170
171 pub fn inner_type(&self) -> &Type {
173 match self {
174 Type::Option(inner) => inner,
175 other => other,
176 }
177 }
178}
179
180impl Type {
181 pub fn to_u8(&self) -> u8 {
182 match self {
183 Type::Option(inner) => 0x80 | inner.to_u8(),
184 Type::Float4 => 1,
185 Type::Float8 => 2,
186 Type::Int1 => 3,
187 Type::Int2 => 4,
188 Type::Int4 => 5,
189 Type::Int8 => 6,
190 Type::Int16 => 7,
191 Type::Utf8 => 8,
192 Type::Uint1 => 9,
193 Type::Uint2 => 10,
194 Type::Uint4 => 11,
195 Type::Uint8 => 12,
196 Type::Uint16 => 13,
197 Type::Boolean => 14,
198 Type::Date => 15,
199 Type::DateTime => 16,
200 Type::Time => 17,
201 Type::Duration => 18,
202 Type::IdentityId => 19,
203 Type::Uuid4 => 20,
204 Type::Uuid7 => 21,
205 Type::Blob => 22,
206 Type::Int => 23,
207 Type::Decimal {
208 ..
209 } => 24,
210 Type::Uint => 25,
211 Type::Any => 26,
212 Type::DictionaryId => 27,
213 Type::List(_) => 28,
214 }
215 }
216}
217
218impl Type {
219 pub fn from_u8(value: u8) -> Self {
220 if value & 0x80 != 0 {
221 return Type::Option(Box::new(Type::from_u8(value & 0x7F)));
222 }
223 match value {
224 1 => Type::Float4,
225 2 => Type::Float8,
226 3 => Type::Int1,
227 4 => Type::Int2,
228 5 => Type::Int4,
229 6 => Type::Int8,
230 7 => Type::Int16,
231 8 => Type::Utf8,
232 9 => Type::Uint1,
233 10 => Type::Uint2,
234 11 => Type::Uint4,
235 12 => Type::Uint8,
236 13 => Type::Uint16,
237 14 => Type::Boolean,
238 15 => Type::Date,
239 16 => Type::DateTime,
240 17 => Type::Time,
241 18 => Type::Duration,
242 19 => Type::IdentityId,
243 20 => Type::Uuid4,
244 21 => Type::Uuid7,
245 22 => Type::Blob,
246 23 => Type::Int,
247 24 => Type::Decimal,
248 25 => Type::Uint,
249 26 => Type::Any,
250 27 => Type::DictionaryId,
251 28 => Type::list_of(Type::Any),
252 _ => unreachable!(),
253 }
254 }
255}
256
257impl Type {
258 pub fn size(&self) -> usize {
259 match self {
260 Type::Boolean => 1,
261 Type::Float4 => 4,
262 Type::Float8 => 8,
263 Type::Int1 => 1,
264 Type::Int2 => 2,
265 Type::Int4 => 4,
266 Type::Int8 => 8,
267 Type::Int16 => 16,
268 Type::Utf8 => 8, Type::Uint1 => 1,
270 Type::Uint2 => 2,
271 Type::Uint4 => 4,
272 Type::Uint8 => 8,
273 Type::Uint16 => 16,
274 Type::Date => 4,
275 Type::DateTime => 12, Type::Time => 8,
277 Type::Duration => 16, Type::IdentityId => 16, Type::Uuid4 => 16,
281 Type::Uuid7 => 16,
282 Type::Blob => 8, Type::Int => 16, Type::Uint => 16, Type::Decimal {
288 ..
289 } => 16, Type::Option(inner) => inner.size(), Type::Any => 8, Type::List(_) => 8, Type::DictionaryId => 16, }
297 }
298
299 pub fn alignment(&self) -> usize {
300 match self {
301 Type::Boolean => 1,
302 Type::Float4 => 4,
303 Type::Float8 => 8,
304 Type::Int1 => 1,
305 Type::Int2 => 2,
306 Type::Int4 => 4,
307 Type::Int8 => 8,
308 Type::Int16 => 16,
309 Type::Utf8 => 4, Type::Uint1 => 1,
311 Type::Uint2 => 2,
312 Type::Uint4 => 4,
313 Type::Uint8 => 8,
314 Type::Uint16 => 16,
315 Type::Date => 4,
316 Type::DateTime => 8,
317 Type::Time => 8,
318 Type::Duration => 8,
319 Type::IdentityId => 8, Type::Uuid4 => 8,
321 Type::Uuid7 => 8,
322 Type::Blob => 4, Type::Int => 16, Type::Uint => 16, Type::Decimal {
328 ..
329 } => 16, Type::Option(inner) => inner.alignment(),
332 Type::Any => 8, Type::DictionaryId => 16,
334 Type::List(_) => 8, }
336 }
337}
338
339impl Display for Type {
340 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
341 match self {
342 Type::Boolean => f.write_str("Boolean"),
343 Type::Float4 => f.write_str("Float4"),
344 Type::Float8 => f.write_str("Float8"),
345 Type::Int1 => f.write_str("Int1"),
346 Type::Int2 => f.write_str("Int2"),
347 Type::Int4 => f.write_str("Int4"),
348 Type::Int8 => f.write_str("Int8"),
349 Type::Int16 => f.write_str("Int16"),
350 Type::Utf8 => f.write_str("Utf8"),
351 Type::Uint1 => f.write_str("Uint1"),
352 Type::Uint2 => f.write_str("Uint2"),
353 Type::Uint4 => f.write_str("Uint4"),
354 Type::Uint8 => f.write_str("Uint8"),
355 Type::Uint16 => f.write_str("Uint16"),
356 Type::Date => f.write_str("Date"),
357 Type::DateTime => f.write_str("DateTime"),
358 Type::Time => f.write_str("Time"),
359 Type::Duration => f.write_str("Duration"),
360 Type::IdentityId => f.write_str("IdentityId"),
361 Type::Uuid4 => f.write_str("Uuid4"),
362 Type::Uuid7 => f.write_str("Uuid7"),
363 Type::Blob => f.write_str("Blob"),
364 Type::Int => f.write_str("Int"),
365 Type::Uint => f.write_str("Uint"),
366 Type::Decimal => f.write_str("Decimal"),
367 Type::Option(inner) => write!(f, "Option({inner})"),
368 Type::Any => f.write_str("Any"),
369 Type::DictionaryId => f.write_str("DictionaryId"),
370 Type::List(inner) => write!(f, "List({inner})"),
371 }
372 }
373}
374
375impl From<&Value> for Type {
376 fn from(value: &Value) -> Self {
377 match value {
378 Value::None {
379 inner,
380 } => Type::Option(Box::new(inner.clone())),
381 Value::Boolean(_) => Type::Boolean,
382 Value::Float4(_) => Type::Float4,
383 Value::Float8(_) => Type::Float8,
384 Value::Int1(_) => Type::Int1,
385 Value::Int2(_) => Type::Int2,
386 Value::Int4(_) => Type::Int4,
387 Value::Int8(_) => Type::Int8,
388 Value::Int16(_) => Type::Int16,
389 Value::Utf8(_) => Type::Utf8,
390 Value::Uint1(_) => Type::Uint1,
391 Value::Uint2(_) => Type::Uint2,
392 Value::Uint4(_) => Type::Uint4,
393 Value::Uint8(_) => Type::Uint8,
394 Value::Uint16(_) => Type::Uint16,
395 Value::Date(_) => Type::Date,
396 Value::DateTime(_) => Type::DateTime,
397 Value::Time(_) => Type::Time,
398 Value::Duration(_) => Type::Duration,
399 Value::IdentityId(_) => Type::IdentityId,
400 Value::Uuid4(_) => Type::Uuid4,
401 Value::Uuid7(_) => Type::Uuid7,
402 Value::Blob(_) => Type::Blob,
403 Value::Int(_) => Type::Int,
404 Value::Uint(_) => Type::Uint,
405 Value::Decimal(_) => Type::Decimal,
406 Value::Any(_) => Type::Any,
407 Value::DictionaryId(_) => Type::DictionaryId,
408 Value::Type(t) => t.clone(),
409 Value::List(items) => {
410 let element_type = items.first().map(|v| Type::from(v)).unwrap_or(Type::Any);
411 Type::list_of(element_type)
412 }
413 }
414 }
415}
416
417impl FromStr for Type {
418 type Err = ();
419
420 fn from_str(s: &str) -> Result<Self, Self::Err> {
421 let upper = s.to_uppercase();
422 if upper.starts_with("OPTION<") && upper.ends_with('>') {
424 let inner_str = &s[7..s.len() - 1]; let inner = Type::from_str(inner_str)?;
426 return Ok(Type::Option(Box::new(inner)));
427 }
428 match upper.as_str() {
429 "BOOL" | "BOOLEAN" => Ok(Type::Boolean),
430 "FLOAT4" => Ok(Type::Float4),
431 "FLOAT8" => Ok(Type::Float8),
432 "INT1" => Ok(Type::Int1),
433 "INT2" => Ok(Type::Int2),
434 "INT4" => Ok(Type::Int4),
435 "INT8" => Ok(Type::Int8),
436 "INT16" => Ok(Type::Int16),
437 "UTF8" | "TEXT" => Ok(Type::Utf8),
438 "UINT1" => Ok(Type::Uint1),
439 "UINT2" => Ok(Type::Uint2),
440 "UINT4" => Ok(Type::Uint4),
441 "UINT8" => Ok(Type::Uint8),
442 "UINT16" => Ok(Type::Uint16),
443 "DATE" => Ok(Type::Date),
444 "DATETIME" => Ok(Type::DateTime),
445 "TIME" => Ok(Type::Time),
446 "DURATION" | "INTERVAL" => Ok(Type::Duration),
447 "IDENTITYID" | "IDENTITY_ID" => Ok(Type::IdentityId),
448 "UUID4" => Ok(Type::Uuid4),
449 "UUID7" => Ok(Type::Uuid7),
450 "BLOB" => Ok(Type::Blob),
451 "INT" => Ok(Type::Int),
452 "UINT" => Ok(Type::Uint),
453 "DECIMAL" => Ok(Type::Decimal),
454 "ANY" => Ok(Type::Any),
455 "DICTIONARYID" | "DICTIONARY_ID" => Ok(Type::DictionaryId),
456 _ => Err(()),
457 }
458 }
459}