csv_nose/
field_type.rs

1use std::fmt;
2
3/// Data type detected for a CSV field.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
5pub enum Type {
6    /// Unsigned integer (non-negative whole number).
7    Unsigned,
8    /// Signed integer (whole number, possibly negative).
9    Signed,
10    /// Floating point number.
11    Float,
12    /// Boolean value (true/false, yes/no, 0/1, etc.).
13    Boolean,
14    /// Date value (without time component).
15    Date,
16    /// `DateTime` value (date with time component).
17    DateTime,
18    /// Null/empty value.
19    NULL,
20    /// Text/string value (fallback type).
21    #[default]
22    Text,
23}
24
25impl fmt::Display for Type {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        match self {
28            Type::Unsigned => write!(f, "Unsigned"),
29            Type::Signed => write!(f, "Signed"),
30            Type::Float => write!(f, "Float"),
31            Type::Boolean => write!(f, "Boolean"),
32            Type::Date => write!(f, "Date"),
33            Type::DateTime => write!(f, "DateTime"),
34            Type::NULL => write!(f, "NULL"),
35            Type::Text => write!(f, "Text"),
36        }
37    }
38}
39
40impl Type {
41    /// Returns true if this type is numeric.
42    pub fn is_numeric(&self) -> bool {
43        matches!(self, Type::Unsigned | Type::Signed | Type::Float)
44    }
45
46    /// Returns true if this type is temporal.
47    pub fn is_temporal(&self) -> bool {
48        matches!(self, Type::Date | Type::DateTime)
49    }
50
51    /// Returns the type priority for type inference.
52    /// Higher priority types are preferred when merging types.
53    pub const fn priority(&self) -> u8 {
54        match self {
55            Type::NULL => 0,
56            Type::Boolean => 1,
57            Type::Unsigned => 2,
58            Type::Signed => 3,
59            Type::Float => 4,
60            Type::Date => 5,
61            Type::DateTime => 6,
62            Type::Text => 7,
63        }
64    }
65
66    /// Merge two types, returning the most general type that can represent both.
67    pub fn merge(self, other: Type) -> Type {
68        if self == other {
69            return self;
70        }
71
72        // NULL can be promoted to any type
73        if self == Type::NULL {
74            return other;
75        }
76        if other == Type::NULL {
77            return self;
78        }
79
80        // Numeric type promotion
81        match (self, other) {
82            (Type::Unsigned, Type::Signed) | (Type::Signed, Type::Unsigned) => Type::Signed,
83            (Type::Unsigned, Type::Float)
84            | (Type::Float, Type::Unsigned)
85            | (Type::Signed, Type::Float)
86            | (Type::Float, Type::Signed) => Type::Float,
87            (Type::Date, Type::DateTime) | (Type::DateTime, Type::Date) => Type::DateTime,
88            // Everything else becomes Text
89            _ => Type::Text,
90        }
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn test_type_merge() {
100        assert_eq!(Type::Unsigned.merge(Type::Unsigned), Type::Unsigned);
101        assert_eq!(Type::Unsigned.merge(Type::Signed), Type::Signed);
102        assert_eq!(Type::Unsigned.merge(Type::Float), Type::Float);
103        assert_eq!(Type::NULL.merge(Type::Unsigned), Type::Unsigned);
104        assert_eq!(Type::Date.merge(Type::DateTime), Type::DateTime);
105        assert_eq!(Type::Boolean.merge(Type::Text), Type::Text);
106    }
107}