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