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