1use serde::{Deserialize, Serialize};
7use std::fmt;
8
9#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
14#[non_exhaustive]
15pub enum LogicalType {
16 Any,
18
19 Null,
21
22 Bool,
24
25 Int8,
27
28 Int16,
30
31 Int32,
33
34 Int64,
36
37 Float32,
39
40 Float64,
42
43 String,
45
46 Bytes,
48
49 Date,
51
52 Time,
54
55 Timestamp,
57
58 Duration,
60
61 ZonedTime,
63
64 ZonedDatetime,
66
67 List(Box<LogicalType>),
69
70 Map {
72 key: Box<LogicalType>,
74 value: Box<LogicalType>,
76 },
77
78 Struct(Vec<(String, LogicalType)>),
80
81 Node,
83
84 Edge,
86
87 Path,
89
90 Vector(usize),
95}
96
97impl LogicalType {
98 #[must_use]
100 pub const fn is_numeric(&self) -> bool {
101 matches!(
102 self,
103 LogicalType::Int8
104 | LogicalType::Int16
105 | LogicalType::Int32
106 | LogicalType::Int64
107 | LogicalType::Float32
108 | LogicalType::Float64
109 )
110 }
111
112 #[must_use]
114 pub const fn is_integer(&self) -> bool {
115 matches!(
116 self,
117 LogicalType::Int8 | LogicalType::Int16 | LogicalType::Int32 | LogicalType::Int64
118 )
119 }
120
121 #[must_use]
123 pub const fn is_float(&self) -> bool {
124 matches!(self, LogicalType::Float32 | LogicalType::Float64)
125 }
126
127 #[must_use]
129 pub const fn is_temporal(&self) -> bool {
130 matches!(
131 self,
132 LogicalType::Date
133 | LogicalType::Time
134 | LogicalType::Timestamp
135 | LogicalType::Duration
136 | LogicalType::ZonedTime
137 | LogicalType::ZonedDatetime
138 )
139 }
140
141 #[must_use]
143 pub const fn is_graph_element(&self) -> bool {
144 matches!(
145 self,
146 LogicalType::Node | LogicalType::Edge | LogicalType::Path
147 )
148 }
149
150 #[must_use]
154 pub const fn is_nullable(&self) -> bool {
155 true
156 }
157
158 #[must_use]
160 pub fn list_element_type(&self) -> Option<&LogicalType> {
161 match self {
162 LogicalType::List(elem) => Some(elem),
163 _ => None,
164 }
165 }
166
167 #[must_use]
169 pub const fn is_vector(&self) -> bool {
170 matches!(self, LogicalType::Vector(_))
171 }
172
173 #[must_use]
175 pub const fn vector_dimensions(&self) -> Option<usize> {
176 match self {
177 LogicalType::Vector(dim) => Some(*dim),
178 _ => None,
179 }
180 }
181
182 #[must_use]
184 pub fn can_coerce_from(&self, other: &LogicalType) -> bool {
185 if self == other {
186 return true;
187 }
188
189 if matches!(self, LogicalType::Any) {
191 return true;
192 }
193
194 if matches!(other, LogicalType::Null) && self.is_nullable() {
196 return true;
197 }
198
199 match (other, self) {
201 (LogicalType::Int8, LogicalType::Int16 | LogicalType::Int32 | LogicalType::Int64) => {
202 true
203 }
204 (LogicalType::Int16, LogicalType::Int32 | LogicalType::Int64) => true,
205 (LogicalType::Int32, LogicalType::Int64) => true,
206 (LogicalType::Float32, LogicalType::Float64) => true,
207 (
209 LogicalType::Int8 | LogicalType::Int16 | LogicalType::Int32,
210 LogicalType::Float32 | LogicalType::Float64,
211 ) => true,
212 (LogicalType::Int64, LogicalType::Float64) => true,
213 (LogicalType::Time, LogicalType::ZonedTime) => true,
215 (LogicalType::Timestamp, LogicalType::ZonedDatetime) => true,
216 _ => false,
217 }
218 }
219
220 #[must_use]
222 pub fn common_type(&self, other: &LogicalType) -> Option<LogicalType> {
223 if self == other {
224 return Some(self.clone());
225 }
226
227 if matches!(self, LogicalType::Any) {
229 return Some(other.clone());
230 }
231 if matches!(other, LogicalType::Any) {
232 return Some(self.clone());
233 }
234
235 if matches!(self, LogicalType::Null) {
237 return Some(other.clone());
238 }
239 if matches!(other, LogicalType::Null) {
240 return Some(self.clone());
241 }
242
243 if self.is_numeric() && other.is_numeric() {
245 if self.is_float() || other.is_float() {
247 return Some(LogicalType::Float64);
248 }
249 return Some(LogicalType::Int64);
251 }
252
253 match (self, other) {
255 (LogicalType::Time, LogicalType::ZonedTime)
256 | (LogicalType::ZonedTime, LogicalType::Time) => {
257 return Some(LogicalType::ZonedTime);
258 }
259 (LogicalType::Timestamp, LogicalType::ZonedDatetime)
260 | (LogicalType::ZonedDatetime, LogicalType::Timestamp) => {
261 return Some(LogicalType::ZonedDatetime);
262 }
263 _ => {}
264 }
265
266 None
267 }
268}
269
270impl fmt::Display for LogicalType {
271 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272 match self {
273 LogicalType::Any => write!(f, "ANY"),
274 LogicalType::Null => write!(f, "NULL"),
275 LogicalType::Bool => write!(f, "BOOL"),
276 LogicalType::Int8 => write!(f, "INT8"),
277 LogicalType::Int16 => write!(f, "INT16"),
278 LogicalType::Int32 => write!(f, "INT32"),
279 LogicalType::Int64 => write!(f, "INT64"),
280 LogicalType::Float32 => write!(f, "FLOAT32"),
281 LogicalType::Float64 => write!(f, "FLOAT64"),
282 LogicalType::String => write!(f, "STRING"),
283 LogicalType::Bytes => write!(f, "BYTES"),
284 LogicalType::Date => write!(f, "DATE"),
285 LogicalType::Time => write!(f, "TIME"),
286 LogicalType::Timestamp => write!(f, "TIMESTAMP"),
287 LogicalType::Duration => write!(f, "DURATION"),
288 LogicalType::ZonedTime => write!(f, "ZONED TIME"),
289 LogicalType::ZonedDatetime => write!(f, "ZONED DATETIME"),
290 LogicalType::List(elem) => write!(f, "LIST<{elem}>"),
291 LogicalType::Map { key, value } => write!(f, "MAP<{key}, {value}>"),
292 LogicalType::Struct(fields) => {
293 write!(f, "STRUCT<")?;
294 for (i, (name, ty)) in fields.iter().enumerate() {
295 if i > 0 {
296 write!(f, ", ")?;
297 }
298 write!(f, "{name}: {ty}")?;
299 }
300 write!(f, ">")
301 }
302 LogicalType::Node => write!(f, "NODE"),
303 LogicalType::Edge => write!(f, "EDGE"),
304 LogicalType::Path => write!(f, "PATH"),
305 LogicalType::Vector(dim) => write!(f, "VECTOR({dim})"),
306 }
307 }
308}
309
310impl Default for LogicalType {
311 fn default() -> Self {
312 LogicalType::Any
313 }
314}
315
316#[cfg(test)]
317mod tests {
318 use super::*;
319
320 #[test]
321 fn test_numeric_checks() {
322 assert!(LogicalType::Int64.is_numeric());
323 assert!(LogicalType::Float64.is_numeric());
324 assert!(!LogicalType::String.is_numeric());
325
326 assert!(LogicalType::Int64.is_integer());
327 assert!(!LogicalType::Float64.is_integer());
328
329 assert!(LogicalType::Float64.is_float());
330 assert!(!LogicalType::Int64.is_float());
331 }
332
333 #[test]
334 fn test_coercion() {
335 assert!(LogicalType::Int64.can_coerce_from(&LogicalType::Int64));
337
338 assert!(LogicalType::Int64.can_coerce_from(&LogicalType::Null));
340 assert!(LogicalType::String.can_coerce_from(&LogicalType::Null));
341
342 assert!(LogicalType::Int64.can_coerce_from(&LogicalType::Int32));
344 assert!(LogicalType::Int32.can_coerce_from(&LogicalType::Int16));
345 assert!(!LogicalType::Int32.can_coerce_from(&LogicalType::Int64));
346
347 assert!(LogicalType::Float64.can_coerce_from(&LogicalType::Float32));
349
350 assert!(LogicalType::Float64.can_coerce_from(&LogicalType::Int64));
352 assert!(LogicalType::Float32.can_coerce_from(&LogicalType::Int32));
353 }
354
355 #[test]
356 fn test_common_type() {
357 assert_eq!(
359 LogicalType::Int64.common_type(&LogicalType::Int64),
360 Some(LogicalType::Int64)
361 );
362
363 assert_eq!(
365 LogicalType::Int32.common_type(&LogicalType::Int64),
366 Some(LogicalType::Int64)
367 );
368 assert_eq!(
369 LogicalType::Int64.common_type(&LogicalType::Float64),
370 Some(LogicalType::Float64)
371 );
372
373 assert_eq!(
375 LogicalType::Null.common_type(&LogicalType::String),
376 Some(LogicalType::String)
377 );
378
379 assert_eq!(LogicalType::String.common_type(&LogicalType::Int64), None);
381 }
382
383 #[test]
384 fn test_display() {
385 assert_eq!(LogicalType::Int64.to_string(), "INT64");
386 assert_eq!(
387 LogicalType::List(Box::new(LogicalType::String)).to_string(),
388 "LIST<STRING>"
389 );
390 assert_eq!(
391 LogicalType::Map {
392 key: Box::new(LogicalType::String),
393 value: Box::new(LogicalType::Int64)
394 }
395 .to_string(),
396 "MAP<STRING, INT64>"
397 );
398 }
399
400 #[test]
401 fn test_vector_type() {
402 let v384 = LogicalType::Vector(384);
403 let v768 = LogicalType::Vector(768);
404 let v1536 = LogicalType::Vector(1536);
405
406 assert!(v384.is_vector());
408 assert!(v768.is_vector());
409 assert!(!LogicalType::Float64.is_vector());
410 assert!(!LogicalType::List(Box::new(LogicalType::Float32)).is_vector());
411
412 assert_eq!(v384.vector_dimensions(), Some(384));
414 assert_eq!(v768.vector_dimensions(), Some(768));
415 assert_eq!(v1536.vector_dimensions(), Some(1536));
416 assert_eq!(LogicalType::Float64.vector_dimensions(), None);
417
418 assert_eq!(v384.to_string(), "VECTOR(384)");
420 assert_eq!(v768.to_string(), "VECTOR(768)");
421 assert_eq!(v1536.to_string(), "VECTOR(1536)");
422
423 assert_eq!(LogicalType::Vector(384), LogicalType::Vector(384));
425 assert_ne!(LogicalType::Vector(384), LogicalType::Vector(768));
426
427 assert!(!v384.is_numeric());
429 assert!(!v384.is_integer());
430 assert!(!v384.is_float());
431 }
432}