1use serde::{Deserialize, Serialize};
7use std::fmt;
8
9#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
14pub enum LogicalType {
15 Any,
17
18 Null,
20
21 Bool,
23
24 Int8,
26
27 Int16,
29
30 Int32,
32
33 Int64,
35
36 Float32,
38
39 Float64,
41
42 String,
44
45 Bytes,
47
48 Date,
50
51 Time,
53
54 Timestamp,
56
57 Duration,
59
60 ZonedTime,
62
63 ZonedDatetime,
65
66 List(Box<LogicalType>),
68
69 Map {
71 key: Box<LogicalType>,
73 value: Box<LogicalType>,
75 },
76
77 Struct(Vec<(String, LogicalType)>),
79
80 Node,
82
83 Edge,
85
86 Path,
88
89 Vector(usize),
94}
95
96impl LogicalType {
97 #[must_use]
99 pub const fn is_numeric(&self) -> bool {
100 matches!(
101 self,
102 LogicalType::Int8
103 | LogicalType::Int16
104 | LogicalType::Int32
105 | LogicalType::Int64
106 | LogicalType::Float32
107 | LogicalType::Float64
108 )
109 }
110
111 #[must_use]
113 pub const fn is_integer(&self) -> bool {
114 matches!(
115 self,
116 LogicalType::Int8 | LogicalType::Int16 | LogicalType::Int32 | LogicalType::Int64
117 )
118 }
119
120 #[must_use]
122 pub const fn is_float(&self) -> bool {
123 matches!(self, LogicalType::Float32 | LogicalType::Float64)
124 }
125
126 #[must_use]
128 pub const fn is_temporal(&self) -> bool {
129 matches!(
130 self,
131 LogicalType::Date
132 | LogicalType::Time
133 | LogicalType::Timestamp
134 | LogicalType::Duration
135 | LogicalType::ZonedTime
136 | LogicalType::ZonedDatetime
137 )
138 }
139
140 #[must_use]
142 pub const fn is_graph_element(&self) -> bool {
143 matches!(
144 self,
145 LogicalType::Node | LogicalType::Edge | LogicalType::Path
146 )
147 }
148
149 #[must_use]
153 pub const fn is_nullable(&self) -> bool {
154 true
155 }
156
157 #[must_use]
159 pub fn list_element_type(&self) -> Option<&LogicalType> {
160 match self {
161 LogicalType::List(elem) => Some(elem),
162 _ => None,
163 }
164 }
165
166 #[must_use]
168 pub const fn is_vector(&self) -> bool {
169 matches!(self, LogicalType::Vector(_))
170 }
171
172 #[must_use]
174 pub const fn vector_dimensions(&self) -> Option<usize> {
175 match self {
176 LogicalType::Vector(dim) => Some(*dim),
177 _ => None,
178 }
179 }
180
181 #[must_use]
183 pub fn can_coerce_from(&self, other: &LogicalType) -> bool {
184 if self == other {
185 return true;
186 }
187
188 if matches!(self, LogicalType::Any) {
190 return true;
191 }
192
193 if matches!(other, LogicalType::Null) && self.is_nullable() {
195 return true;
196 }
197
198 match (other, self) {
200 (LogicalType::Int8, LogicalType::Int16 | LogicalType::Int32 | LogicalType::Int64) => {
201 true
202 }
203 (LogicalType::Int16, LogicalType::Int32 | LogicalType::Int64) => true,
204 (LogicalType::Int32, LogicalType::Int64) => true,
205 (LogicalType::Float32, LogicalType::Float64) => true,
206 (
208 LogicalType::Int8 | LogicalType::Int16 | LogicalType::Int32,
209 LogicalType::Float32 | LogicalType::Float64,
210 ) => true,
211 (LogicalType::Int64, LogicalType::Float64) => true,
212 (LogicalType::Time, LogicalType::ZonedTime) => true,
214 (LogicalType::Timestamp, LogicalType::ZonedDatetime) => true,
215 _ => false,
216 }
217 }
218
219 #[must_use]
221 pub fn common_type(&self, other: &LogicalType) -> Option<LogicalType> {
222 if self == other {
223 return Some(self.clone());
224 }
225
226 if matches!(self, LogicalType::Any) {
228 return Some(other.clone());
229 }
230 if matches!(other, LogicalType::Any) {
231 return Some(self.clone());
232 }
233
234 if matches!(self, LogicalType::Null) {
236 return Some(other.clone());
237 }
238 if matches!(other, LogicalType::Null) {
239 return Some(self.clone());
240 }
241
242 if self.is_numeric() && other.is_numeric() {
244 if self.is_float() || other.is_float() {
246 return Some(LogicalType::Float64);
247 }
248 return Some(LogicalType::Int64);
250 }
251
252 match (self, other) {
254 (LogicalType::Time, LogicalType::ZonedTime)
255 | (LogicalType::ZonedTime, LogicalType::Time) => {
256 return Some(LogicalType::ZonedTime);
257 }
258 (LogicalType::Timestamp, LogicalType::ZonedDatetime)
259 | (LogicalType::ZonedDatetime, LogicalType::Timestamp) => {
260 return Some(LogicalType::ZonedDatetime);
261 }
262 _ => {}
263 }
264
265 None
266 }
267}
268
269impl fmt::Display for LogicalType {
270 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
271 match self {
272 LogicalType::Any => write!(f, "ANY"),
273 LogicalType::Null => write!(f, "NULL"),
274 LogicalType::Bool => write!(f, "BOOL"),
275 LogicalType::Int8 => write!(f, "INT8"),
276 LogicalType::Int16 => write!(f, "INT16"),
277 LogicalType::Int32 => write!(f, "INT32"),
278 LogicalType::Int64 => write!(f, "INT64"),
279 LogicalType::Float32 => write!(f, "FLOAT32"),
280 LogicalType::Float64 => write!(f, "FLOAT64"),
281 LogicalType::String => write!(f, "STRING"),
282 LogicalType::Bytes => write!(f, "BYTES"),
283 LogicalType::Date => write!(f, "DATE"),
284 LogicalType::Time => write!(f, "TIME"),
285 LogicalType::Timestamp => write!(f, "TIMESTAMP"),
286 LogicalType::Duration => write!(f, "DURATION"),
287 LogicalType::ZonedTime => write!(f, "ZONED TIME"),
288 LogicalType::ZonedDatetime => write!(f, "ZONED DATETIME"),
289 LogicalType::List(elem) => write!(f, "LIST<{elem}>"),
290 LogicalType::Map { key, value } => write!(f, "MAP<{key}, {value}>"),
291 LogicalType::Struct(fields) => {
292 write!(f, "STRUCT<")?;
293 for (i, (name, ty)) in fields.iter().enumerate() {
294 if i > 0 {
295 write!(f, ", ")?;
296 }
297 write!(f, "{name}: {ty}")?;
298 }
299 write!(f, ">")
300 }
301 LogicalType::Node => write!(f, "NODE"),
302 LogicalType::Edge => write!(f, "EDGE"),
303 LogicalType::Path => write!(f, "PATH"),
304 LogicalType::Vector(dim) => write!(f, "VECTOR({dim})"),
305 }
306 }
307}
308
309impl Default for LogicalType {
310 fn default() -> Self {
311 LogicalType::Any
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
320 fn test_numeric_checks() {
321 assert!(LogicalType::Int64.is_numeric());
322 assert!(LogicalType::Float64.is_numeric());
323 assert!(!LogicalType::String.is_numeric());
324
325 assert!(LogicalType::Int64.is_integer());
326 assert!(!LogicalType::Float64.is_integer());
327
328 assert!(LogicalType::Float64.is_float());
329 assert!(!LogicalType::Int64.is_float());
330 }
331
332 #[test]
333 fn test_coercion() {
334 assert!(LogicalType::Int64.can_coerce_from(&LogicalType::Int64));
336
337 assert!(LogicalType::Int64.can_coerce_from(&LogicalType::Null));
339 assert!(LogicalType::String.can_coerce_from(&LogicalType::Null));
340
341 assert!(LogicalType::Int64.can_coerce_from(&LogicalType::Int32));
343 assert!(LogicalType::Int32.can_coerce_from(&LogicalType::Int16));
344 assert!(!LogicalType::Int32.can_coerce_from(&LogicalType::Int64));
345
346 assert!(LogicalType::Float64.can_coerce_from(&LogicalType::Float32));
348
349 assert!(LogicalType::Float64.can_coerce_from(&LogicalType::Int64));
351 assert!(LogicalType::Float32.can_coerce_from(&LogicalType::Int32));
352 }
353
354 #[test]
355 fn test_common_type() {
356 assert_eq!(
358 LogicalType::Int64.common_type(&LogicalType::Int64),
359 Some(LogicalType::Int64)
360 );
361
362 assert_eq!(
364 LogicalType::Int32.common_type(&LogicalType::Int64),
365 Some(LogicalType::Int64)
366 );
367 assert_eq!(
368 LogicalType::Int64.common_type(&LogicalType::Float64),
369 Some(LogicalType::Float64)
370 );
371
372 assert_eq!(
374 LogicalType::Null.common_type(&LogicalType::String),
375 Some(LogicalType::String)
376 );
377
378 assert_eq!(LogicalType::String.common_type(&LogicalType::Int64), None);
380 }
381
382 #[test]
383 fn test_display() {
384 assert_eq!(LogicalType::Int64.to_string(), "INT64");
385 assert_eq!(
386 LogicalType::List(Box::new(LogicalType::String)).to_string(),
387 "LIST<STRING>"
388 );
389 assert_eq!(
390 LogicalType::Map {
391 key: Box::new(LogicalType::String),
392 value: Box::new(LogicalType::Int64)
393 }
394 .to_string(),
395 "MAP<STRING, INT64>"
396 );
397 }
398
399 #[test]
400 fn test_vector_type() {
401 let v384 = LogicalType::Vector(384);
402 let v768 = LogicalType::Vector(768);
403 let v1536 = LogicalType::Vector(1536);
404
405 assert!(v384.is_vector());
407 assert!(v768.is_vector());
408 assert!(!LogicalType::Float64.is_vector());
409 assert!(!LogicalType::List(Box::new(LogicalType::Float32)).is_vector());
410
411 assert_eq!(v384.vector_dimensions(), Some(384));
413 assert_eq!(v768.vector_dimensions(), Some(768));
414 assert_eq!(v1536.vector_dimensions(), Some(1536));
415 assert_eq!(LogicalType::Float64.vector_dimensions(), None);
416
417 assert_eq!(v384.to_string(), "VECTOR(384)");
419 assert_eq!(v768.to_string(), "VECTOR(768)");
420 assert_eq!(v1536.to_string(), "VECTOR(1536)");
421
422 assert_eq!(LogicalType::Vector(384), LogicalType::Vector(384));
424 assert_ne!(LogicalType::Vector(384), LogicalType::Vector(768));
425
426 assert!(!v384.is_numeric());
428 assert!(!v384.is_integer());
429 assert!(!v384.is_float());
430 }
431}