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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
//  DATA TYPE.rs
//    by Lut99
// 
//  Created:
//    30 Aug 2022, 12:02:57
//  Last edited:
//    17 Jan 2023, 15:13:15
//  Auto updated?
//    Yes
// 
//  Description:
//!   Defines a DataType enum that is optimized for execution (and
//!   transferral along the wire).
// 

use std::error::Error;
use std::fmt::{Display, Formatter, Result as FResult};

use serde::{Deserialize, Serialize};

use crate::spec::BuiltinClasses;


/***** AUXILLARY ERRORS *****/
/// Defines errors that occur when parsing DataTypes.
#[derive(Debug)]
pub enum DataTypeError {
    /// The given string was not recognized.
    UnknownDataType{ raw: String },
}

impl Display for DataTypeError {
    #[inline]
    fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
        use DataTypeError::*;
        match self {
            UnknownDataType{ raw } => write!(f, "Unknown data type '{raw}'"),
        }
    }
}

impl Error for DataTypeError {}





/***** LIBRARY *****/
/// Defines a DataType enum that is optimized for execution (and transferral along the wire).
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(tag = "kind")]
pub enum DataType {
    // Meta types
    /// Any type is accepted.
    #[serde(rename = "any")]
    Any,
    /// No type is accepted.
    #[serde(rename = "void")]
    Void,

    // Permissive types
    /// Allows both integers and reals.
    #[serde(rename = "num")]
    Numeric,
    /// Allows integers, reals and strings.
    #[serde(rename = "add")]
    Addable,
    /// Allows any callable object.
    #[serde(rename = "call")]
    Callable,
    /// Allows everything but Void
    #[serde(rename = "nvd")]
    NonVoid,

    // Atomic types (sorry Thomas)
    /// Only Boolean values are accepted (i.e., true or false, 1 or 0, yes or no, etc).
    #[serde(rename = "bool")]
    Boolean,
    /// Only Integral values are accepted (i.e., non-decimal numbers)
    #[serde(rename = "int")]
    Integer,
    /// Only Real values are accepted (i.e., decimal numbers)
    #[serde(rename = "real")]
    Real,
    /// Only String values are accepted (i.e., arrays of characters)
    #[serde(rename = "str")]
    String,
    /// Only Semantic versioning are accepted (i.e., major.minor.patch)
    #[serde(rename = "ver")]
    Semver,

    // Composite types (sorry Thomas)
    /// Arrays (i.e., a memory area divided into homogeneous types).
    #[serde(rename = "arr")]
    Array{
        #[serde(rename = "t")]
        elem_type : Box<DataType>,
    },
    /// Functions (i.e., executable pieces of code). Contains both the types (and arity) of its arguments and the return type.
    #[serde(rename = "func")]
    Function{
        #[serde(rename = "a")]
        args : Vec<DataType>,
        #[serde(rename = "t")]
        ret  : Box<DataType>,
    },
    /// Classes (i.e., a memory area divided into heterogeneous types). The usize indexes the signature into the Workflow's global buffers.
    #[serde(rename = "clss")]
    Class{
        #[serde(rename = "n")]
        name : String,
    },
    /// An externally represented dataset. The string is its identifier.
    #[serde(rename = "data")]
    Data,
    /// An externally represented dataset but one that may also be generated by functions.
    #[serde(rename = "res")]
    IntermediateResult,
}

impl DataType {
    /// Returns if this DataType is the same or at least targeted of the given one.
    /// 
    /// A common use-case for this function is checking return types, where the return type would be the given one.
    /// 
    /// # Arguments
    /// - `allowed`: The DataType that describes what is allowed.
    /// 
    /// # Returns
    /// Whether or not this DataType "is the same" as the other one.
    #[inline]
    pub fn allowed_by(&self, other: &Self) -> bool {
        use DataType::*;
        match (self, other) {
            // Individual cases
            (Data, IntermediateResult) => true,

            // Group cases
            (Integer, Numeric) => true,
            (Real, Numeric)    => true,

            (Integer, Addable) => true,
            (Real, Addable)    => true,
            (String, Addable)  => true,

            (Function{ .. }, Callable)  => true,

            (Void, NonVoid) => false,
            (_, NonVoid)    => true,

            (Any, _)  => true,
            (_, Any)  => true,

            // Recursive cases
            (Array{ elem_type: lhs }, Array{ elem_type: rhs }) => lhs.allowed_by(rhs),

            // General case
            (t1, t2) => t1 == t2,
        }
    }



    /// Returns if this DataType is Void (i.e., no value).
    #[inline]
    pub fn is_void(&self) -> bool { matches!(self, Self::Void) }
}

impl Display for DataType {
    #[inline]
    fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
        use DataType::*;
        match self {
            Any  => write!(f, "Any"),
            Void => write!(f, "Void"),

            Numeric  => write!(f, "Numeric"),
            Addable  => write!(f, "Addable (Numeric or String)"),
            Callable => write!(f, "Callable (Function)"),
            NonVoid  => write!(f, "non-Void"),

            Boolean => write!(f, "Boolean"),
            Integer => write!(f, "Integer"),
            Real    => write!(f, "Real"),
            String  => write!(f, "String"),
            Semver  => write!(f, "Semver"),

            Array{ elem_type }    => write!(f, "Array<{elem_type}>"),
            Function{ args, ret } => write!(f, "Func<({}){}>", args.iter().map(|t| format!("{t}")).collect::<Vec<std::string::String>>().join(", "), if **ret != DataType::Void { format!(" -> {ret}") } else { std::string::String::new() }),
            Class{ name }         => write!(f, "Class<{name}>"),
            Data                  => write!(f, "Data"),
            IntermediateResult    => write!(f, "IntermediateResult"),
        }
    }
}

impl From<brane_dsl::DataType> for DataType {
    #[inline]
    fn from(value: brane_dsl::DataType) -> Self {
        use brane_dsl::DataType::*;
        match value {
            Any  => Self::Any,
            Void => Self::Void,

            Boolean => Self::Boolean,
            Integer => Self::Integer,
            Real    => Self::Real,
            String  => Self::String,
            Semver  => Self::Semver,

            Array(a)      => Self::Array{ elem_type: a.into() },
            Function(sig) => Self::Function{ args: sig.args.into_iter().map(|d| d.into()).collect(), ret: Box::new(sig.ret.into()) },
            Class(name)   => {
                // Match if 'Data' or 'IntermediateResult'
                if name == BuiltinClasses::Data.name() {
                    Self::Data
                } else if name == BuiltinClasses::IntermediateResult.name() {
                    Self::IntermediateResult
                } else {
                    Self::Class{ name }
                }
            },
        }
    }
}

impl From<&brane_dsl::DataType> for DataType {
    #[inline]
    fn from(value: &brane_dsl::DataType) -> Self {
        use brane_dsl::DataType::*;
        match value {
            Any  => Self::Any,
            Void => Self::Void,

            Boolean => Self::Boolean,
            Integer => Self::Integer,
            Real    => Self::Real,
            String  => Self::String,
            Semver  => Self::Semver,

            Array(a)      => Self::Array{ elem_type: a.into() },
            Function(sig) => Self::Function{ args: sig.args.iter().map(|d| d.into()).collect(), ret: Box::new((&sig.ret).into()) },
            Class(name)   => {
                // Match if 'Data' or 'IntermediateResult'
                if name == BuiltinClasses::Data.name() {
                    Self::Data
                } else if name == BuiltinClasses::IntermediateResult.name() {
                    Self::IntermediateResult
                } else {
                    Self::Class{ name: name.clone() }
                }
            },
        }
    }
}

impl From<Box<brane_dsl::DataType>> for Box<DataType> {
    #[inline]
    fn from(value: Box<brane_dsl::DataType>) -> Self {
        Self::from(&value)
    }
}

impl From<&Box<brane_dsl::DataType>> for Box<DataType> {
    #[inline]
    fn from(value: &Box<brane_dsl::DataType>) -> Self {
        Box::new(DataType::from(value.as_ref()))
    }
}

impl From<&str> for DataType {
    fn from(value: &str) -> Self {
        // First: any arrays are done recursively
        if !value.is_empty() && &value[..1] == "[" && &value[value.len() - 1..] == "]" {
            return Self::Array{ elem_type: Box::new(Self::from(&value[1..value.len() - 1])) };
        } else if value.len() >= 2 && &value[value.len() - 2..] == "[]" {
            return Self::Array{ elem_type: Box::new(Self::from(&value[..value.len() - 2])) };
        }

        // Otherwise, match literals & classes
        use DataType::*;
        match value {
            // Literal types
            "bool" | "boolean" => Boolean,
            "int"  | "integer" => Integer,
            "float" | "real"   => Real,
            "string"           => String,

            // The rest is always a class unless it's data or an intermediate result
            value => if value == BuiltinClasses::Data.name() {
                Data
            } else if value == BuiltinClasses::IntermediateResult.name() {
                IntermediateResult
            } else {
                Class{ name: value.into() }
            },
        }
    }
}

impl From<&String> for DataType {
    #[inline]
    fn from(value: &String) -> Self {
        // Use the string-one
        Self::from(value.as_str())
    }
}

impl From<String> for DataType {
    #[inline]
    fn from(value: String) -> Self {
        // Use the string-one
        Self::from(value.as_str())
    }
}