1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
use std::fmt;

/// Represent the data types for values to be used in type checker
#[derive(Clone)]
pub enum DataType {
    /// Represent general type so can be equal to any other type
    Any,
    /// Represent String Type
    Text,
    /// Represent Integer 64 bit type
    Integer,
    /// Represent Float 64 bit type
    Float,
    /// Represent Boolean (true | false) type
    Boolean,
    /// Represent Date type
    Date,
    /// Represent Time type
    Time,
    /// Represent Date with Time type
    DateTime,
    /// Represent `Undefined` value
    Undefined,
    /// Represent `NULL` value
    Null,
    /// Represent a set of valid variant of types
    Variant(Vec<DataType>),
    /// Represent an optional type so it can passed or not, must be last parameter
    Optional(Box<DataType>),
    /// Represent variable arguments so can pass 0 or more value with spastic type, must be last parameter
    Varargs(Box<DataType>),
    /// Represent dynamic type that calculated depending on other types (for example depending on Parameters)
    /// For now the main use case is to use it to calculate return type of function that has many variants
    Dynamic(fn(&[DataType]) -> DataType),
}

impl PartialEq for DataType {
    fn eq(&self, other: &Self) -> bool {
        if self.is_any() || other.is_any() {
            return true;
        }

        if let DataType::Variant(types) = self {
            for data_type in types {
                if data_type == other {
                    return true;
                }
            }
            return false;
        }

        if let DataType::Variant(types) = other {
            for data_type in types {
                if data_type == self {
                    return true;
                }
            }
            return false;
        }

        if let DataType::Optional(optional_type) = self {
            return optional_type.as_ref() == other;
        }

        if let DataType::Optional(optional_type) = other {
            return optional_type.as_ref() == self;
        }

        if let DataType::Varargs(data_type) = self {
            return data_type.as_ref() == other;
        }

        if let DataType::Varargs(data_type) = other {
            return data_type.as_ref() == self;
        }

        if self.is_bool() && other.is_bool() {
            return true;
        }

        if self.is_int() && other.is_int() {
            return true;
        }

        if self.is_float() && other.is_float() {
            return true;
        }

        if self.is_text() && other.is_text() {
            return true;
        }

        if self.is_date() && other.is_date() {
            return true;
        }

        if self.is_time() && other.is_time() {
            return true;
        }

        if self.is_datetime() && other.is_datetime() {
            return true;
        }

        if self.is_null() && other.is_null() {
            return true;
        }

        if self.is_undefined() && other.is_undefined() {
            return true;
        }

        false
    }
}

impl fmt::Display for DataType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            DataType::Any => write!(f, "Any"),
            DataType::Text => write!(f, "Text"),
            DataType::Integer => write!(f, "Integer"),
            DataType::Float => write!(f, "Float"),
            DataType::Boolean => write!(f, "Boolean"),
            DataType::Date => write!(f, "Date"),
            DataType::Time => write!(f, "Time"),
            DataType::DateTime => write!(f, "DateTime"),
            DataType::Undefined => write!(f, "Undefined"),
            DataType::Null => write!(f, "Null"),
            DataType::Variant(types) => {
                write!(f, "[")?;
                for (pos, data_type) in types.iter().enumerate() {
                    write!(f, "{}", data_type)?;
                    if pos != types.len() - 1 {
                        write!(f, " | ")?;
                    }
                }
                write!(f, "]")
            }
            DataType::Optional(data_type) => {
                write!(f, "{}?", data_type)
            }
            DataType::Varargs(data_type) => {
                write!(f, "...{}", data_type)
            }
            DataType::Dynamic(_function) => {
                write!(f, "DynamicType")
            }
        }
    }
}

impl DataType {
    pub fn is_any(&self) -> bool {
        matches!(self, DataType::Any)
    }

    pub fn is_bool(&self) -> bool {
        matches!(self, DataType::Boolean)
    }

    pub fn is_int(&self) -> bool {
        matches!(self, DataType::Integer)
    }

    pub fn is_float(&self) -> bool {
        matches!(self, DataType::Float)
    }

    pub fn is_number(&self) -> bool {
        self.is_int() || self.is_float()
    }

    pub fn is_text(&self) -> bool {
        matches!(self, DataType::Text)
    }

    pub fn is_time(&self) -> bool {
        matches!(self, DataType::Time)
    }

    pub fn is_date(&self) -> bool {
        matches!(self, DataType::Date)
    }

    pub fn is_datetime(&self) -> bool {
        matches!(self, DataType::DateTime)
    }

    pub fn is_null(&self) -> bool {
        matches!(self, DataType::Null)
    }

    pub fn is_undefined(&self) -> bool {
        matches!(self, DataType::Undefined)
    }

    pub fn is_variant(&self) -> bool {
        matches!(self, DataType::Variant(_))
    }

    pub fn is_variant_with(&self, data_type: &DataType) -> bool {
        match self {
            DataType::Variant(types) => types.contains(data_type),
            _ => false,
        }
    }

    pub fn is_optional(&self) -> bool {
        matches!(self, DataType::Optional(_))
    }

    pub fn is_varargs(&self) -> bool {
        matches!(self, DataType::Varargs(_))
    }
}

/// Function to used with Dynamic Data type
/// The result is the same type as the first parameter
pub fn same_type_as_first_parameter(parameters: &[DataType]) -> DataType {
    parameters[0].clone()
}