1use super::NativeType;
19use crate::error::Result;
20use arrow::datatypes::DataType;
21use core::fmt;
22use std::{cmp::Ordering, hash::Hash, sync::Arc};
23
24#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
26pub enum TypeSignature<'a> {
27 Native(&'a NativeType),
29 Extension {
34 name: &'a str,
35 parameters: &'a [TypeParameter<'a>],
36 },
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
40pub enum TypeParameter<'a> {
41 Type(TypeSignature<'a>),
42 Number(i128),
43}
44
45pub type LogicalTypeRef = Arc<dyn LogicalType>;
47
48pub trait LogicalType: Sync + Send {
79 fn native(&self) -> &NativeType;
81 fn signature(&self) -> TypeSignature<'_>;
84
85 fn default_cast_for(&self, origin: &DataType) -> Result<DataType> {
88 self.native().default_cast_for(origin)
89 }
90}
91
92impl fmt::Debug for dyn LogicalType {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 f.debug_tuple("LogicalType")
95 .field(&self.signature())
96 .field(&self.native())
97 .finish()
98 }
99}
100
101impl std::fmt::Display for dyn LogicalType {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 match self.signature() {
104 TypeSignature::Native(_) => write!(f, "{}", self.native()),
105 TypeSignature::Extension { name, .. } => write!(f, "{name}"),
106 }
107 }
108}
109
110impl PartialEq for dyn LogicalType {
111 fn eq(&self, other: &Self) -> bool {
112 self.signature().eq(&other.signature())
114 }
115}
116
117impl Eq for dyn LogicalType {}
118
119impl PartialOrd for dyn LogicalType {
120 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
121 Some(self.cmp(other))
122 }
123}
124
125impl Ord for dyn LogicalType {
126 fn cmp(&self, other: &Self) -> Ordering {
127 self.signature().cmp(&other.signature())
129 }
130}
131
132impl Hash for dyn LogicalType {
133 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
134 self.signature().hash(state);
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 use crate::types::{
143 LogicalField, LogicalFields, logical_boolean, logical_date, logical_float32,
144 logical_float64, logical_int32, logical_int64, logical_null, logical_string,
145 };
146 use arrow::datatypes::{Field, Fields};
147 use insta::assert_snapshot;
148
149 #[test]
150 fn test_logical_type_display_simple() {
151 assert_snapshot!(logical_null(), @"Null");
152 assert_snapshot!(logical_boolean(), @"Boolean");
153 assert_snapshot!(logical_int32(), @"Int32");
154 assert_snapshot!(logical_int64(), @"Int64");
155 assert_snapshot!(logical_float32(), @"Float32");
156 assert_snapshot!(logical_float64(), @"Float64");
157 assert_snapshot!(logical_string(), @"String");
158 assert_snapshot!(logical_date(), @"Date");
159 }
160
161 #[test]
162 fn test_logical_type_display_list() {
163 let list_type: Arc<dyn LogicalType> = Arc::new(NativeType::List(Arc::new(
164 LogicalField::from(&Field::new("item", DataType::Int32, true)),
165 )));
166 assert_snapshot!(list_type, @"List(Int32)");
167 }
168
169 #[test]
170 fn test_logical_type_display_struct() {
171 let struct_type: Arc<dyn LogicalType> = Arc::new(NativeType::Struct(
172 LogicalFields::from(&Fields::from(vec![
173 Field::new("x", DataType::Float64, false),
174 Field::new("y", DataType::Float64, true),
175 ])),
176 ));
177 assert_snapshot!(struct_type, @r#"Struct("x": non-null Float64, "y": Float64)"#);
178 }
179
180 #[test]
181 fn test_logical_type_display_fixed_size_list() {
182 let fsl_type: Arc<dyn LogicalType> = Arc::new(NativeType::FixedSizeList(
183 Arc::new(LogicalField::from(&Field::new(
184 "item",
185 DataType::Float32,
186 false,
187 ))),
188 3,
189 ));
190 assert_snapshot!(fsl_type, @"FixedSizeList(3 x non-null Float32)");
191 }
192
193 #[test]
194 fn test_logical_type_display_map() {
195 let map_type: Arc<dyn LogicalType> = Arc::new(NativeType::Map(Arc::new(
196 LogicalField::from(&Field::new("entries", DataType::Utf8, false)),
197 )));
198 assert_snapshot!(map_type, @"Map(non-null String)");
199 }
200
201 #[test]
202 fn test_logical_type_display_union() {
203 use arrow::datatypes::UnionFields;
204
205 let union_fields = UnionFields::try_new(
206 vec![0, 1],
207 vec![
208 Field::new("int_val", DataType::Int32, false),
209 Field::new("str_val", DataType::Utf8, true),
210 ],
211 )
212 .unwrap();
213 let union_type: Arc<dyn LogicalType> = Arc::new(NativeType::Union(
214 crate::types::LogicalUnionFields::from(&union_fields),
215 ));
216 assert_snapshot!(union_type, @r#"Union(0: ("int_val": non-null Int32), 1: ("str_val": String))"#);
217 }
218
219 #[test]
220 fn test_logical_type_display_nullable_vs_non_nullable() {
221 let nullable_list: Arc<dyn LogicalType> = Arc::new(NativeType::List(Arc::new(
222 LogicalField::from(&Field::new("item", DataType::Int32, true)),
223 )));
224 let non_nullable_list: Arc<dyn LogicalType> =
225 Arc::new(NativeType::List(Arc::new(LogicalField::from(&Field::new(
226 "item",
227 DataType::Int32,
228 false,
229 )))));
230
231 assert_snapshot!(nullable_list, @"List(Int32)");
232 assert_snapshot!(non_nullable_list, @"List(non-null Int32)");
233 }
234
235 #[test]
236 fn test_logical_type_display_extension() {
237 struct JsonType;
238 impl LogicalType for JsonType {
239 fn native(&self) -> &NativeType {
240 &NativeType::String
241 }
242 fn signature(&self) -> TypeSignature<'_> {
243 TypeSignature::Extension {
244 name: "JSON",
245 parameters: &[],
246 }
247 }
248 }
249 let json: Arc<dyn LogicalType> = Arc::new(JsonType);
250 assert_snapshot!(json, @"JSON");
251 }
252}