Skip to main content

spark_connect/
types.rs

1//! DataType type system mirroring PySpark's `pyspark.sql.types`.
2//!
3//! Defines the type hierarchy and conversion functions between Python DataTypes
4//! and the Spark Connect protobuf representation.
5
6use std::collections::BTreeMap;
7use std::fmt;
8use std::hash::{Hash, Hasher};
9
10use spark_connect_core::error::{Result, SparkError};
11
12/// The base DataType representation, mirroring `pyspark.sql.types.DataType`.
13///
14/// All concrete types are variants of this enum. Each variant carries the data
15/// needed to fully specify that type (e.g., DecimalType carries precision and scale).
16// Not `Eq` (only `PartialEq`): a Struct field's metadata holds arbitrary JSON values.
17#[derive(Debug, Clone, PartialEq)]
18pub enum DataType {
19    /// `pyspark.sql.types.NullType`
20    Null,
21    /// `pyspark.sql.types.BooleanType`
22    Boolean,
23    /// `pyspark.sql.types.ByteType` (tinyint)
24    Byte,
25    /// `pyspark.sql.types.ShortType` (smallint)
26    Short,
27    /// `pyspark.sql.types.IntegerType` (int)
28    Integer,
29    /// `pyspark.sql.types.LongType` (bigint)
30    Long,
31    /// `pyspark.sql.types.FloatType`
32    Float,
33    /// `pyspark.sql.types.DoubleType`
34    Double,
35    /// `pyspark.sql.types.DecimalType`
36    Decimal { precision: i32, scale: i32 },
37    /// `pyspark.sql.types.StringType`
38    String { collation: String },
39    /// `pyspark.sql.types.CharType`
40    Char { length: i32 },
41    /// `pyspark.sql.types.VarcharType`
42    Varchar { length: i32 },
43    /// `pyspark.sql.types.BinaryType`
44    Binary,
45    /// `pyspark.sql.types.DateType`
46    Date,
47    /// `pyspark.sql.types.TimestampType`
48    Timestamp,
49    /// `pyspark.sql.types.TimestampNTZType`
50    TimestampNtz,
51    /// `pyspark.sql.types.TimeType`
52    Time { precision: i32 },
53    /// `pyspark.sql.types.CalendarIntervalType`
54    CalendarInterval,
55    /// `pyspark.sql.types.YearMonthIntervalType`
56    YearMonthInterval { start_field: i32, end_field: i32 },
57    /// `pyspark.sql.types.DayTimeIntervalType`
58    DayTimeInterval { start_field: i32, end_field: i32 },
59    /// `pyspark.sql.types.ArrayType`
60    Array {
61        element_type: Box<DataType>,
62        contains_null: bool,
63    },
64    /// `pyspark.sql.types.MapType`
65    Map {
66        key_type: Box<DataType>,
67        value_type: Box<DataType>,
68        value_contains_null: bool,
69    },
70    /// `pyspark.sql.types.StructType`
71    Struct { fields: Vec<StructField> },
72    /// `pyspark.sql.types.VariantType`
73    Variant,
74    /// `pyspark.sql.types.GeometryType`
75    Geometry { srid: i32 },
76    /// `pyspark.sql.types.GeographyType`
77    Geography { srid: i32 },
78    /// `pyspark.sql.types.UserDefinedType` (stub)
79    Udt {
80        type_str: String,
81        jvm_class: Option<String>,
82        python_class: Option<String>,
83        serialized_python_class: Option<String>,
84        sql_type: Option<Box<DataType>>,
85    },
86    /// `pyspark.sql.connect.types.UnparsedDataType` - a DDL type string left for
87    /// the server to parse (round-trips through the `unparsed` proto).
88    Unparsed { data_type_string: String },
89}
90
91/// A field in a StructType, mirroring `pyspark.sql.types.StructField`.
92// Not `Eq`: metadata values are arbitrary JSON (`serde_json::Value`, which is only `PartialEq`
93// because of floats), matching pyspark's `Dict[str, Any]` field metadata.
94#[derive(Debug, Clone, PartialEq)]
95pub struct StructField {
96    pub name: String,
97    pub data_type: DataType,
98    pub nullable: bool,
99    pub metadata: BTreeMap<String, serde_json::Value>,
100}
101
102impl DataType {
103    /// Parses a DDL-formatted string into a DataType, mirroring `DataType.fromDDL()`.
104    ///
105    /// This supports:
106    /// - Primitive types: int, bigint, string, double, boolean, date, timestamp, binary,
107    ///   tinyint, smallint, float, decimal(p,s), char(n), varchar(n), interval
108    /// - Complex types: array<...>, map<...,...>, struct<name:type,...>
109    /// - Top-level struct can omit the "struct<>" wrapper for backward compatibility
110    /// - DDL like "a INT, b STRING" is parsed as a struct
111    ///
112    /// Examples:
113    /// ```ignore
114    /// DataType::from_ddl("int") // IntegerType
115    /// DataType::from_ddl("array<string>") // ArrayType(StringType, true)
116    /// DataType::from_ddl("struct<name:string,age:int>") // StructType
117    /// DataType::from_ddl("a INT, b STRING") // Top-level struct
118    /// ```
119    pub fn from_ddl(ddl_str: &str) -> Result<DataType> {
120        parse_datatype_string(ddl_str)
121    }
122
123    /// Returns whether this type needs conversion between Python objects and internal SQL objects.
124    /// This is used to avoid unnecessary conversions for ArrayType/MapType/StructType.
125    ///
126    /// Types that need conversion include:
127    /// - DateType: needs conversion to/from datetime.date
128    /// - TimestampType: needs conversion to/from datetime.datetime
129    /// - TimestampNTZType: needs conversion to/from datetime.datetime (no timezone)
130    /// - TimeType: needs conversion to/from datetime.time
131    /// - DayTimeIntervalType: needs conversion to/from datetime.timedelta
132    /// - CalendarIntervalType: needs conversion
133    /// - YearMonthIntervalType: needs conversion (complex)
134    /// - ArrayType: if element type needs conversion
135    /// - MapType: if key or value type needs conversion
136    /// - StructType: always needs conversion
137    pub fn need_conversion(&self) -> bool {
138        match self {
139            DataType::Date
140            | DataType::Timestamp
141            | DataType::TimestampNtz
142            | DataType::Time { .. }
143            | DataType::DayTimeInterval { .. }
144            | DataType::YearMonthInterval { .. }
145            | DataType::CalendarInterval => true,
146            DataType::Array { element_type, .. } => element_type.need_conversion(),
147            DataType::Map {
148                key_type,
149                value_type,
150                ..
151            } => key_type.need_conversion() || value_type.need_conversion(),
152            DataType::Struct { .. } => true,
153            _ => false,
154        }
155    }
156
157    /// Returns the type name, mirroring `DataType.typeName()`.
158    ///
159    /// For most types, this is the class name with the "Type" suffix removed and lowercased.
160    /// E.g., "ByteType" -> "byte", but NullType -> "void", and special handling for others.
161    pub fn type_name(&self) -> String {
162        match self {
163            DataType::Null => "void".to_string(),
164            DataType::Boolean => "boolean".to_string(),
165            DataType::Byte => "byte".to_string(),
166            DataType::Short => "short".to_string(),
167            DataType::Integer => "integer".to_string(),
168            DataType::Long => "long".to_string(),
169            DataType::Float => "float".to_string(),
170            DataType::Double => "double".to_string(),
171            DataType::Decimal { .. } => "decimal".to_string(),
172            DataType::String { .. } => "string".to_string(),
173            DataType::Char { .. } => "char".to_string(),
174            DataType::Varchar { .. } => "varchar".to_string(),
175            DataType::Binary => "binary".to_string(),
176            DataType::Date => "date".to_string(),
177            DataType::Timestamp => "timestamp".to_string(),
178            DataType::TimestampNtz => "timestamp_ntz".to_string(),
179            DataType::Time { .. } => "time".to_string(),
180            DataType::CalendarInterval => "interval".to_string(),
181            DataType::YearMonthInterval { .. } => "interval".to_string(),
182            DataType::DayTimeInterval { .. } => "interval".to_string(),
183            DataType::Array { .. } => "array".to_string(),
184            DataType::Map { .. } => "map".to_string(),
185            DataType::Struct { .. } => "struct".to_string(),
186            DataType::Variant => "variant".to_string(),
187            DataType::Geometry { .. } => "geometry".to_string(),
188            DataType::Geography { .. } => "geography".to_string(),
189            DataType::Udt { .. } => "udt".to_string(),
190            DataType::Unparsed { .. } => "unparsed".to_string(),
191        }
192    }
193
194    /// Returns the simple string representation, mirroring `DataType.simpleString()`.
195    ///
196    /// For example:
197    /// - "int", "string", "boolean"
198    /// - "decimal(10,0)", "char(50)", "varchar(100)"
199    /// - "array<int>", "map<string,int>", "struct<name:string,age:int>"
200    /// - "interval day to second"
201    pub fn simple_string(&self) -> String {
202        match self {
203            DataType::Null => "void".to_string(),
204            DataType::Boolean => "boolean".to_string(),
205            DataType::Byte => "tinyint".to_string(),
206            DataType::Short => "smallint".to_string(),
207            DataType::Integer => "int".to_string(),
208            DataType::Long => "bigint".to_string(),
209            DataType::Float => "float".to_string(),
210            DataType::Double => "double".to_string(),
211            DataType::Decimal { precision, scale } => {
212                format!("decimal({},{})", precision, scale)
213            }
214            DataType::String { collation } => {
215                // The default collation (empty, or the explicit UTF8_BINARY) renders as
216                // plain "string"; only a non-default collation adds " collate <name>".
217                // Previously an empty collation produced the malformed "string collate ".
218                if collation.is_empty() || collation == "UTF8_BINARY" {
219                    "string".to_string()
220                } else {
221                    format!("string collate {}", collation)
222                }
223            }
224            DataType::Char { length } => format!("char({})", length),
225            DataType::Varchar { length } => format!("varchar({})", length),
226            DataType::Binary => "binary".to_string(),
227            DataType::Date => "date".to_string(),
228            DataType::Timestamp => "timestamp".to_string(),
229            DataType::TimestampNtz => "timestamp_ntz".to_string(),
230            DataType::Time { precision } => format!("time({})", precision),
231            DataType::CalendarInterval => "interval".to_string(),
232            DataType::YearMonthInterval {
233                start_field,
234                end_field,
235            } => interval_string(*start_field, *end_field, YEAR_MONTH_FIELDS),
236            DataType::DayTimeInterval {
237                start_field,
238                end_field,
239            } => interval_string(*start_field, *end_field, DAY_TIME_FIELDS),
240            DataType::Array {
241                element_type,
242                contains_null: _,
243            } => {
244                format!("array<{}>", element_type.simple_string())
245            }
246            DataType::Map {
247                key_type,
248                value_type,
249                value_contains_null: _,
250            } => {
251                format!(
252                    "map<{},{}>",
253                    key_type.simple_string(),
254                    value_type.simple_string()
255                )
256            }
257            DataType::Struct { fields } => {
258                let field_strs: Vec<String> = fields.iter().map(|f| f.simple_string()).collect();
259                format!("struct<{}>", field_strs.join(","))
260            }
261            DataType::Variant => "variant".to_string(),
262            DataType::Geometry { srid } => {
263                if *srid == -1 {
264                    "geometry(any)".to_string()
265                } else {
266                    format!("geometry({})", srid)
267                }
268            }
269            DataType::Geography { srid } => {
270                if *srid == -1 {
271                    "geography(any)".to_string()
272                } else {
273                    format!("geography({})", srid)
274                }
275            }
276            DataType::Udt { .. } => "udt".to_string(),
277            DataType::Unparsed { data_type_string } => {
278                // Round-trips with the `unparsed(...)` branch in `from_json`.
279                format!("unparsed({})", data_type_string)
280            }
281        }
282    }
283
284    /// Returns the JSON value representation, mirroring `DataType.jsonValue()`.
285    ///
286    /// Most simple types return their type name as a string. Complex types
287    /// (Array, Map, Struct) return a dictionary with type and component info.
288    pub fn json_value(&self) -> serde_json::Value {
289        match self {
290            DataType::Null => serde_json::json!("void"),
291            DataType::Boolean => serde_json::json!("boolean"),
292            DataType::Byte => serde_json::json!("byte"),
293            DataType::Short => serde_json::json!("short"),
294            DataType::Integer => serde_json::json!("integer"),
295            DataType::Long => serde_json::json!("long"),
296            DataType::Float => serde_json::json!("float"),
297            DataType::Double => serde_json::json!("double"),
298            DataType::Decimal { precision, scale } => {
299                serde_json::json!(format!("decimal({},{})", precision, scale))
300            }
301            DataType::String { collation } => {
302                if collation == "UTF8_BINARY" {
303                    serde_json::json!("string")
304                } else {
305                    serde_json::json!(format!("string collate {}", collation))
306                }
307            }
308            DataType::Char { length } => {
309                serde_json::json!(format!("char({})", length))
310            }
311            DataType::Varchar { length } => {
312                serde_json::json!(format!("varchar({})", length))
313            }
314            DataType::Binary => serde_json::json!("binary"),
315            DataType::Date => serde_json::json!("date"),
316            DataType::Timestamp => serde_json::json!("timestamp"),
317            DataType::TimestampNtz => serde_json::json!("timestamp_ntz"),
318            DataType::Time { precision } => {
319                serde_json::json!(format!("time({})", precision))
320            }
321            DataType::CalendarInterval => serde_json::json!("interval"),
322            DataType::YearMonthInterval {
323                start_field,
324                end_field,
325            } => {
326                serde_json::json!(interval_string(*start_field, *end_field, YEAR_MONTH_FIELDS))
327            }
328            DataType::DayTimeInterval {
329                start_field,
330                end_field,
331            } => {
332                serde_json::json!(interval_string(*start_field, *end_field, DAY_TIME_FIELDS))
333            }
334            DataType::Array {
335                element_type,
336                contains_null,
337            } => {
338                serde_json::json!({
339                    "type": "array",
340                    "elementType": element_type.json_value(),
341                    "containsNull": contains_null,
342                })
343            }
344            DataType::Map {
345                key_type,
346                value_type,
347                value_contains_null,
348            } => {
349                serde_json::json!({
350                    "type": "map",
351                    "keyType": key_type.json_value(),
352                    "valueType": value_type.json_value(),
353                    "valueContainsNull": value_contains_null,
354                })
355            }
356            DataType::Struct { fields } => {
357                let field_values: Vec<serde_json::Value> =
358                    fields.iter().map(|f| f.json_value()).collect();
359                serde_json::json!({
360                    "type": "struct",
361                    "fields": field_values,
362                })
363            }
364            DataType::Variant => serde_json::json!("variant"),
365            DataType::Geometry { srid } => {
366                if *srid == -1 {
367                    serde_json::json!("geometry(SRID:ANY)")
368                } else {
369                    serde_json::json!(format!("geometry(OGC:CRS84)"))
370                }
371            }
372            DataType::Geography { srid } => {
373                if *srid == -1 {
374                    serde_json::json!("geography(SRID:ANY, SPHERICAL)")
375                } else {
376                    serde_json::json!(format!("geography(OGC:CRS84, SPHERICAL)"))
377                }
378            }
379            DataType::Udt {
380                type_str: _,
381                jvm_class,
382                python_class,
383                serialized_python_class,
384                sql_type,
385            } => {
386                let mut obj = serde_json::Map::new();
387                obj.insert("type".to_string(), serde_json::json!("udt"));
388                if let Some(jvm_cls) = jvm_class {
389                    obj.insert("class".to_string(), serde_json::json!(jvm_cls));
390                }
391                if let Some(py_cls) = python_class {
392                    obj.insert("pyClass".to_string(), serde_json::json!(py_cls));
393                }
394                if let Some(serialized) = serialized_python_class {
395                    obj.insert("serializedClass".to_string(), serde_json::json!(serialized));
396                }
397                if let Some(sql_ty) = sql_type {
398                    obj.insert("sqlType".to_string(), sql_ty.json_value());
399                }
400                serde_json::Value::Object(obj)
401            }
402            DataType::Unparsed { data_type_string } => {
403                serde_json::json!(format!("unparsed({})", data_type_string))
404            }
405        }
406    }
407
408    /// Converts to a JSON string, mirroring `DataType.json()`.
409    pub fn json(&self) -> String {
410        self.json_value().to_string()
411    }
412
413    /// Parses a JSON value into a DataType, mirroring the reverse of `json()` / `jsonValue()`.
414    pub fn from_json(value: &serde_json::Value) -> Result<DataType> {
415        parse_json_value(value, None)
416    }
417
418    /// Parses a JSON string into a DataType (convenience over [`from_json`]).
419    pub fn from_json_str(s: &str) -> Result<DataType> {
420        let value: serde_json::Value = serde_json::from_str(s)
421            .map_err(|e| SparkError::value("INVALID_JSON", &[("detail", &e.to_string())]))?;
422        DataType::from_json(&value)
423    }
424
425    /// Converts to a protobuf DataType, mirroring
426    /// `pyspark.sql.connect.types.pyspark_types_to_proto_types`.
427    pub fn to_proto(&self) -> spark_connect_proto::DataType {
428        let mut proto = spark_connect_proto::DataType::default();
429
430        match self {
431            DataType::Null => {
432                proto.kind = Some(spark_connect_proto::data_type::Kind::Null(
433                    spark_connect_proto::data_type::Null::default(),
434                ));
435            }
436            DataType::Boolean => {
437                proto.kind = Some(spark_connect_proto::data_type::Kind::Boolean(
438                    spark_connect_proto::data_type::Boolean::default(),
439                ));
440            }
441            DataType::Byte => {
442                proto.kind = Some(spark_connect_proto::data_type::Kind::Byte(
443                    spark_connect_proto::data_type::Byte::default(),
444                ));
445            }
446            DataType::Short => {
447                proto.kind = Some(spark_connect_proto::data_type::Kind::Short(
448                    spark_connect_proto::data_type::Short::default(),
449                ));
450            }
451            DataType::Integer => {
452                proto.kind = Some(spark_connect_proto::data_type::Kind::Integer(
453                    spark_connect_proto::data_type::Integer::default(),
454                ));
455            }
456            DataType::Long => {
457                proto.kind = Some(spark_connect_proto::data_type::Kind::Long(
458                    spark_connect_proto::data_type::Long::default(),
459                ));
460            }
461            DataType::Float => {
462                proto.kind = Some(spark_connect_proto::data_type::Kind::Float(
463                    spark_connect_proto::data_type::Float::default(),
464                ));
465            }
466            DataType::Double => {
467                proto.kind = Some(spark_connect_proto::data_type::Kind::Double(
468                    spark_connect_proto::data_type::Double::default(),
469                ));
470            }
471            DataType::Decimal { precision, scale } => {
472                let mut decimal = spark_connect_proto::data_type::Decimal::default();
473                decimal.precision = Some(*precision);
474                decimal.scale = Some(*scale);
475                proto.kind = Some(spark_connect_proto::data_type::Kind::Decimal(decimal));
476            }
477            DataType::String { collation } => {
478                let mut string = spark_connect_proto::data_type::String::default();
479                string.collation = collation.clone();
480                proto.kind = Some(spark_connect_proto::data_type::Kind::String(string));
481            }
482            DataType::Char { length } => {
483                let mut char_type = spark_connect_proto::data_type::Char::default();
484                char_type.length = *length;
485                proto.kind = Some(spark_connect_proto::data_type::Kind::Char(char_type));
486            }
487            DataType::Varchar { length } => {
488                let mut varchar_type = spark_connect_proto::data_type::VarChar::default();
489                varchar_type.length = *length;
490                proto.kind = Some(spark_connect_proto::data_type::Kind::VarChar(varchar_type));
491            }
492            DataType::Binary => {
493                proto.kind = Some(spark_connect_proto::data_type::Kind::Binary(
494                    spark_connect_proto::data_type::Binary::default(),
495                ));
496            }
497            DataType::Date => {
498                proto.kind = Some(spark_connect_proto::data_type::Kind::Date(
499                    spark_connect_proto::data_type::Date::default(),
500                ));
501            }
502            DataType::Timestamp => {
503                proto.kind = Some(spark_connect_proto::data_type::Kind::Timestamp(
504                    spark_connect_proto::data_type::Timestamp::default(),
505                ));
506            }
507            DataType::TimestampNtz => {
508                proto.kind = Some(spark_connect_proto::data_type::Kind::TimestampNtz(
509                    spark_connect_proto::data_type::TimestampNtz::default(),
510                ));
511            }
512            DataType::Time { precision } => {
513                let mut time_type = spark_connect_proto::data_type::Time::default();
514                time_type.precision = Some(*precision);
515                proto.kind = Some(spark_connect_proto::data_type::Kind::Time(time_type));
516            }
517            DataType::CalendarInterval => {
518                proto.kind = Some(spark_connect_proto::data_type::Kind::CalendarInterval(
519                    spark_connect_proto::data_type::CalendarInterval::default(),
520                ));
521            }
522            DataType::YearMonthInterval {
523                start_field,
524                end_field,
525            } => {
526                let mut ymi = spark_connect_proto::data_type::YearMonthInterval::default();
527                ymi.start_field = Some(*start_field);
528                ymi.end_field = Some(*end_field);
529                proto.kind = Some(spark_connect_proto::data_type::Kind::YearMonthInterval(ymi));
530            }
531            DataType::DayTimeInterval {
532                start_field,
533                end_field,
534            } => {
535                let mut dti = spark_connect_proto::data_type::DayTimeInterval::default();
536                dti.start_field = Some(*start_field);
537                dti.end_field = Some(*end_field);
538                proto.kind = Some(spark_connect_proto::data_type::Kind::DayTimeInterval(dti));
539            }
540            DataType::Array {
541                element_type,
542                contains_null,
543            } => {
544                let mut array = spark_connect_proto::data_type::Array::default();
545                array.element_type = Some(Box::new(element_type.to_proto()));
546                array.contains_null = *contains_null;
547                proto.kind = Some(spark_connect_proto::data_type::Kind::Array(Box::new(array)));
548            }
549            DataType::Map {
550                key_type,
551                value_type,
552                value_contains_null,
553            } => {
554                let mut map = spark_connect_proto::data_type::Map::default();
555                map.key_type = Some(Box::new(key_type.to_proto()));
556                map.value_type = Some(Box::new(value_type.to_proto()));
557                map.value_contains_null = *value_contains_null;
558                proto.kind = Some(spark_connect_proto::data_type::Kind::Map(Box::new(map)));
559            }
560            DataType::Struct { fields } => {
561                let mut struct_type = spark_connect_proto::data_type::Struct::default();
562                for field in fields {
563                    let mut proto_field = spark_connect_proto::data_type::StructField::default();
564                    proto_field.name = field.name.clone();
565                    proto_field.data_type = Some(field.data_type.to_proto());
566                    proto_field.nullable = field.nullable;
567                    if !field.metadata.is_empty() {
568                        proto_field.metadata =
569                            Some(serde_json::to_string(&field.metadata).unwrap_or_default());
570                    }
571                    struct_type.fields.push(proto_field);
572                }
573                proto.kind = Some(spark_connect_proto::data_type::Kind::Struct(struct_type));
574            }
575            DataType::Variant => {
576                proto.kind = Some(spark_connect_proto::data_type::Kind::Variant(
577                    spark_connect_proto::data_type::Variant::default(),
578                ));
579            }
580            DataType::Geometry { srid } => {
581                let mut geometry = spark_connect_proto::data_type::Geometry::default();
582                geometry.srid = *srid;
583                proto.kind = Some(spark_connect_proto::data_type::Kind::Geometry(geometry));
584            }
585            DataType::Geography { srid } => {
586                let mut geography = spark_connect_proto::data_type::Geography::default();
587                geography.srid = *srid;
588                proto.kind = Some(spark_connect_proto::data_type::Kind::Geography(geography));
589            }
590            DataType::Udt {
591                type_str: _,
592                jvm_class,
593                python_class,
594                serialized_python_class,
595                sql_type,
596            } => {
597                let mut udt = spark_connect_proto::data_type::Udt::default();
598                udt.r#type = "udt".to_string();
599                if let Some(jvm_cls) = jvm_class {
600                    udt.jvm_class = Some(jvm_cls.clone());
601                }
602                if let Some(py_cls) = python_class {
603                    udt.python_class = Some(py_cls.clone());
604                }
605                if let Some(serialized) = serialized_python_class {
606                    udt.serialized_python_class = Some(serialized.clone());
607                }
608                if let Some(sql_ty) = sql_type {
609                    udt.sql_type = Some(Box::new(sql_ty.to_proto()));
610                }
611                proto.kind = Some(spark_connect_proto::data_type::Kind::Udt(Box::new(udt)));
612            }
613            DataType::Unparsed { data_type_string } => {
614                let mut unparsed = spark_connect_proto::data_type::Unparsed::default();
615                unparsed.data_type_string = data_type_string.clone();
616                proto.kind = Some(spark_connect_proto::data_type::Kind::Unparsed(unparsed));
617            }
618        }
619
620        proto
621    }
622
623    /// Converts from a protobuf DataType, mirroring
624    /// `pyspark.sql.connect.types.proto_schema_to_pyspark_data_type`.
625    pub fn from_proto(proto: &spark_connect_proto::DataType) -> Result<DataType> {
626        match &proto.kind {
627            Some(spark_connect_proto::data_type::Kind::Null(_)) => Ok(DataType::Null),
628            Some(spark_connect_proto::data_type::Kind::Boolean(_)) => Ok(DataType::Boolean),
629            Some(spark_connect_proto::data_type::Kind::Byte(_)) => Ok(DataType::Byte),
630            Some(spark_connect_proto::data_type::Kind::Short(_)) => Ok(DataType::Short),
631            Some(spark_connect_proto::data_type::Kind::Integer(_)) => Ok(DataType::Integer),
632            Some(spark_connect_proto::data_type::Kind::Long(_)) => Ok(DataType::Long),
633            Some(spark_connect_proto::data_type::Kind::Float(_)) => Ok(DataType::Float),
634            Some(spark_connect_proto::data_type::Kind::Double(_)) => Ok(DataType::Double),
635            Some(spark_connect_proto::data_type::Kind::Decimal(d)) => {
636                let precision = d.precision.unwrap_or(10);
637                let scale = d.scale.unwrap_or(0);
638                Ok(DataType::Decimal { precision, scale })
639            }
640            Some(spark_connect_proto::data_type::Kind::String(s)) => {
641                let collation = if s.collation.is_empty() {
642                    "UTF8_BINARY".to_string()
643                } else {
644                    s.collation.clone()
645                };
646                Ok(DataType::String { collation })
647            }
648            Some(spark_connect_proto::data_type::Kind::Char(c)) => {
649                Ok(DataType::Char { length: c.length })
650            }
651            Some(spark_connect_proto::data_type::Kind::VarChar(vc)) => {
652                Ok(DataType::Varchar { length: vc.length })
653            }
654            Some(spark_connect_proto::data_type::Kind::Binary(_)) => Ok(DataType::Binary),
655            Some(spark_connect_proto::data_type::Kind::Date(_)) => Ok(DataType::Date),
656            Some(spark_connect_proto::data_type::Kind::Timestamp(_)) => Ok(DataType::Timestamp),
657            Some(spark_connect_proto::data_type::Kind::TimestampNtz(_)) => {
658                Ok(DataType::TimestampNtz)
659            }
660            Some(spark_connect_proto::data_type::Kind::Time(t)) => {
661                let precision = t.precision.unwrap_or(6);
662                Ok(DataType::Time { precision })
663            }
664            Some(spark_connect_proto::data_type::Kind::CalendarInterval(_)) => {
665                Ok(DataType::CalendarInterval)
666            }
667            Some(spark_connect_proto::data_type::Kind::YearMonthInterval(ymi)) => {
668                let start_field = ymi.start_field.unwrap_or(0);
669                let end_field = ymi.end_field.unwrap_or(1);
670                Ok(DataType::YearMonthInterval {
671                    start_field,
672                    end_field,
673                })
674            }
675            Some(spark_connect_proto::data_type::Kind::DayTimeInterval(dti)) => {
676                let start_field = dti.start_field.unwrap_or(0);
677                let end_field = dti.end_field.unwrap_or(3);
678                Ok(DataType::DayTimeInterval {
679                    start_field,
680                    end_field,
681                })
682            }
683            Some(spark_connect_proto::data_type::Kind::Array(a)) => {
684                let element_type =
685                    Box::new(DataType::from_proto(a.element_type.as_ref().ok_or_else(
686                        || SparkError::connect_msg("Array element_type is missing"),
687                    )?)?);
688                Ok(DataType::Array {
689                    element_type,
690                    contains_null: a.contains_null,
691                })
692            }
693            Some(spark_connect_proto::data_type::Kind::Struct(s)) => {
694                let mut fields = Vec::new();
695                for field_proto in &s.fields {
696                    let data_type =
697                        DataType::from_proto(field_proto.data_type.as_ref().ok_or_else(|| {
698                            SparkError::connect_msg("StructField data_type is missing")
699                        })?)?;
700                    let metadata = if let Some(meta_str) = &field_proto.metadata {
701                        serde_json::from_str(meta_str).unwrap_or_default()
702                    } else {
703                        BTreeMap::new()
704                    };
705                    fields.push(StructField {
706                        name: field_proto.name.clone(),
707                        data_type,
708                        nullable: field_proto.nullable,
709                        metadata,
710                    });
711                }
712                Ok(DataType::Struct { fields })
713            }
714            Some(spark_connect_proto::data_type::Kind::Map(m)) => {
715                let key_type =
716                    Box::new(DataType::from_proto(m.key_type.as_ref().ok_or_else(
717                        || SparkError::connect_msg("Map key_type is missing"),
718                    )?)?);
719                let value_type =
720                    Box::new(DataType::from_proto(m.value_type.as_ref().ok_or_else(
721                        || SparkError::connect_msg("Map value_type is missing"),
722                    )?)?);
723                Ok(DataType::Map {
724                    key_type,
725                    value_type,
726                    value_contains_null: m.value_contains_null,
727                })
728            }
729            Some(spark_connect_proto::data_type::Kind::Variant(_)) => Ok(DataType::Variant),
730            Some(spark_connect_proto::data_type::Kind::Geometry(g)) => {
731                Ok(DataType::Geometry { srid: g.srid })
732            }
733            Some(spark_connect_proto::data_type::Kind::Geography(g)) => {
734                Ok(DataType::Geography { srid: g.srid })
735            }
736            Some(spark_connect_proto::data_type::Kind::Udt(u)) => {
737                let sql_type = if let Some(ref st) = u.sql_type {
738                    Some(Box::new(DataType::from_proto(st)?))
739                } else {
740                    None
741                };
742                Ok(DataType::Udt {
743                    type_str: u.r#type.clone(),
744                    jvm_class: u.jvm_class.clone(),
745                    python_class: u.python_class.clone(),
746                    serialized_python_class: u.serialized_python_class.clone(),
747                    sql_type,
748                })
749            }
750            Some(spark_connect_proto::data_type::Kind::Unparsed(u)) => Ok(DataType::Unparsed {
751                data_type_string: u.data_type_string.clone(),
752            }),
753            Some(spark_connect_proto::data_type::Kind::TimestampNtzNanos(tn)) => {
754                let precision = tn.precision.unwrap_or(9);
755                Ok(DataType::Time { precision })
756            }
757            Some(spark_connect_proto::data_type::Kind::TimestampLtzNanos(_)) => {
758                Ok(DataType::Timestamp)
759            }
760            None => Err(SparkError::connect_msg("DataType kind not set")),
761        }
762    }
763}
764
765impl StructField {
766    /// Returns the simple string representation, mirroring `StructField.simpleString()`.
767    ///
768    /// Format: "name:type"
769    pub fn simple_string(&self) -> String {
770        format!("{}:{}", self.name, self.data_type.simple_string())
771    }
772
773    /// Returns the JSON value representation, mirroring `StructField.jsonValue()`.
774    pub fn json_value(&self) -> serde_json::Value {
775        serde_json::json!({
776            "name": &self.name,
777            "type": self.data_type.json_value(),
778            "nullable": self.nullable,
779            "metadata": self.metadata,
780        })
781    }
782
783    /// Parses a StructField from its JSON string, mirroring `StructField.fromJson`.
784    pub fn from_json_str(s: &str) -> Result<StructField> {
785        let v: serde_json::Value = serde_json::from_str(s)
786            .map_err(|e| SparkError::value("INVALID_JSON", &[("detail", &e.to_string())]))?;
787        let name = v
788            .get("name")
789            .and_then(|x| x.as_str())
790            .ok_or_else(|| SparkError::value("INVALID_JSON", &[("detail", "missing field name")]))?
791            .to_string();
792        let nullable = v.get("nullable").and_then(|x| x.as_bool()).unwrap_or(true);
793        let data_type = DataType::from_json(v.get("type").unwrap_or(&serde_json::Value::Null))?;
794        let mut metadata = BTreeMap::new();
795        if let Some(md) = v.get("metadata").and_then(|x| x.as_object()) {
796            for (k, val) in md {
797                metadata.insert(k.clone(), val.clone());
798            }
799        }
800        Ok(StructField {
801            name,
802            data_type,
803            nullable,
804            metadata,
805        })
806    }
807}
808
809/// Helper methods for StructType operations, mirroring `pyspark.sql.types.StructType`.
810/// Since StructType is represented as `DataType::Struct { fields }`, these methods provide
811/// convenience operations for struct types.
812impl DataType {
813    /// Returns all field names in a StructType, mirroring `StructType.fieldNames()`.
814    ///
815    /// Returns an error if called on a non-Struct type.
816    pub fn field_names(&self) -> Result<Vec<String>> {
817        match self {
818            DataType::Struct { fields } => Ok(fields.iter().map(|f| f.name.clone()).collect()),
819            _ => Err(SparkError::value(
820                "INVALID_TYPE",
821                &[("detail", "fieldNames() can only be called on StructType")],
822            )),
823        }
824    }
825
826    /// Alias for `field_names()`, also mirroring pyspark's `names` attribute.
827    pub fn names(&self) -> Result<Vec<String>> {
828        self.field_names()
829    }
830
831    /// DDL string for a StructType, mirroring `StructType.toDDL()`:
832    /// comma-separated `name type[ NOT NULL][ COMMENT '...']` per field.
833    pub fn to_ddl(&self) -> Result<String> {
834        match self {
835            DataType::Struct { fields } => Ok(fields
836                .iter()
837                .map(|f| {
838                    let mut s = format!("{} {}", f.name, f.data_type.simple_string());
839                    if !f.nullable {
840                        s.push_str(" NOT NULL");
841                    }
842                    if let Some(comment) = f.metadata.get("comment").and_then(|c| c.as_str()) {
843                        s.push_str(&format!(" COMMENT '{}'", comment.replace('\'', "\\'")));
844                    }
845                    s
846                })
847                .collect::<Vec<_>>()
848                .join(",")),
849            _ => Err(SparkError::value(
850                "INVALID_TYPE",
851                &[("detail", "toDDL() can only be called on StructType")],
852            )),
853        }
854    }
855
856    /// Tree-string for a StructType, mirroring `StructType.treeString()`.
857    pub fn tree_string(&self) -> Result<String> {
858        self.tree_string_with_depth(i32::MAX)
859    }
860
861    /// Like [`DataType::tree_string`], but stops recursing into nested structs
862    /// once `max_depth` nesting levels have been printed (top-level fields are
863    /// depth 1). Mirrors `StructType.treeString(maxDepth)`.
864    pub fn tree_string_with_depth(&self, max_depth: i32) -> Result<String> {
865        match self {
866            DataType::Struct { .. } => {
867                let mut out = String::from("root\n");
868                self.append_tree(&mut out, " |", 1, max_depth);
869                Ok(out)
870            }
871            _ => Err(SparkError::value(
872                "INVALID_TYPE",
873                &[("detail", "treeString() can only be called on StructType")],
874            )),
875        }
876    }
877
878    fn append_tree(&self, out: &mut String, prefix: &str, depth: i32, max_depth: i32) {
879        if let DataType::Struct { fields } = self {
880            for f in fields {
881                // A nested struct is rendered as the bare type name ("struct") and its
882                // fields are shown by recursing below, so `max_depth` can truncate them.
883                // Using `simple_string()` here would inline the children (e.g.
884                // "struct<inner:int>"), leaking them past `max_depth`. Non-struct types
885                // have no depth-controlled children, so their simple string is used.
886                let is_struct = matches!(f.data_type, DataType::Struct { .. });
887                let type_repr = if is_struct {
888                    f.data_type.type_name()
889                } else {
890                    f.data_type.simple_string()
891                };
892                out.push_str(&format!(
893                    "{}-- {}: {} (nullable = {})\n",
894                    prefix, f.name, type_repr, f.nullable
895                ));
896                if is_struct && depth < max_depth {
897                    f.data_type.append_tree(
898                        out,
899                        &format!("{}    |", prefix.trim_end_matches('|')),
900                        depth + 1,
901                        max_depth,
902                    );
903                }
904            }
905        }
906    }
907
908    /// Return a copy with every field made nullable (recursively), mirroring
909    /// `StructType.toNullable()`.
910    pub fn to_nullable(&self) -> DataType {
911        match self {
912            DataType::Struct { fields } => DataType::Struct {
913                fields: fields
914                    .iter()
915                    .map(|f| StructField {
916                        name: f.name.clone(),
917                        data_type: f.data_type.to_nullable(),
918                        nullable: true,
919                        metadata: f.metadata.clone(),
920                    })
921                    .collect(),
922            },
923            DataType::Array { element_type, .. } => DataType::Array {
924                element_type: Box::new(element_type.to_nullable()),
925                contains_null: true,
926            },
927            DataType::Map {
928                key_type,
929                value_type,
930                ..
931            } => DataType::Map {
932                key_type: Box::new(key_type.to_nullable()),
933                value_type: Box::new(value_type.to_nullable()),
934                value_contains_null: true,
935            },
936            other => other.clone(),
937        }
938    }
939
940    /// Adds a field to a StructType, mirroring `StructType.add()`.
941    ///
942    /// This is a builder method that returns a new StructType with the field added.
943    /// Returns an error if called on a non-Struct type.
944    ///
945    /// Example:
946    /// ```ignore
947    /// let struct_type = DataType::Struct { fields: vec![] };
948    /// let with_field = struct_type.add(
949    ///     "name",
950    ///     DataType::String { collation: "UTF8_BINARY".to_string() },
951    ///     true,
952    ///     None,
953    /// )?;
954    /// ```
955    pub fn add(
956        &self,
957        field_name: &str,
958        field_type: DataType,
959        nullable: bool,
960        metadata: Option<BTreeMap<String, serde_json::Value>>,
961    ) -> Result<DataType> {
962        match self {
963            DataType::Struct { fields } => {
964                let mut new_fields = fields.clone();
965                new_fields.push(StructField {
966                    name: field_name.to_string(),
967                    data_type: field_type,
968                    nullable,
969                    metadata: metadata.unwrap_or_default(),
970                });
971                Ok(DataType::Struct { fields: new_fields })
972            }
973            _ => Err(SparkError::value(
974                "INVALID_TYPE",
975                &[("detail", "add() can only be called on StructType")],
976            )),
977        }
978    }
979}
980
981impl fmt::Display for DataType {
982    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
983        write!(f, "{}", self.simple_string())
984    }
985}
986
987impl Hash for DataType {
988    fn hash<H: Hasher>(&self, state: &mut H) {
989        self.simple_string().hash(state);
990    }
991}
992
993// Helper constants for interval field mappings
994const DAY_TIME_FIELDS: &[(&str, i32)] = &[("day", 0), ("hour", 1), ("minute", 2), ("second", 3)];
995
996const YEAR_MONTH_FIELDS: &[(&str, i32)] = &[("year", 0), ("month", 1)];
997
998/// Helper to format interval strings
999fn interval_string(start_field: i32, end_field: i32, fields: &[(&str, i32)]) -> String {
1000    let field_map: std::collections::HashMap<i32, &str> =
1001        fields.iter().map(|(name, code)| (*code, *name)).collect();
1002
1003    if let (Some(&start_name), Some(&end_name)) =
1004        (field_map.get(&start_field), field_map.get(&end_field))
1005    {
1006        if start_name == end_name {
1007            format!("interval {}", start_name)
1008        } else {
1009            format!("interval {} to {}", start_name, end_name)
1010        }
1011    } else {
1012        "interval".to_string()
1013    }
1014}
1015
1016/// Parses a JSON value into a DataType
1017fn parse_json_value(value: &serde_json::Value, _field_name: Option<&str>) -> Result<DataType> {
1018    match value {
1019        serde_json::Value::String(s) => parse_json_type_string(s),
1020        serde_json::Value::Object(obj) => parse_json_object(obj),
1021        _ => Err(SparkError::value(
1022            "INVALID_DATATYPE_FORMAT",
1023            &[("detail", "Expected string or object for DataType")],
1024        )),
1025    }
1026}
1027
1028fn parse_json_type_string(s: &str) -> Result<DataType> {
1029    match s {
1030        "void" => Ok(DataType::Null),
1031        "boolean" => Ok(DataType::Boolean),
1032        "byte" => Ok(DataType::Byte),
1033        "short" => Ok(DataType::Short),
1034        "integer" => Ok(DataType::Integer),
1035        "long" => Ok(DataType::Long),
1036        "float" => Ok(DataType::Float),
1037        "double" => Ok(DataType::Double),
1038        "binary" => Ok(DataType::Binary),
1039        "date" => Ok(DataType::Date),
1040        "timestamp" => Ok(DataType::Timestamp),
1041        "timestamp_ntz" => Ok(DataType::TimestampNtz),
1042        "interval" => Ok(DataType::CalendarInterval),
1043        "variant" => Ok(DataType::Variant),
1044        s if s.starts_with("decimal(") => parse_decimal(s),
1045        s if s.starts_with("char(") => parse_char(s),
1046        s if s.starts_with("varchar(") => parse_varchar(s),
1047        s if s.starts_with("time(") => parse_time(s),
1048        s if s.starts_with("interval") => parse_interval_string(s),
1049        s if s.starts_with("string collate") => {
1050            let collation = s.trim_start_matches("string collate").trim().to_string();
1051            Ok(DataType::String { collation })
1052        }
1053        "string" => Ok(DataType::String {
1054            collation: "UTF8_BINARY".to_string(),
1055        }),
1056        s if s.starts_with("geometry(") => parse_geometry(s),
1057        s if s.starts_with("geography(") => parse_geography(s),
1058        s if s.starts_with("unparsed(") => Ok(DataType::Unparsed {
1059            data_type_string: s.to_string(),
1060        }),
1061        _ => Err(SparkError::value(
1062            "INVALID_DATATYPE_STRING",
1063            &[("detail", &format!("Unknown type string: {}", s))],
1064        )),
1065    }
1066}
1067
1068fn parse_json_object(obj: &serde_json::Map<String, serde_json::Value>) -> Result<DataType> {
1069    let type_field = obj.get("type").and_then(|v| v.as_str());
1070
1071    match type_field {
1072        Some("array") => {
1073            let element_type = obj.get("elementType").ok_or_else(|| {
1074                SparkError::value(
1075                    "INVALID_DATATYPE_FORMAT",
1076                    &[("detail", "Array missing elementType")],
1077                )
1078            })?;
1079            let contains_null = obj
1080                .get("containsNull")
1081                .and_then(|v| v.as_bool())
1082                .unwrap_or(true);
1083            Ok(DataType::Array {
1084                element_type: Box::new(parse_json_value(element_type, None)?),
1085                contains_null,
1086            })
1087        }
1088        Some("map") => {
1089            let key_type = obj.get("keyType").ok_or_else(|| {
1090                SparkError::value(
1091                    "INVALID_DATATYPE_FORMAT",
1092                    &[("detail", "Map missing keyType")],
1093                )
1094            })?;
1095            let value_type = obj.get("valueType").ok_or_else(|| {
1096                SparkError::value(
1097                    "INVALID_DATATYPE_FORMAT",
1098                    &[("detail", "Map missing valueType")],
1099                )
1100            })?;
1101            let value_contains_null = obj
1102                .get("valueContainsNull")
1103                .and_then(|v| v.as_bool())
1104                .unwrap_or(true);
1105            Ok(DataType::Map {
1106                key_type: Box::new(parse_json_value(key_type, None)?),
1107                value_type: Box::new(parse_json_value(value_type, None)?),
1108                value_contains_null,
1109            })
1110        }
1111        Some("struct") => {
1112            let fields_arr = obj
1113                .get("fields")
1114                .and_then(|v| v.as_array())
1115                .ok_or_else(|| {
1116                    SparkError::value(
1117                        "INVALID_DATATYPE_FORMAT",
1118                        &[("detail", "Struct missing fields")],
1119                    )
1120                })?;
1121
1122            let mut fields = Vec::new();
1123            for field_obj in fields_arr {
1124                if let Some(field_map) = field_obj.as_object() {
1125                    let name = field_map
1126                        .get("name")
1127                        .and_then(|v| v.as_str())
1128                        .ok_or_else(|| {
1129                            SparkError::value(
1130                                "INVALID_DATATYPE_FORMAT",
1131                                &[("detail", "StructField missing name")],
1132                            )
1133                        })?
1134                        .to_string();
1135                    let field_type = field_map.get("type").ok_or_else(|| {
1136                        SparkError::value(
1137                            "INVALID_DATATYPE_FORMAT",
1138                            &[("detail", "StructField missing type")],
1139                        )
1140                    })?;
1141                    let data_type = parse_json_value(field_type, Some(&name))?;
1142                    let nullable = field_map
1143                        .get("nullable")
1144                        .and_then(|v| v.as_bool())
1145                        .unwrap_or(true);
1146                    let metadata = field_map
1147                        .get("metadata")
1148                        .and_then(|v| v.as_object())
1149                        .map(|m| m.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
1150                        .unwrap_or_default();
1151
1152                    fields.push(StructField {
1153                        name,
1154                        data_type,
1155                        nullable,
1156                        metadata,
1157                    });
1158                }
1159            }
1160            Ok(DataType::Struct { fields })
1161        }
1162        _ => Err(SparkError::value(
1163            "INVALID_DATATYPE_FORMAT",
1164            &[("detail", "Unknown type in object")],
1165        )),
1166    }
1167}
1168
1169fn parse_decimal(s: &str) -> Result<DataType> {
1170    let inner = s
1171        .strip_prefix("decimal(")
1172        .and_then(|s| s.strip_suffix(")"))
1173        .ok_or_else(|| {
1174            SparkError::value(
1175                "INVALID_DATATYPE_FORMAT",
1176                &[("detail", "Invalid decimal format")],
1177            )
1178        })?;
1179
1180    let parts: Vec<&str> = inner.split(',').collect();
1181    if parts.len() != 2 {
1182        return Err(SparkError::value(
1183            "INVALID_DATATYPE_FORMAT",
1184            &[("detail", "Decimal requires precision and scale")],
1185        ));
1186    }
1187
1188    let precision = parts[0].trim().parse::<i32>().map_err(|_| {
1189        SparkError::value(
1190            "INVALID_DATATYPE_FORMAT",
1191            &[("detail", "Invalid precision")],
1192        )
1193    })?;
1194    let scale = parts[1].trim().parse::<i32>().map_err(|_| {
1195        SparkError::value("INVALID_DATATYPE_FORMAT", &[("detail", "Invalid scale")])
1196    })?;
1197
1198    Ok(DataType::Decimal { precision, scale })
1199}
1200
1201fn parse_char(s: &str) -> Result<DataType> {
1202    let inner = s
1203        .strip_prefix("char(")
1204        .and_then(|s| s.strip_suffix(")"))
1205        .ok_or_else(|| {
1206            SparkError::value(
1207                "INVALID_DATATYPE_FORMAT",
1208                &[("detail", "Invalid char format")],
1209            )
1210        })?;
1211
1212    let length = inner.trim().parse::<i32>().map_err(|_| {
1213        SparkError::value(
1214            "INVALID_DATATYPE_FORMAT",
1215            &[("detail", "Invalid char length")],
1216        )
1217    })?;
1218
1219    Ok(DataType::Char { length })
1220}
1221
1222fn parse_varchar(s: &str) -> Result<DataType> {
1223    let inner = s
1224        .strip_prefix("varchar(")
1225        .and_then(|s| s.strip_suffix(")"))
1226        .ok_or_else(|| {
1227            SparkError::value(
1228                "INVALID_DATATYPE_FORMAT",
1229                &[("detail", "Invalid varchar format")],
1230            )
1231        })?;
1232
1233    let length = inner.trim().parse::<i32>().map_err(|_| {
1234        SparkError::value(
1235            "INVALID_DATATYPE_FORMAT",
1236            &[("detail", "Invalid varchar length")],
1237        )
1238    })?;
1239
1240    Ok(DataType::Varchar { length })
1241}
1242
1243fn parse_time(s: &str) -> Result<DataType> {
1244    let inner = s
1245        .strip_prefix("time(")
1246        .and_then(|s| s.strip_suffix(")"))
1247        .ok_or_else(|| {
1248            SparkError::value(
1249                "INVALID_DATATYPE_FORMAT",
1250                &[("detail", "Invalid time format")],
1251            )
1252        })?;
1253
1254    let precision = inner.trim().parse::<i32>().map_err(|_| {
1255        SparkError::value(
1256            "INVALID_DATATYPE_FORMAT",
1257            &[("detail", "Invalid time precision")],
1258        )
1259    })?;
1260
1261    Ok(DataType::Time { precision })
1262}
1263
1264fn parse_interval_string(s: &str) -> Result<DataType> {
1265    if s == "interval" {
1266        return Ok(DataType::CalendarInterval);
1267    }
1268
1269    let parts: Vec<&str> = s.split_whitespace().collect();
1270    if parts.len() < 2 {
1271        return Ok(DataType::CalendarInterval);
1272    }
1273
1274    if parts.len() == 2 {
1275        // Single field like "interval day"
1276        let field_name = parts[1];
1277        for &(name, code) in YEAR_MONTH_FIELDS {
1278            if name == field_name {
1279                return Ok(DataType::YearMonthInterval {
1280                    start_field: code,
1281                    end_field: code,
1282                });
1283            }
1284        }
1285        for &(name, code) in DAY_TIME_FIELDS {
1286            if name == field_name {
1287                return Ok(DataType::DayTimeInterval {
1288                    start_field: code,
1289                    end_field: code,
1290                });
1291            }
1292        }
1293    } else if parts.len() == 4 && parts[2] == "to" {
1294        // Range like "interval day to second"
1295        let start = parts[1];
1296        let end = parts[3];
1297
1298        // Try year-month fields
1299        if let (Some(&(_, start_code)), Some(&(_, end_code))) = (
1300            YEAR_MONTH_FIELDS.iter().find(|(name, _)| *name == start),
1301            YEAR_MONTH_FIELDS.iter().find(|(name, _)| *name == end),
1302        ) {
1303            return Ok(DataType::YearMonthInterval {
1304                start_field: start_code,
1305                end_field: end_code,
1306            });
1307        }
1308
1309        // Try day-time fields
1310        if let (Some(&(_, start_code)), Some(&(_, end_code))) = (
1311            DAY_TIME_FIELDS.iter().find(|(name, _)| *name == start),
1312            DAY_TIME_FIELDS.iter().find(|(name, _)| *name == end),
1313        ) {
1314            return Ok(DataType::DayTimeInterval {
1315                start_field: start_code,
1316                end_field: end_code,
1317            });
1318        }
1319    }
1320
1321    Ok(DataType::CalendarInterval)
1322}
1323
1324fn parse_geometry(s: &str) -> Result<DataType> {
1325    let inner = s
1326        .strip_prefix("geometry(")
1327        .and_then(|s| s.strip_suffix(")"))
1328        .ok_or_else(|| {
1329            SparkError::value(
1330                "INVALID_DATATYPE_FORMAT",
1331                &[("detail", "Invalid geometry format")],
1332            )
1333        })?;
1334
1335    let srid = if inner.to_lowercase() == "any" {
1336        -1
1337    } else {
1338        inner.parse::<i32>().map_err(|_| {
1339            SparkError::value(
1340                "INVALID_DATATYPE_FORMAT",
1341                &[("detail", "Invalid geometry srid")],
1342            )
1343        })?
1344    };
1345
1346    Ok(DataType::Geometry { srid })
1347}
1348
1349fn parse_geography(s: &str) -> Result<DataType> {
1350    let inner = s
1351        .strip_prefix("geography(")
1352        .and_then(|s| s.strip_suffix(")"))
1353        .ok_or_else(|| {
1354            SparkError::value(
1355                "INVALID_DATATYPE_FORMAT",
1356                &[("detail", "Invalid geography format")],
1357            )
1358        })?;
1359
1360    let srid = if inner.to_lowercase() == "any" {
1361        -1
1362    } else {
1363        inner
1364            .split(',')
1365            .next()
1366            .unwrap_or("4326")
1367            .parse::<i32>()
1368            .map_err(|_| {
1369                SparkError::value(
1370                    "INVALID_DATATYPE_FORMAT",
1371                    &[("detail", "Invalid geography srid")],
1372                )
1373            })?
1374    };
1375
1376    Ok(DataType::Geography { srid })
1377}
1378
1379/// Main DDL parser: parses DDL strings into DataType.
1380/// Handles primitive types, complex types (array, map, struct), and top-level schemas.
1381fn parse_datatype_string(input: &str) -> Result<DataType> {
1382    let trimmed = input.trim();
1383
1384    // A top-level schema is "a INT, b STRING" (multiple fields) OR a single "a INT"
1385    // (one field, no comma) -- both parse to a StructType, matching PySpark's
1386    // _parse_datatype_string. Detect either a top-level comma or a top-level space
1387    // separating a field name from its type; fall back to single-type parsing if the
1388    // schema interpretation fails (e.g. a bare type that itself contains spaces such as
1389    // "interval day to second").
1390    if !trimmed.to_lowercase().starts_with("struct<")
1391        && (contains_top_level_comma(trimmed) || contains_top_level_whitespace(trimmed))
1392    {
1393        if let Ok(dt) = parse_top_level_schema(trimmed) {
1394            return Ok(dt);
1395        }
1396    }
1397
1398    parse_single_type(trimmed)
1399}
1400
1401/// Whether `s` has a top-level whitespace char (outside `<>`/`()`), i.e. it looks like a
1402/// "name type" field rather than a bare type such as `int` or `array<int>`.
1403fn contains_top_level_whitespace(s: &str) -> bool {
1404    let mut depth = 0;
1405    for c in s.chars() {
1406        match c {
1407            '<' | '(' => depth += 1,
1408            '>' | ')' => depth -= 1,
1409            _ if depth == 0 && c.is_whitespace() => return true,
1410            _ => {}
1411        }
1412    }
1413    false
1414}
1415
1416/// Check if a string contains a comma at the top level (not inside brackets/parens)
1417fn contains_top_level_comma(s: &str) -> bool {
1418    let mut depth = 0;
1419    for c in s.chars() {
1420        match c {
1421            '<' | '(' => depth += 1,
1422            '>' | ')' => depth -= 1,
1423            ',' if depth == 0 => return true,
1424            _ => {}
1425        }
1426    }
1427    false
1428}
1429
1430/// Parse a top-level schema like "a INT, b STRING" or "a: int, b: string"
1431fn parse_top_level_schema(input: &str) -> Result<DataType> {
1432    let mut fields = Vec::new();
1433    let parts = split_top_level_comma(input);
1434
1435    for part in parts {
1436        let trimmed = part.trim();
1437        if trimmed.is_empty() {
1438            continue;
1439        }
1440
1441        // Parse "name type" or "name: type" format
1442        let (name, type_str) = if let Some(colon_idx) = trimmed.find(':') {
1443            let name = trimmed[..colon_idx].trim();
1444            let type_str = trimmed[colon_idx + 1..].trim();
1445            (name, type_str)
1446        } else {
1447            // Assume first word is name, rest is type
1448            let parts: Vec<&str> = trimmed.splitn(2, char::is_whitespace).collect();
1449            if parts.len() != 2 {
1450                return Err(SparkError::value(
1451                    "INVALID_DATATYPE_FORMAT",
1452                    &[("detail", &format!("Cannot parse field: {}", trimmed))],
1453                ));
1454            }
1455            (parts[0], parts[1])
1456        };
1457
1458        let data_type = parse_single_type(type_str)?;
1459        fields.push(StructField {
1460            name: name.to_string(),
1461            data_type,
1462            nullable: true,
1463            metadata: BTreeMap::new(),
1464        });
1465    }
1466
1467    Ok(DataType::Struct { fields })
1468}
1469
1470/// Split by top-level commas (respecting angle brackets and parentheses)
1471fn split_top_level_comma(s: &str) -> Vec<&str> {
1472    let mut result = Vec::new();
1473    let mut start = 0;
1474    let mut depth = 0;
1475    let bytes = s.as_bytes();
1476
1477    for (i, &b) in bytes.iter().enumerate() {
1478        match b {
1479            b'<' | b'(' => depth += 1,
1480            b'>' | b')' => depth -= 1,
1481            b',' if depth == 0 => {
1482                result.push(&s[start..i]);
1483                start = i + 1;
1484            }
1485            _ => {}
1486        }
1487    }
1488    result.push(&s[start..]);
1489    result
1490}
1491
1492/// Parse a single type (primitive or complex)
1493fn parse_single_type(input: &str) -> Result<DataType> {
1494    let trimmed = input.trim().to_lowercase();
1495
1496    // Complex types
1497    if trimmed.starts_with("array<") {
1498        return parse_array_type(input);
1499    }
1500    if trimmed.starts_with("map<") {
1501        return parse_map_type(input);
1502    }
1503    if trimmed.starts_with("struct<") {
1504        return parse_struct_type(input);
1505    }
1506
1507    // Primitive types
1508    match trimmed.as_str() {
1509        "null" | "void" => Ok(DataType::Null),
1510        "boolean" => Ok(DataType::Boolean),
1511        "byte" | "tinyint" => Ok(DataType::Byte),
1512        "short" | "smallint" => Ok(DataType::Short),
1513        "int" | "integer" => Ok(DataType::Integer),
1514        "long" | "bigint" => Ok(DataType::Long),
1515        "float" => Ok(DataType::Float),
1516        "double" => Ok(DataType::Double),
1517        "string" => Ok(DataType::String {
1518            collation: "UTF8_BINARY".to_string(),
1519        }),
1520        "binary" => Ok(DataType::Binary),
1521        "date" => Ok(DataType::Date),
1522        "timestamp" => Ok(DataType::Timestamp),
1523        "timestamp_ntz" => Ok(DataType::TimestampNtz),
1524        "interval" => Ok(DataType::CalendarInterval),
1525        "variant" => Ok(DataType::Variant),
1526        _ => {
1527            // Try complex parsing with already-lowercased trimmed version
1528            if trimmed.starts_with("decimal(") {
1529                parse_decimal(&trimmed)
1530            } else if trimmed.starts_with("char(") {
1531                parse_char(&trimmed)
1532            } else if trimmed.starts_with("varchar(") {
1533                parse_varchar(&trimmed)
1534            } else if trimmed.starts_with("time(") {
1535                parse_time(&trimmed)
1536            } else if trimmed.starts_with("interval") {
1537                parse_interval_string(&trimmed)
1538            } else if trimmed.starts_with("string collate") {
1539                let collation = trimmed
1540                    .trim_start_matches("string")
1541                    .trim_start_matches("collate")
1542                    .trim()
1543                    .to_string();
1544                Ok(DataType::String { collation })
1545            } else if trimmed.starts_with("geometry(") {
1546                parse_geometry(&trimmed)
1547            } else if trimmed.starts_with("geography(") {
1548                parse_geography(&trimmed)
1549            } else {
1550                Err(SparkError::value(
1551                    "INVALID_DATATYPE_FORMAT",
1552                    &[("detail", &format!("Unknown type: {}", input))],
1553                ))
1554            }
1555        }
1556    }
1557}
1558
1559/// Parse array<elementType>
1560fn parse_array_type(input: &str) -> Result<DataType> {
1561    let trimmed = input.trim();
1562    if !trimmed.to_lowercase().starts_with("array<") || !trimmed.ends_with('>') {
1563        return Err(SparkError::value(
1564            "INVALID_DATATYPE_FORMAT",
1565            &[("detail", "Invalid array type format")],
1566        ));
1567    }
1568
1569    let inner = &trimmed[6..trimmed.len() - 1];
1570    let element_type = parse_single_type(inner)?;
1571
1572    Ok(DataType::Array {
1573        element_type: Box::new(element_type),
1574        contains_null: true,
1575    })
1576}
1577
1578/// Parse map<keyType,valueType>
1579fn parse_map_type(input: &str) -> Result<DataType> {
1580    let trimmed = input.trim();
1581    if !trimmed.to_lowercase().starts_with("map<") || !trimmed.ends_with('>') {
1582        return Err(SparkError::value(
1583            "INVALID_DATATYPE_FORMAT",
1584            &[("detail", "Invalid map type format")],
1585        ));
1586    }
1587
1588    let inner = &trimmed[4..trimmed.len() - 1];
1589    let parts = split_map_parts(inner)?;
1590    if parts.len() != 2 {
1591        return Err(SparkError::value(
1592            "INVALID_DATATYPE_FORMAT",
1593            &[(
1594                "detail",
1595                "Map must have exactly 2 type parameters (key and value)",
1596            )],
1597        ));
1598    }
1599
1600    let key_type = parse_single_type(parts[0])?;
1601    let value_type = parse_single_type(parts[1])?;
1602
1603    Ok(DataType::Map {
1604        key_type: Box::new(key_type),
1605        value_type: Box::new(value_type),
1606        value_contains_null: true,
1607    })
1608}
1609
1610/// Split map key and value types by comma at the top level
1611fn split_map_parts(s: &str) -> Result<Vec<&str>> {
1612    let mut depth = 0;
1613    let mut parts = Vec::new();
1614    let mut start = 0;
1615
1616    for (i, c) in s.char_indices() {
1617        match c {
1618            '<' | '(' => depth += 1,
1619            '>' | ')' => depth -= 1,
1620            ',' if depth == 0 => {
1621                parts.push(&s[start..i]);
1622                start = i + 1;
1623            }
1624            _ => {}
1625        }
1626    }
1627    parts.push(&s[start..]);
1628
1629    Ok(parts)
1630}
1631
1632/// Parse struct<field1:type1,field2:type2,...>
1633fn parse_struct_type(input: &str) -> Result<DataType> {
1634    let trimmed = input.trim();
1635    if !trimmed.to_lowercase().starts_with("struct<") || !trimmed.ends_with('>') {
1636        return Err(SparkError::value(
1637            "INVALID_DATATYPE_FORMAT",
1638            &[("detail", "Invalid struct type format")],
1639        ));
1640    }
1641
1642    let inner = &trimmed[7..trimmed.len() - 1];
1643    if inner.trim().is_empty() {
1644        return Ok(DataType::Struct { fields: vec![] });
1645    }
1646
1647    let mut fields = Vec::new();
1648    let parts = split_struct_fields(inner);
1649
1650    for part in parts {
1651        let part_trimmed = part.trim();
1652        if part_trimmed.is_empty() {
1653            continue;
1654        }
1655
1656        // Find the colon separating field name from type
1657        if let Some(colon_idx) = find_unbracketed_colon(part_trimmed) {
1658            let name = part_trimmed[..colon_idx].trim();
1659            let type_str = part_trimmed[colon_idx + 1..].trim();
1660
1661            let data_type = parse_single_type(type_str)?;
1662            fields.push(StructField {
1663                name: name.to_string(),
1664                data_type,
1665                nullable: true,
1666                metadata: BTreeMap::new(),
1667            });
1668        } else {
1669            return Err(SparkError::value(
1670                "INVALID_DATATYPE_FORMAT",
1671                &[("detail", &format!("Invalid struct field: {}", part_trimmed))],
1672            ));
1673        }
1674    }
1675
1676    Ok(DataType::Struct { fields })
1677}
1678
1679/// Split struct fields by comma at the top level
1680fn split_struct_fields(s: &str) -> Vec<&str> {
1681    let mut depth = 0;
1682    let mut parts = Vec::new();
1683    let mut start = 0;
1684
1685    for (i, c) in s.char_indices() {
1686        match c {
1687            '<' | '(' => depth += 1,
1688            '>' | ')' => depth -= 1,
1689            ',' if depth == 0 => {
1690                parts.push(&s[start..i]);
1691                start = i + 1;
1692            }
1693            _ => {}
1694        }
1695    }
1696    parts.push(&s[start..]);
1697
1698    parts
1699}
1700
1701/// Find the position of an unbracketed colon
1702fn find_unbracketed_colon(s: &str) -> Option<usize> {
1703    let mut depth = 0;
1704    for (i, c) in s.char_indices() {
1705        match c {
1706            '<' | '(' => depth += 1,
1707            '>' | ')' => depth -= 1,
1708            ':' if depth == 0 => return Some(i),
1709            _ => {}
1710        }
1711    }
1712    None
1713}
1714
1715#[cfg(test)]
1716mod tests {
1717    use super::*;
1718
1719    #[test]
1720    fn test_null_type() {
1721        let dt = DataType::Null;
1722        assert_eq!(dt.type_name(), "void");
1723        assert_eq!(dt.simple_string(), "void");
1724        assert_eq!(dt.json(), "\"void\"");
1725    }
1726
1727    #[test]
1728    fn test_simple_types() {
1729        let tests = vec![
1730            (DataType::Boolean, "boolean", "boolean", "\"boolean\""),
1731            (DataType::Byte, "byte", "tinyint", "\"byte\""),
1732            (DataType::Short, "short", "smallint", "\"short\""),
1733            (DataType::Integer, "integer", "int", "\"integer\""),
1734            (DataType::Long, "long", "bigint", "\"long\""),
1735            (DataType::Float, "float", "float", "\"float\""),
1736            (DataType::Double, "double", "double", "\"double\""),
1737            (DataType::Binary, "binary", "binary", "\"binary\""),
1738            (DataType::Date, "date", "date", "\"date\""),
1739            (
1740                DataType::Timestamp,
1741                "timestamp",
1742                "timestamp",
1743                "\"timestamp\"",
1744            ),
1745            (
1746                DataType::TimestampNtz,
1747                "timestamp_ntz",
1748                "timestamp_ntz",
1749                "\"timestamp_ntz\"",
1750            ),
1751            (
1752                DataType::CalendarInterval,
1753                "interval",
1754                "interval",
1755                "\"interval\"",
1756            ),
1757            (DataType::Variant, "variant", "variant", "\"variant\""),
1758        ];
1759
1760        for (dt, expected_typename, expected_simple, expected_json) in tests {
1761            assert_eq!(dt.type_name(), expected_typename, "type_name for {:?}", dt);
1762            assert_eq!(
1763                dt.simple_string(),
1764                expected_simple,
1765                "simple_string for {:?}",
1766                dt
1767            );
1768            assert_eq!(dt.json(), expected_json, "json for {:?}", dt);
1769        }
1770    }
1771
1772    #[test]
1773    fn test_decimal_type() {
1774        let dt = DataType::Decimal {
1775            precision: 10,
1776            scale: 0,
1777        };
1778        assert_eq!(dt.simple_string(), "decimal(10,0)");
1779        assert_eq!(dt.json(), "\"decimal(10,0)\"");
1780
1781        let dt2 = DataType::Decimal {
1782            precision: 38,
1783            scale: 18,
1784        };
1785        assert_eq!(dt2.simple_string(), "decimal(38,18)");
1786    }
1787
1788    #[test]
1789    fn test_char_varchar_types() {
1790        let char_dt = DataType::Char { length: 50 };
1791        assert_eq!(char_dt.simple_string(), "char(50)");
1792        assert_eq!(char_dt.json(), "\"char(50)\"");
1793
1794        let varchar_dt = DataType::Varchar { length: 100 };
1795        assert_eq!(varchar_dt.simple_string(), "varchar(100)");
1796        assert_eq!(varchar_dt.json(), "\"varchar(100)\"");
1797    }
1798
1799    #[test]
1800    fn test_array_type() {
1801        let dt = DataType::Array {
1802            element_type: Box::new(DataType::String {
1803                collation: "UTF8_BINARY".to_string(),
1804            }),
1805            contains_null: true,
1806        };
1807        assert_eq!(dt.simple_string(), "array<string>");
1808
1809        let json_val = dt.json_value();
1810        assert_eq!(json_val["type"], "array");
1811        assert_eq!(json_val["containsNull"], true);
1812
1813        let dt2 = DataType::Array {
1814            element_type: Box::new(DataType::Integer),
1815            contains_null: false,
1816        };
1817        assert_eq!(dt2.simple_string(), "array<int>");
1818    }
1819
1820    #[test]
1821    fn test_map_type() {
1822        let dt = DataType::Map {
1823            key_type: Box::new(DataType::String {
1824                collation: "UTF8_BINARY".to_string(),
1825            }),
1826            value_type: Box::new(DataType::Integer),
1827            value_contains_null: true,
1828        };
1829        assert_eq!(dt.simple_string(), "map<string,int>");
1830
1831        let json_val = dt.json_value();
1832        assert_eq!(json_val["type"], "map");
1833        assert_eq!(json_val["valueContainsNull"], true);
1834    }
1835
1836    #[test]
1837    fn test_struct_type() {
1838        let dt = DataType::Struct {
1839            fields: vec![
1840                StructField {
1841                    name: "name".to_string(),
1842                    data_type: DataType::String {
1843                        collation: "UTF8_BINARY".to_string(),
1844                    },
1845                    nullable: true,
1846                    metadata: BTreeMap::new(),
1847                },
1848                StructField {
1849                    name: "age".to_string(),
1850                    data_type: DataType::Integer,
1851                    nullable: true,
1852                    metadata: BTreeMap::new(),
1853                },
1854            ],
1855        };
1856        assert_eq!(dt.simple_string(), "struct<name:string,age:int>");
1857
1858        let json_val = dt.json_value();
1859        assert_eq!(json_val["type"], "struct");
1860        assert_eq!(json_val["fields"].as_array().unwrap().len(), 2);
1861    }
1862
1863    #[test]
1864    fn test_interval_types() {
1865        let dti = DataType::DayTimeInterval {
1866            start_field: 0,
1867            end_field: 3,
1868        };
1869        assert_eq!(dti.simple_string(), "interval day to second");
1870
1871        let ymi = DataType::YearMonthInterval {
1872            start_field: 0,
1873            end_field: 1,
1874        };
1875        assert_eq!(ymi.simple_string(), "interval year to month");
1876    }
1877
1878    #[test]
1879    fn test_string_collation() {
1880        let dt = DataType::String {
1881            collation: "UTF8_BINARY".to_string(),
1882        };
1883        assert_eq!(dt.simple_string(), "string");
1884
1885        let dt2 = DataType::String {
1886            collation: "UNICODE".to_string(),
1887        };
1888        assert_eq!(dt2.simple_string(), "string collate UNICODE");
1889    }
1890
1891    #[test]
1892    fn test_time_type() {
1893        let dt = DataType::Time { precision: 6 };
1894        assert_eq!(dt.simple_string(), "time(6)");
1895        assert_eq!(dt.json(), "\"time(6)\"");
1896    }
1897
1898    #[test]
1899    fn test_proto_roundtrip_simple() {
1900        let types = vec![
1901            DataType::Null,
1902            DataType::Boolean,
1903            DataType::Byte,
1904            DataType::Integer,
1905            DataType::Long,
1906            DataType::Float,
1907            DataType::Double,
1908            DataType::Binary,
1909            DataType::Date,
1910            DataType::Timestamp,
1911            DataType::TimestampNtz,
1912            DataType::CalendarInterval,
1913            DataType::Variant,
1914        ];
1915
1916        for dt in types {
1917            let proto = dt.to_proto();
1918            let roundtrip = DataType::from_proto(&proto).unwrap();
1919            assert_eq!(dt, roundtrip, "proto roundtrip failed for {:?}", dt);
1920        }
1921    }
1922
1923    #[test]
1924    fn test_proto_roundtrip_decimal() {
1925        let dt = DataType::Decimal {
1926            precision: 38,
1927            scale: 18,
1928        };
1929        let proto = dt.to_proto();
1930        let roundtrip = DataType::from_proto(&proto).unwrap();
1931        assert_eq!(dt, roundtrip);
1932    }
1933
1934    #[test]
1935    fn test_proto_roundtrip_array() {
1936        let dt = DataType::Array {
1937            element_type: Box::new(DataType::String {
1938                collation: "UTF8_BINARY".to_string(),
1939            }),
1940            contains_null: false,
1941        };
1942        let proto = dt.to_proto();
1943        let roundtrip = DataType::from_proto(&proto).unwrap();
1944        assert_eq!(dt, roundtrip);
1945    }
1946
1947    #[test]
1948    fn test_proto_roundtrip_struct() {
1949        let dt = DataType::Struct {
1950            fields: vec![
1951                StructField {
1952                    name: "a".to_string(),
1953                    data_type: DataType::Integer,
1954                    nullable: true,
1955                    metadata: BTreeMap::new(),
1956                },
1957                StructField {
1958                    name: "b".to_string(),
1959                    data_type: DataType::String {
1960                        collation: "UTF8_BINARY".to_string(),
1961                    },
1962                    nullable: false,
1963                    metadata: BTreeMap::new(),
1964                },
1965            ],
1966        };
1967        let proto = dt.to_proto();
1968        let roundtrip = DataType::from_proto(&proto).unwrap();
1969        assert_eq!(dt, roundtrip);
1970    }
1971
1972    #[test]
1973    fn test_json_parse_decimal() {
1974        let dt = DataType::from_json(&serde_json::json!("decimal(10,0)")).unwrap();
1975        assert_eq!(dt.simple_string(), "decimal(10,0)");
1976    }
1977
1978    #[test]
1979    fn test_json_parse_array() {
1980        let json = serde_json::json!({
1981            "type": "array",
1982            "elementType": "string",
1983            "containsNull": true,
1984        });
1985        let dt = DataType::from_json(&json).unwrap();
1986        assert_eq!(dt.simple_string(), "array<string>");
1987    }
1988
1989    #[test]
1990    fn test_json_parse_struct() {
1991        let json = serde_json::json!({
1992            "type": "struct",
1993            "fields": [
1994                {
1995                    "name": "name",
1996                    "type": "string",
1997                    "nullable": true,
1998                    "metadata": {}
1999                },
2000                {
2001                    "name": "age",
2002                    "type": "integer",
2003                    "nullable": true,
2004                    "metadata": {}
2005                }
2006            ]
2007        });
2008        let dt = DataType::from_json(&json).unwrap();
2009        assert_eq!(dt.simple_string(), "struct<name:string,age:int>");
2010    }
2011
2012    #[test]
2013    fn test_hash_consistency() {
2014        let dt1 = DataType::Integer;
2015        let dt2 = DataType::Integer;
2016        let mut hasher1 = std::collections::hash_map::DefaultHasher::new();
2017        let mut hasher2 = std::collections::hash_map::DefaultHasher::new();
2018        dt1.hash(&mut hasher1);
2019        dt2.hash(&mut hasher2);
2020        assert_eq!(
2021            std::hash::Hasher::finish(&hasher1),
2022            std::hash::Hasher::finish(&hasher2)
2023        );
2024    }
2025
2026    #[test]
2027    fn test_equality() {
2028        let dt1 = DataType::Decimal {
2029            precision: 10,
2030            scale: 0,
2031        };
2032        let dt2 = DataType::Decimal {
2033            precision: 10,
2034            scale: 0,
2035        };
2036        assert_eq!(dt1, dt2);
2037
2038        let dt3 = DataType::Decimal {
2039            precision: 10,
2040            scale: 1,
2041        };
2042        assert_ne!(dt1, dt3);
2043    }
2044
2045    #[test]
2046    fn test_struct_field_simple_string() {
2047        let field = StructField {
2048            name: "field_name".to_string(),
2049            data_type: DataType::String {
2050                collation: "UTF8_BINARY".to_string(),
2051            },
2052            nullable: true,
2053            metadata: BTreeMap::new(),
2054        };
2055        assert_eq!(field.simple_string(), "field_name:string");
2056    }
2057
2058    #[test]
2059    fn test_geometry_type() {
2060        let dt = DataType::Geometry { srid: 4326 };
2061        assert_eq!(dt.simple_string(), "geometry(4326)");
2062
2063        let dt_any = DataType::Geometry { srid: -1 };
2064        assert_eq!(dt_any.simple_string(), "geometry(any)");
2065    }
2066
2067    #[test]
2068    fn test_geography_type() {
2069        let dt = DataType::Geography { srid: 4326 };
2070        assert_eq!(dt.simple_string(), "geography(4326)");
2071
2072        let dt_any = DataType::Geography { srid: -1 };
2073        assert_eq!(dt_any.simple_string(), "geography(any)");
2074    }
2075
2076    #[test]
2077    fn test_golden_values_simple_strings() {
2078        // Golden values from Python pyspark.sql.types
2079        struct TestCase {
2080            dt: DataType,
2081            expected_simple: &'static str,
2082            expected_type_name: &'static str,
2083        }
2084
2085        let tests = vec![
2086            TestCase {
2087                dt: DataType::Null,
2088                expected_simple: "void",
2089                expected_type_name: "void",
2090            },
2091            TestCase {
2092                dt: DataType::String {
2093                    collation: "UTF8_BINARY".to_string(),
2094                },
2095                expected_simple: "string",
2096                expected_type_name: "string",
2097            },
2098            TestCase {
2099                dt: DataType::Boolean,
2100                expected_simple: "boolean",
2101                expected_type_name: "boolean",
2102            },
2103            TestCase {
2104                dt: DataType::Byte,
2105                expected_simple: "tinyint",
2106                expected_type_name: "byte",
2107            },
2108            TestCase {
2109                dt: DataType::Short,
2110                expected_simple: "smallint",
2111                expected_type_name: "short",
2112            },
2113            TestCase {
2114                dt: DataType::Integer,
2115                expected_simple: "int",
2116                expected_type_name: "integer",
2117            },
2118            TestCase {
2119                dt: DataType::Long,
2120                expected_simple: "bigint",
2121                expected_type_name: "long",
2122            },
2123            TestCase {
2124                dt: DataType::Float,
2125                expected_simple: "float",
2126                expected_type_name: "float",
2127            },
2128            TestCase {
2129                dt: DataType::Double,
2130                expected_simple: "double",
2131                expected_type_name: "double",
2132            },
2133            TestCase {
2134                dt: DataType::Date,
2135                expected_simple: "date",
2136                expected_type_name: "date",
2137            },
2138            TestCase {
2139                dt: DataType::Timestamp,
2140                expected_simple: "timestamp",
2141                expected_type_name: "timestamp",
2142            },
2143            TestCase {
2144                dt: DataType::TimestampNtz,
2145                expected_simple: "timestamp_ntz",
2146                expected_type_name: "timestamp_ntz",
2147            },
2148            TestCase {
2149                dt: DataType::Binary,
2150                expected_simple: "binary",
2151                expected_type_name: "binary",
2152            },
2153            TestCase {
2154                dt: DataType::Decimal {
2155                    precision: 10,
2156                    scale: 0,
2157                },
2158                expected_simple: "decimal(10,0)",
2159                expected_type_name: "decimal",
2160            },
2161            TestCase {
2162                dt: DataType::Char { length: 50 },
2163                expected_simple: "char(50)",
2164                expected_type_name: "char",
2165            },
2166            TestCase {
2167                dt: DataType::Varchar { length: 100 },
2168                expected_simple: "varchar(100)",
2169                expected_type_name: "varchar",
2170            },
2171            TestCase {
2172                dt: DataType::CalendarInterval,
2173                expected_simple: "interval",
2174                expected_type_name: "interval",
2175            },
2176            TestCase {
2177                dt: DataType::DayTimeInterval {
2178                    start_field: 0,
2179                    end_field: 3,
2180                },
2181                expected_simple: "interval day to second",
2182                expected_type_name: "interval",
2183            },
2184            TestCase {
2185                dt: DataType::YearMonthInterval {
2186                    start_field: 0,
2187                    end_field: 1,
2188                },
2189                expected_simple: "interval year to month",
2190                expected_type_name: "interval",
2191            },
2192            TestCase {
2193                dt: DataType::Variant,
2194                expected_simple: "variant",
2195                expected_type_name: "variant",
2196            },
2197        ];
2198
2199        for test in tests {
2200            assert_eq!(
2201                test.dt.simple_string(),
2202                test.expected_simple,
2203                "simpleString mismatch for {:?}",
2204                test.dt
2205            );
2206            assert_eq!(
2207                test.dt.type_name(),
2208                test.expected_type_name,
2209                "typeName mismatch for {:?}",
2210                test.dt
2211            );
2212        }
2213    }
2214
2215    #[test]
2216    fn test_golden_values_complex_types() {
2217        // ArrayType with StringType
2218        let array_string = DataType::Array {
2219            element_type: Box::new(DataType::String {
2220                collation: "UTF8_BINARY".to_string(),
2221            }),
2222            contains_null: true,
2223        };
2224        assert_eq!(array_string.simple_string(), "array<string>");
2225
2226        // ArrayType with IntegerType, contains_null=false
2227        let array_int = DataType::Array {
2228            element_type: Box::new(DataType::Integer),
2229            contains_null: false,
2230        };
2231        assert_eq!(array_int.simple_string(), "array<int>");
2232
2233        // MapType<StringType, IntegerType>
2234        let map_type = DataType::Map {
2235            key_type: Box::new(DataType::String {
2236                collation: "UTF8_BINARY".to_string(),
2237            }),
2238            value_type: Box::new(DataType::Integer),
2239            value_contains_null: true,
2240        };
2241        assert_eq!(map_type.simple_string(), "map<string,int>");
2242
2243        // StructType([StructField('name', StringType), StructField('age', IntegerType)])
2244        let struct_type = DataType::Struct {
2245            fields: vec![
2246                StructField {
2247                    name: "name".to_string(),
2248                    data_type: DataType::String {
2249                        collation: "UTF8_BINARY".to_string(),
2250                    },
2251                    nullable: true,
2252                    metadata: BTreeMap::new(),
2253                },
2254                StructField {
2255                    name: "age".to_string(),
2256                    data_type: DataType::Integer,
2257                    nullable: true,
2258                    metadata: BTreeMap::new(),
2259                },
2260            ],
2261        };
2262        assert_eq!(struct_type.simple_string(), "struct<name:string,age:int>");
2263    }
2264
2265    #[test]
2266    fn test_golden_json_values() {
2267        // Test JSON for simple types
2268        let null_json = DataType::Null.json();
2269        assert_eq!(null_json, "\"void\"");
2270
2271        let string_json = DataType::String {
2272            collation: "UTF8_BINARY".to_string(),
2273        }
2274        .json();
2275        assert_eq!(string_json, "\"string\"");
2276
2277        let decimal_json = DataType::Decimal {
2278            precision: 10,
2279            scale: 0,
2280        }
2281        .json();
2282        assert_eq!(decimal_json, "\"decimal(10,0)\"");
2283
2284        // Test JSON for array type
2285        let array_json = DataType::Array {
2286            element_type: Box::new(DataType::Integer),
2287            contains_null: false,
2288        };
2289        let json_val = array_json.json_value();
2290        assert_eq!(json_val["type"], "array");
2291        assert_eq!(json_val["containsNull"], false);
2292
2293        // Test JSON for map type
2294        let map_json = DataType::Map {
2295            key_type: Box::new(DataType::String {
2296                collation: "UTF8_BINARY".to_string(),
2297            }),
2298            value_type: Box::new(DataType::Integer),
2299            value_contains_null: true,
2300        };
2301        let json_val = map_json.json_value();
2302        assert_eq!(json_val["type"], "map");
2303        assert_eq!(json_val["valueContainsNull"], true);
2304    }
2305
2306    #[test]
2307    fn test_from_ddl_primitive_types() {
2308        // Test primitive types parsing
2309        assert_eq!(DataType::from_ddl("int").unwrap(), DataType::Integer);
2310        assert_eq!(DataType::from_ddl("INT").unwrap(), DataType::Integer);
2311        assert_eq!(DataType::from_ddl("bigint").unwrap(), DataType::Long);
2312        assert_eq!(
2313            DataType::from_ddl("string").unwrap(),
2314            DataType::String {
2315                collation: "UTF8_BINARY".to_string()
2316            }
2317        );
2318        assert_eq!(DataType::from_ddl("double").unwrap(), DataType::Double);
2319        assert_eq!(DataType::from_ddl("boolean").unwrap(), DataType::Boolean);
2320        assert_eq!(DataType::from_ddl("date").unwrap(), DataType::Date);
2321        assert_eq!(
2322            DataType::from_ddl("timestamp").unwrap(),
2323            DataType::Timestamp
2324        );
2325        assert_eq!(DataType::from_ddl("binary").unwrap(), DataType::Binary);
2326        assert_eq!(DataType::from_ddl("tinyint").unwrap(), DataType::Byte);
2327        assert_eq!(DataType::from_ddl("smallint").unwrap(), DataType::Short);
2328        assert_eq!(DataType::from_ddl("float").unwrap(), DataType::Float);
2329    }
2330
2331    #[test]
2332    fn test_from_ddl_decimal_and_fixed_length() {
2333        // Test decimal
2334        assert_eq!(
2335            DataType::from_ddl("decimal(10,2)").unwrap(),
2336            DataType::Decimal {
2337                precision: 10,
2338                scale: 2
2339            }
2340        );
2341
2342        // Test char
2343        assert_eq!(
2344            DataType::from_ddl("char(50)").unwrap(),
2345            DataType::Char { length: 50 }
2346        );
2347
2348        // Test varchar
2349        assert_eq!(
2350            DataType::from_ddl("varchar(100)").unwrap(),
2351            DataType::Varchar { length: 100 }
2352        );
2353
2354        // Test time
2355        assert_eq!(
2356            DataType::from_ddl("time(6)").unwrap(),
2357            DataType::Time { precision: 6 }
2358        );
2359    }
2360
2361    #[test]
2362    fn test_from_ddl_array_type() {
2363        let dt = DataType::from_ddl("array<int>").unwrap();
2364        assert_eq!(dt.simple_string(), "array<int>");
2365
2366        let dt2 = DataType::from_ddl("array<string>").unwrap();
2367        assert_eq!(dt2.simple_string(), "array<string>");
2368
2369        let dt3 = DataType::from_ddl("array<array<int>>").unwrap();
2370        assert_eq!(dt3.simple_string(), "array<array<int>>");
2371    }
2372
2373    #[test]
2374    fn test_from_ddl_map_type() {
2375        let dt = DataType::from_ddl("map<string,int>").unwrap();
2376        assert_eq!(dt.simple_string(), "map<string,int>");
2377
2378        let dt2 = DataType::from_ddl("map<string,array<int>>").unwrap();
2379        assert_eq!(dt2.simple_string(), "map<string,array<int>>");
2380    }
2381
2382    #[test]
2383    fn test_from_ddl_struct_type() {
2384        let dt = DataType::from_ddl("struct<name:string,age:int>").unwrap();
2385        assert_eq!(dt.simple_string(), "struct<name:string,age:int>");
2386
2387        let dt2 = DataType::from_ddl("struct<a:int,b:array<string>>").unwrap();
2388        assert_eq!(dt2.simple_string(), "struct<a:int,b:array<string>>");
2389    }
2390
2391    #[test]
2392    fn test_from_ddl_top_level_schema() {
2393        // Top-level schema without struct<>
2394        let dt = DataType::from_ddl("a INT, b STRING").unwrap();
2395        assert_eq!(dt.simple_string(), "struct<a:int,b:string>");
2396
2397        let dt2 = DataType::from_ddl("a DOUBLE, b CHAR(50)").unwrap();
2398        assert_eq!(dt2.simple_string(), "struct<a:double,b:char(50)>");
2399
2400        let dt3 = DataType::from_ddl("name string, age int").unwrap();
2401        assert_eq!(dt3.simple_string(), "struct<name:string,age:int>");
2402    }
2403
2404    #[test]
2405    fn test_from_ddl_roundtrip() {
2406        // Test that from_ddl -> simple_string roundtrips correctly
2407        let test_cases = vec![
2408            "int",
2409            "string",
2410            "array<int>",
2411            "map<string,int>",
2412            "struct<name:string,age:int>",
2413            "decimal(10,2)",
2414            "char(50)",
2415            "varchar(100)",
2416            "array<map<string,int>>",
2417            "struct<a:int,b:array<string>>",
2418        ];
2419
2420        for case in test_cases {
2421            let dt = DataType::from_ddl(case).expect(&format!("Failed to parse: {}", case));
2422            let simple = dt.simple_string();
2423            let dt2 =
2424                DataType::from_ddl(&simple).expect(&format!("Failed to roundtrip: {}", simple));
2425            assert_eq!(dt, dt2, "Roundtrip failed for: {}", case);
2426        }
2427    }
2428
2429    #[test]
2430    fn test_need_conversion() {
2431        // Types that need conversion
2432        assert!(DataType::Date.need_conversion());
2433        assert!(DataType::Timestamp.need_conversion());
2434        assert!(DataType::TimestampNtz.need_conversion());
2435        assert!(DataType::CalendarInterval.need_conversion());
2436        assert!(DataType::Time { precision: 6 }.need_conversion());
2437
2438        // Types that don't need conversion
2439        assert!(!DataType::Integer.need_conversion());
2440        assert!(!DataType::String {
2441            collation: "UTF8_BINARY".to_string()
2442        }
2443        .need_conversion());
2444        assert!(!DataType::Boolean.need_conversion());
2445        assert!(!DataType::Double.need_conversion());
2446
2447        // Array/Map propagate conversion needs
2448        let array_with_date = DataType::Array {
2449            element_type: Box::new(DataType::Date),
2450            contains_null: true,
2451        };
2452        assert!(array_with_date.need_conversion());
2453
2454        let array_without = DataType::Array {
2455            element_type: Box::new(DataType::Integer),
2456            contains_null: true,
2457        };
2458        assert!(!array_without.need_conversion());
2459
2460        // StructType always needs conversion
2461        let struct_type = DataType::Struct { fields: vec![] };
2462        assert!(struct_type.need_conversion());
2463
2464        let struct_with_primitives = DataType::Struct {
2465            fields: vec![
2466                StructField {
2467                    name: "a".to_string(),
2468                    data_type: DataType::Integer,
2469                    nullable: true,
2470                    metadata: BTreeMap::new(),
2471                },
2472                StructField {
2473                    name: "b".to_string(),
2474                    data_type: DataType::String {
2475                        collation: "UTF8_BINARY".to_string(),
2476                    },
2477                    nullable: true,
2478                    metadata: BTreeMap::new(),
2479                },
2480            ],
2481        };
2482        assert!(struct_with_primitives.need_conversion());
2483    }
2484
2485    #[test]
2486    fn test_field_names() {
2487        let struct_type = DataType::Struct {
2488            fields: vec![
2489                StructField {
2490                    name: "name".to_string(),
2491                    data_type: DataType::String {
2492                        collation: "UTF8_BINARY".to_string(),
2493                    },
2494                    nullable: true,
2495                    metadata: BTreeMap::new(),
2496                },
2497                StructField {
2498                    name: "age".to_string(),
2499                    data_type: DataType::Integer,
2500                    nullable: true,
2501                    metadata: BTreeMap::new(),
2502                },
2503            ],
2504        };
2505
2506        let names = struct_type.field_names().unwrap();
2507        assert_eq!(names, vec!["name", "age"]);
2508
2509        // Test names() alias
2510        let names2 = struct_type.names().unwrap();
2511        assert_eq!(names2, vec!["name", "age"]);
2512
2513        // Test error on non-struct
2514        let int_type = DataType::Integer;
2515        assert!(int_type.field_names().is_err());
2516    }
2517
2518    #[test]
2519    fn test_struct_add_builder() {
2520        let empty_struct = DataType::Struct { fields: vec![] };
2521
2522        // Add first field
2523        let with_name = empty_struct
2524            .add(
2525                "name",
2526                DataType::String {
2527                    collation: "UTF8_BINARY".to_string(),
2528                },
2529                true,
2530                None,
2531            )
2532            .unwrap();
2533
2534        assert_eq!(with_name.field_names().unwrap(), vec!["name"]);
2535
2536        // Add second field
2537        let with_both = with_name.add("age", DataType::Integer, true, None).unwrap();
2538
2539        assert_eq!(with_both.field_names().unwrap(), vec!["name", "age"]);
2540        assert_eq!(with_both.simple_string(), "struct<name:string,age:int>");
2541
2542        // Test add with metadata
2543        let mut metadata = BTreeMap::new();
2544        metadata.insert(
2545            "key".to_string(),
2546            serde_json::Value::String("value".to_string()),
2547        );
2548        let with_metadata = with_both
2549            .add("score", DataType::Double, false, Some(metadata))
2550            .unwrap();
2551
2552        assert_eq!(
2553            with_metadata.field_names().unwrap(),
2554            vec!["name", "age", "score"]
2555        );
2556
2557        // Test error on non-struct
2558        let int_type = DataType::Integer;
2559        assert!(int_type
2560            .add(
2561                "field",
2562                DataType::String {
2563                    collation: "UTF8_BINARY".to_string(),
2564                },
2565                true,
2566                None
2567            )
2568            .is_err());
2569    }
2570
2571    #[test]
2572    fn test_from_ddl_intervals() {
2573        // Test interval types
2574        assert_eq!(
2575            DataType::from_ddl("interval").unwrap(),
2576            DataType::CalendarInterval
2577        );
2578
2579        assert_eq!(
2580            DataType::from_ddl("interval day").unwrap(),
2581            DataType::DayTimeInterval {
2582                start_field: 0,
2583                end_field: 0
2584            }
2585        );
2586
2587        assert_eq!(
2588            DataType::from_ddl("interval year to month").unwrap(),
2589            DataType::YearMonthInterval {
2590                start_field: 0,
2591                end_field: 1
2592            }
2593        );
2594
2595        assert_eq!(
2596            DataType::from_ddl("interval day to second").unwrap(),
2597            DataType::DayTimeInterval {
2598                start_field: 0,
2599                end_field: 3
2600            }
2601        );
2602    }
2603
2604    #[test]
2605    fn test_from_ddl_case_insensitive() {
2606        // Test case insensitivity
2607        assert_eq!(
2608            DataType::from_ddl("INT").unwrap(),
2609            DataType::from_ddl("int").unwrap()
2610        );
2611
2612        assert_eq!(
2613            DataType::from_ddl("STRUCT<Name:STRING,Age:INT>")
2614                .unwrap()
2615                .simple_string(),
2616            "struct<Name:string,Age:int>"
2617        );
2618
2619        assert_eq!(
2620            DataType::from_ddl("ARRAY<INT>").unwrap(),
2621            DataType::from_ddl("array<int>").unwrap()
2622        );
2623    }
2624
2625    #[test]
2626    fn test_to_nullable_primitive_types() {
2627        // Primitive types should remain unchanged
2628        let int_type = DataType::Integer;
2629        assert_eq!(int_type.to_nullable(), DataType::Integer);
2630
2631        let string_type = DataType::String {
2632            collation: "UTF8_BINARY".to_string(),
2633        };
2634        assert_eq!(string_type.to_nullable(), string_type);
2635
2636        let bool_type = DataType::Boolean;
2637        assert_eq!(bool_type.to_nullable(), bool_type);
2638
2639        let double_type = DataType::Double;
2640        assert_eq!(double_type.to_nullable(), double_type);
2641
2642        let date_type = DataType::Date;
2643        assert_eq!(date_type.to_nullable(), date_type);
2644
2645        let timestamp_type = DataType::Timestamp;
2646        assert_eq!(timestamp_type.to_nullable(), timestamp_type);
2647    }
2648
2649    #[test]
2650    fn test_to_nullable_array_type() {
2651        let array_type = DataType::Array {
2652            element_type: Box::new(DataType::Integer),
2653            contains_null: false,
2654        };
2655        let nullable = array_type.to_nullable();
2656
2657        if let DataType::Array {
2658            element_type: _,
2659            contains_null,
2660        } = nullable
2661        {
2662            assert!(contains_null);
2663        } else {
2664            panic!("Expected Array type");
2665        }
2666    }
2667
2668    #[test]
2669    fn test_to_nullable_array_nested() {
2670        let nested_array = DataType::Array {
2671            element_type: Box::new(DataType::Array {
2672                element_type: Box::new(DataType::Integer),
2673                contains_null: false,
2674            }),
2675            contains_null: false,
2676        };
2677        let nullable = nested_array.to_nullable();
2678
2679        if let DataType::Array {
2680            element_type,
2681            contains_null,
2682        } = nullable
2683        {
2684            assert!(contains_null);
2685            if let DataType::Array {
2686                contains_null: inner_null,
2687                ..
2688            } = element_type.as_ref()
2689            {
2690                assert!(inner_null);
2691            } else {
2692                panic!("Expected nested Array type");
2693            }
2694        } else {
2695            panic!("Expected Array type");
2696        }
2697    }
2698
2699    #[test]
2700    fn test_to_nullable_map_type() {
2701        let map_type = DataType::Map {
2702            key_type: Box::new(DataType::String {
2703                collation: "UTF8_BINARY".to_string(),
2704            }),
2705            value_type: Box::new(DataType::Integer),
2706            value_contains_null: false,
2707        };
2708        let nullable = map_type.to_nullable();
2709
2710        if let DataType::Map {
2711            key_type: _,
2712            value_type: _,
2713            value_contains_null,
2714        } = nullable
2715        {
2716            assert!(value_contains_null);
2717        } else {
2718            panic!("Expected Map type");
2719        }
2720    }
2721
2722    #[test]
2723    fn test_to_nullable_struct_type() {
2724        let struct_type = DataType::Struct {
2725            fields: vec![
2726                StructField {
2727                    name: "a".to_string(),
2728                    data_type: DataType::Integer,
2729                    nullable: false,
2730                    metadata: BTreeMap::new(),
2731                },
2732                StructField {
2733                    name: "b".to_string(),
2734                    data_type: DataType::String {
2735                        collation: "UTF8_BINARY".to_string(),
2736                    },
2737                    nullable: false,
2738                    metadata: BTreeMap::new(),
2739                },
2740            ],
2741        };
2742        let nullable = struct_type.to_nullable();
2743
2744        if let DataType::Struct { fields } = nullable {
2745            assert_eq!(fields.len(), 2);
2746            assert!(fields[0].nullable);
2747            assert!(fields[1].nullable);
2748        } else {
2749            panic!("Expected Struct type");
2750        }
2751    }
2752
2753    #[test]
2754    fn test_simple_string_all_types() {
2755        // Test simple_string for all basic types
2756        assert_eq!(DataType::Null.simple_string(), "void");
2757        assert_eq!(DataType::Boolean.simple_string(), "boolean");
2758        assert_eq!(DataType::Byte.simple_string(), "tinyint");
2759        assert_eq!(DataType::Short.simple_string(), "smallint");
2760        assert_eq!(DataType::Integer.simple_string(), "int");
2761        assert_eq!(DataType::Long.simple_string(), "bigint");
2762        assert_eq!(DataType::Float.simple_string(), "float");
2763        assert_eq!(DataType::Double.simple_string(), "double");
2764        assert_eq!(DataType::Binary.simple_string(), "binary");
2765        assert_eq!(DataType::Date.simple_string(), "date");
2766        assert_eq!(DataType::Timestamp.simple_string(), "timestamp");
2767        assert_eq!(DataType::TimestampNtz.simple_string(), "timestamp_ntz");
2768        assert_eq!(DataType::CalendarInterval.simple_string(), "interval");
2769        assert_eq!(DataType::Variant.simple_string(), "variant");
2770    }
2771
2772    #[test]
2773    fn test_simple_string_with_collation() {
2774        let string_default = DataType::String {
2775            collation: "".to_string(),
2776        };
2777        assert_eq!(string_default.simple_string(), "string");
2778
2779        let string_utf8 = DataType::String {
2780            collation: "UTF8_BINARY".to_string(),
2781        };
2782        assert_eq!(string_utf8.simple_string(), "string");
2783
2784        let string_custom = DataType::String {
2785            collation: "UNICODE".to_string(),
2786        };
2787        assert_eq!(string_custom.simple_string(), "string collate UNICODE");
2788    }
2789
2790    #[test]
2791    fn test_type_name_all_types() {
2792        assert_eq!(DataType::Null.type_name(), "void");
2793        assert_eq!(DataType::Boolean.type_name(), "boolean");
2794        assert_eq!(DataType::Byte.type_name(), "byte");
2795        assert_eq!(DataType::Integer.type_name(), "integer");
2796        assert_eq!(
2797            DataType::String {
2798                collation: "UTF8_BINARY".to_string()
2799            }
2800            .type_name(),
2801            "string"
2802        );
2803    }
2804
2805    #[test]
2806    fn test_from_json_string_types() {
2807        let json_int = serde_json::json!("integer");
2808        let dt = DataType::from_json(&json_int).unwrap();
2809        assert_eq!(dt, DataType::Integer);
2810
2811        let json_string = serde_json::json!("string");
2812        let dt = DataType::from_json(&json_string).unwrap();
2813        assert_eq!(
2814            dt,
2815            DataType::String {
2816                collation: "UTF8_BINARY".to_string()
2817            }
2818        );
2819
2820        let json_timestamp = serde_json::json!("timestamp");
2821        let dt = DataType::from_json(&json_timestamp).unwrap();
2822        assert_eq!(dt, DataType::Timestamp);
2823    }
2824
2825    #[test]
2826    fn test_from_json_str_simple() {
2827        let dt = DataType::from_json_str("\"integer\"").unwrap();
2828        assert_eq!(dt, DataType::Integer);
2829
2830        let dt = DataType::from_json_str("\"double\"").unwrap();
2831        assert_eq!(dt, DataType::Double);
2832
2833        let dt = DataType::from_json_str("\"boolean\"").unwrap();
2834        assert_eq!(dt, DataType::Boolean);
2835    }
2836
2837    #[test]
2838    fn test_from_json_complex_types() {
2839        // Array type
2840        let json_array = serde_json::json!({
2841            "type": "array",
2842            "elementType": "integer",
2843            "containsNull": true
2844        });
2845        let dt = DataType::from_json(&json_array).unwrap();
2846        match dt {
2847            DataType::Array {
2848                element_type,
2849                contains_null,
2850            } => {
2851                assert_eq!(*element_type, DataType::Integer);
2852                assert!(contains_null);
2853            }
2854            _ => panic!("Expected Array type"),
2855        }
2856
2857        // Map type
2858        let json_map = serde_json::json!({
2859            "type": "map",
2860            "keyType": "string",
2861            "valueType": "integer",
2862            "valueContainsNull": false
2863        });
2864        let dt = DataType::from_json(&json_map).unwrap();
2865        match dt {
2866            DataType::Map {
2867                key_type,
2868                value_type,
2869                value_contains_null,
2870            } => {
2871                assert_eq!(
2872                    *key_type,
2873                    DataType::String {
2874                        collation: "UTF8_BINARY".to_string()
2875                    }
2876                );
2877                assert_eq!(*value_type, DataType::Integer);
2878                assert!(!value_contains_null);
2879            }
2880            _ => panic!("Expected Map type"),
2881        }
2882
2883        // Struct type
2884        let json_struct = serde_json::json!({
2885            "type": "struct",
2886            "fields": [
2887                {
2888                    "name": "a",
2889                    "type": "integer",
2890                    "nullable": true,
2891                    "metadata": {}
2892                },
2893                {
2894                    "name": "b",
2895                    "type": "string",
2896                    "nullable": false,
2897                    "metadata": {}
2898                }
2899            ]
2900        });
2901        let dt = DataType::from_json(&json_struct).unwrap();
2902        match dt {
2903            DataType::Struct { fields } => {
2904                assert_eq!(fields.len(), 2);
2905                assert_eq!(fields[0].name, "a");
2906                assert_eq!(fields[1].name, "b");
2907            }
2908            _ => panic!("Expected Struct type"),
2909        }
2910    }
2911
2912    #[test]
2913    fn test_from_json_decimal() {
2914        let json_decimal = serde_json::json!("decimal(10,2)");
2915        let dt = DataType::from_json(&json_decimal).unwrap();
2916        match dt {
2917            DataType::Decimal { precision, scale } => {
2918                assert_eq!(precision, 10);
2919                assert_eq!(scale, 2);
2920            }
2921            _ => panic!("Expected Decimal type"),
2922        }
2923    }
2924
2925    #[test]
2926    fn test_from_json_geometry() {
2927        let json_geom = serde_json::json!("geometry(any)");
2928        let dt = DataType::from_json(&json_geom).unwrap();
2929        match dt {
2930            DataType::Geometry { srid } => {
2931                assert_eq!(srid, -1);
2932            }
2933            _ => panic!("Expected Geometry type"),
2934        }
2935    }
2936
2937    #[test]
2938    fn test_json_value_simple_types() {
2939        let int_val = DataType::Integer.json_value();
2940        assert_eq!(int_val, serde_json::json!("integer"));
2941
2942        let string_val = DataType::String {
2943            collation: "UTF8_BINARY".to_string(),
2944        }
2945        .json_value();
2946        assert_eq!(string_val, serde_json::json!("string"));
2947
2948        let bool_val = DataType::Boolean.json_value();
2949        assert_eq!(bool_val, serde_json::json!("boolean"));
2950    }
2951
2952    #[test]
2953    fn test_json_value_complex_types() {
2954        let array_val = DataType::Array {
2955            element_type: Box::new(DataType::Integer),
2956            contains_null: true,
2957        }
2958        .json_value();
2959        assert!(array_val.is_object());
2960        assert_eq!(
2961            array_val.get("type").and_then(|v| v.as_str()),
2962            Some("array")
2963        );
2964    }
2965
2966    #[test]
2967    fn test_to_ddl() {
2968        let struct_type = DataType::Struct {
2969            fields: vec![
2970                StructField {
2971                    name: "id".to_string(),
2972                    data_type: DataType::Integer,
2973                    nullable: false,
2974                    metadata: BTreeMap::new(),
2975                },
2976                StructField {
2977                    name: "name".to_string(),
2978                    data_type: DataType::String {
2979                        collation: "UTF8_BINARY".to_string(),
2980                    },
2981                    nullable: true,
2982                    metadata: BTreeMap::new(),
2983                },
2984            ],
2985        };
2986        let ddl = struct_type.to_ddl().unwrap();
2987        assert!(ddl.contains("id"));
2988        assert!(ddl.contains("name"));
2989        assert!(ddl.to_uppercase().contains("INT") || ddl.contains("int"));
2990        assert!(ddl.to_uppercase().contains("STRING") || ddl.contains("string"));
2991    }
2992
2993    #[test]
2994    fn test_tree_string() {
2995        let struct_type = DataType::Struct {
2996            fields: vec![
2997                StructField {
2998                    name: "id".to_string(),
2999                    data_type: DataType::Integer,
3000                    nullable: false,
3001                    metadata: BTreeMap::new(),
3002                },
3003                StructField {
3004                    name: "nested".to_string(),
3005                    data_type: DataType::Struct {
3006                        fields: vec![StructField {
3007                            name: "x".to_string(),
3008                            data_type: DataType::Double,
3009                            nullable: true,
3010                            metadata: BTreeMap::new(),
3011                        }],
3012                    },
3013                    nullable: true,
3014                    metadata: BTreeMap::new(),
3015                },
3016            ],
3017        };
3018        let tree = struct_type.tree_string().unwrap();
3019        assert!(tree.contains("id"));
3020        assert!(tree.contains("nested"));
3021        assert!(tree.contains("x"));
3022    }
3023
3024    #[test]
3025    fn test_to_proto_simple_types() {
3026        let int_proto = DataType::Integer.to_proto();
3027        assert!(int_proto.kind.is_some());
3028
3029        let string_proto = DataType::String {
3030            collation: "UTF8_BINARY".to_string(),
3031        }
3032        .to_proto();
3033        assert!(string_proto.kind.is_some());
3034
3035        let bool_proto = DataType::Boolean.to_proto();
3036        assert!(bool_proto.kind.is_some());
3037    }
3038
3039    #[test]
3040    fn test_to_proto_array_type() {
3041        let array_type = DataType::Array {
3042            element_type: Box::new(DataType::Integer),
3043            contains_null: false,
3044        };
3045        let proto = array_type.to_proto();
3046        assert!(proto.kind.is_some());
3047    }
3048
3049    #[test]
3050    fn test_to_proto_map_type() {
3051        let map_type = DataType::Map {
3052            key_type: Box::new(DataType::String {
3053                collation: "UTF8_BINARY".to_string(),
3054            }),
3055            value_type: Box::new(DataType::Integer),
3056            value_contains_null: true,
3057        };
3058        let proto = map_type.to_proto();
3059        assert!(proto.kind.is_some());
3060    }
3061
3062    #[test]
3063    fn test_to_proto_struct_type() {
3064        let struct_type = DataType::Struct {
3065            fields: vec![
3066                StructField {
3067                    name: "a".to_string(),
3068                    data_type: DataType::Integer,
3069                    nullable: true,
3070                    metadata: BTreeMap::new(),
3071                },
3072                StructField {
3073                    name: "b".to_string(),
3074                    data_type: DataType::String {
3075                        collation: "UTF8_BINARY".to_string(),
3076                    },
3077                    nullable: false,
3078                    metadata: BTreeMap::new(),
3079                },
3080            ],
3081        };
3082        let proto = struct_type.to_proto();
3083        assert!(proto.kind.is_some());
3084    }
3085
3086    #[test]
3087    fn test_display_trait() {
3088        let int_type = DataType::Integer;
3089        let display_str = format!("{}", int_type);
3090        assert_eq!(display_str, "int");
3091
3092        let array_type = DataType::Array {
3093            element_type: Box::new(DataType::Integer),
3094            contains_null: true,
3095        };
3096        let display_str = format!("{}", array_type);
3097        assert_eq!(display_str, "array<int>");
3098    }
3099
3100    #[test]
3101    fn test_interval_year_month() {
3102        let ym = DataType::YearMonthInterval {
3103            start_field: 0,
3104            end_field: 1,
3105        };
3106        assert_eq!(ym.simple_string(), "interval year to month");
3107    }
3108
3109    #[test]
3110    fn test_interval_day_time() {
3111        let dt = DataType::DayTimeInterval {
3112            start_field: 0,
3113            end_field: 3,
3114        };
3115        assert_eq!(dt.simple_string(), "interval day to second");
3116    }
3117
3118    #[test]
3119    fn test_udt_type() {
3120        let udt = DataType::Udt {
3121            type_str: "com.example.MyUDT".to_string(),
3122            jvm_class: Some("com.example.MyUDT".to_string()),
3123            python_class: None,
3124            serialized_python_class: None,
3125            sql_type: None,
3126        };
3127        assert_eq!(udt.simple_string(), "udt");
3128        assert_eq!(udt.type_name(), "udt");
3129    }
3130
3131    #[test]
3132    fn test_char_varchar() {
3133        let char_type = DataType::Char { length: 50 };
3134        assert_eq!(char_type.simple_string(), "char(50)");
3135
3136        let varchar_type = DataType::Varchar { length: 100 };
3137        assert_eq!(varchar_type.simple_string(), "varchar(100)");
3138    }
3139}