1use std::fmt::{Debug, Display};
2
3use regex::Regex;
4
5use crate::{prelude::BuiltinFn, value::Value};
6
7#[derive(Clone, PartialEq)]
8pub enum Type {
9 Value,
10 String,
11 Number,
12 Fn {
13 args: Vec<Type>,
14 variadic_arg: Option<Box<Type>>,
15 returns: Box<Type>,
16 },
17 Bool,
18 Type(Box<Type>),
19 Unknown,
20}
21
22impl Type {
23 pub fn from(name: &str) -> Self {
24 match name {
25 "String" => Type::String,
26 "Bool" => Type::Bool,
27 "Value" => Type::Value,
28 _ => {
29 if let Some(captures) = Regex::new(r"^Type<(.+)>$").unwrap().captures(name) {
30 return Type::Type(Type::from(&captures[1]).into());
31 }
32
33 if let Some(captures) = Regex::new(
34 r"^Fn\((?P<args>(?:\w+(?:,\s*)?)*)\s*(?:\.\.\.(?P<varg>\w+))?\)\s*->\s*(?P<return>\w+)$",
35 ).unwrap().captures(name) {
36 let args_str = captures.name("args").map_or("", |m| m.as_str());
37 let variadic_str = captures.name("varg").map_or("", |m| m.as_str());
38 let return_type_str = captures.name("return").unwrap().as_str();
39
40 let args: Vec<Type> = if args_str.trim().is_empty() {
41 vec![]
42 } else {
43 args_str.split(',')
44 .filter_map(|s| {
45 let trimmed = s.trim();
46 if trimmed.is_empty() { None } else { Some(Type::from(trimmed)) }
47 })
48 .collect()
49 };
50
51 let variadic_arg = if variadic_str.is_empty() {
52 None
53 } else {
54 Some(Type::from(variadic_str).into())
55 };
56
57 let returns = Type::from(return_type_str).into();
58
59 return Type::Fn {
60 args,
61 variadic_arg,
62 returns,
63 };
64 }
65
66 Type::Unknown
67 }
68 }
69 }
70
71 pub fn name(&self) -> String {
72 match self {
73 Type::Value => "Value".to_string(),
74 Type::String => "String".to_string(),
75 Type::Number => "Number".to_string(),
76 Type::Fn {
77 args,
78 variadic_arg,
79 returns,
80 } => {
81 let mut args: Vec<String> = args.iter().map(|arg| arg.name()).collect();
82
83 if let Some(varg) = variadic_arg {
84 args.push(format!("...{}", varg.name()));
85 }
86
87 let args = args.join(", ");
88
89 let returns = returns.name();
90
91 format!("Fn({args}) -> {returns}")
92 }
93 Type::Bool => "Bool".to_string(),
94 Type::Type(ty) => ty.name().to_string(),
95 Type::Unknown => "Unknown".to_string(),
96 }
97 }
98
99 pub fn is_type(&self) -> bool {
100 matches!(self, Type::Type(_))
101 }
102}
103
104impl Display for Type {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 write!(f, "{}", self.name())
107 }
108}
109
110impl Debug for Type {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 write!(f, "{}", self.name())
113 }
114}
115
116impl From<Value> for Type {
117 fn from(value: Value) -> Self {
118 match value {
119 Value::String(_) => Type::String,
120 Value::Number(_) => Type::Number,
121 Value::Fn(builtin_fn) => {
122 let mut args: Vec<Type> =
123 builtin_fn.args.iter().map(|arg| arg.ty.clone()).collect();
124
125 let variadic_arg = builtin_fn
126 .args
127 .last()
128 .filter(|arg| arg.variadic)
129 .map(|arg| Box::new(arg.ty.clone()));
130
131 if variadic_arg.is_some() {
132 args.pop();
133 }
134
135 Type::Fn {
136 args,
137 variadic_arg,
138 returns: Box::new(builtin_fn.return_type.clone()),
139 }
140 }
141 Value::Bool(_) => Type::Bool,
142 Value::Type(ty) => *ty.clone(),
143 }
144 }
145}
146
147impl From<BuiltinFn<'static>> for Type {
148 fn from(value: BuiltinFn) -> Self {
149 let args: Vec<Type> = value
150 .args
151 .iter()
152 .filter(|x| !x.variadic)
153 .map(|x| x.ty.clone())
154 .collect();
155 let varg = value
156 .args
157 .iter()
158 .find(|x| x.variadic)
159 .map(|x| Box::new(x.ty.clone()));
160 let returns = value.return_type.clone();
161
162 Self::Fn {
163 args,
164 variadic_arg: varg,
165 returns: returns.into(),
166 }
167 }
168}
169
170#[cfg(test)]
171mod from_tests {
172 use crate::prelude::BuiltinFn;
173
174 use super::*;
175
176 #[test]
177 fn test_from_type_value() {
178 let type_value = Value::Type(Type::String.into());
179 let ty: Type = type_value.into();
180 assert_eq!(Type::String, ty);
181 }
182
183 #[test]
184 fn test_get_type_type_value() {
185 let string_value = Value::Type(Type::String.into());
186 let ty: Type = string_value.get_type();
187 assert_eq!(Type::String, ty);
188 }
189
190 #[test]
191 fn test_from_string_value() {
192 let string_value = Value::String("test".to_string());
193 let ty: Type = string_value.into();
194 assert_eq!(Type::String, ty);
195 }
196
197 #[test]
198 fn test_get_type_string_value() {
199 let string_value = Value::String("test".to_string());
200 let ty: Type = string_value.get_type();
201 assert_eq!(Type::String, ty);
202 }
203
204 #[test]
205 fn test_from_bool_value() {
206 let bool_value = Value::Bool(true);
207 let ty: Type = bool_value.into();
208 assert_eq!(Type::Bool, ty);
209 }
210
211 #[test]
212 fn test_get_type_bool_value() {
213 let bool_value = Value::Bool(true);
214 let ty: Type = bool_value.get_type();
215 assert_eq!(Type::Bool, ty);
216 }
217
218 #[test]
219 fn test_from_fn_value() {
220 let builtin_fn = Value::Fn(BuiltinFn::ID.into());
221
222 let ty: Type = builtin_fn.into();
223
224 assert_eq!(
225 Type::Fn {
226 args: vec![Type::Value],
227 variadic_arg: None,
228 returns: Box::new(Type::Value),
229 },
230 ty
231 );
232 }
233
234 #[test]
235 fn test_get_type_fn_value() {
236 let builtin_fn = Value::Fn(BuiltinFn::ID.into());
237
238 let ty: Type = builtin_fn.get_type();
239
240 assert_eq!(
241 Type::Fn {
242 args: vec![Type::Value],
243 variadic_arg: None,
244 returns: Box::new(Type::Value),
245 },
246 ty
247 );
248 }
249}
250
251#[cfg(test)]
252mod name_and_display_tests {
253 use super::*;
254
255 #[test]
256 fn test_name_value() {
257 assert_eq!("Value", Type::Value.name());
258 }
259
260 #[test]
261 fn test_name_string() {
262 assert_eq!("String", Type::String.name());
263 }
264
265 #[test]
266 fn test_name_fn_0_args() {
267 assert_eq!(
268 "Fn() -> String",
269 Type::Fn {
270 args: vec![],
271 variadic_arg: None,
272 returns: Type::String.into(),
273 }
274 .name()
275 );
276 }
277
278 #[test]
279 fn test_name_fn_0_args_and_varadic() {
280 assert_eq!(
281 "Fn(...Value) -> String",
282 Type::Fn {
283 args: vec![],
284 variadic_arg: Some(Type::Value.into()),
285 returns: Type::String.into(),
286 }
287 .name()
288 );
289 }
290
291 #[test]
292 fn test_name_fn_1_args() {
293 assert_eq!(
294 "Fn(Value) -> String",
295 Type::Fn {
296 args: vec![Type::Value],
297 variadic_arg: None,
298 returns: Type::String.into(),
299 }
300 .name()
301 );
302 }
303
304 #[test]
305 fn test_name_fn_2_args() {
306 assert_eq!(
307 "Fn(Value, String) -> String",
308 Type::Fn {
309 args: vec![Type::Value, Type::String],
310 variadic_arg: None,
311 returns: Type::String.into(),
312 }
313 .name()
314 );
315 }
316
317 #[test]
318 fn test_name_fn_2_args_and_varadic() {
319 assert_eq!(
320 "Fn(Value, String, ...Value) -> String",
321 Type::Fn {
322 args: vec![Type::Value, Type::String],
323 variadic_arg: Some(Type::Value.into()),
324 returns: Type::String.into(),
325 }
326 .name()
327 );
328 }
329
330 #[test]
331 fn test_name_bool() {
332 assert_eq!("Bool", Type::Bool.name());
333 }
334}
335
336#[cfg(test)]
337mod from_string_tests {
338 use super::*;
339
340 use pretty_assertions::assert_eq;
341
342 #[test]
343 fn from_string_to_string() {
344 let ty = Type::from("String");
345
346 assert_eq!(Type::String, ty);
347 }
348
349 #[test]
350 fn from_string_to_bool() {
351 let ty = Type::from("Bool");
352
353 assert_eq!(Type::Bool, ty);
354 }
355
356 #[test]
357 fn from_string_to_value() {
358 let ty = Type::from("Value");
359
360 assert_eq!(Type::Value, ty);
361 }
362
363 #[test]
364 fn from_string_to_unknown() {
365 let ty = Type::from("Unknown");
366
367 assert_eq!(Type::Unknown, ty);
368 }
369
370 #[test]
371 fn from_string_to_type_string() {
372 let ty = Type::from("Type<String>");
373
374 assert_eq!(Type::Type(Type::String.into()), ty);
375 }
376
377 #[test]
378 fn from_string_to_type_fn_zero_args() {
379 let ty = Type::from("Fn() -> Value");
380
381 assert_eq!(
382 Type::Fn {
383 args: vec![],
384 variadic_arg: None,
385 returns: Type::Value.into()
386 },
387 ty
388 );
389 }
390
391 #[test]
392 fn from_string_to_type_fn_single_arg() {
393 let ty = Type::from("Fn(String) -> Value");
394
395 assert_eq!(
396 Type::Fn {
397 args: vec![Type::String],
398 variadic_arg: None,
399 returns: Type::Value.into()
400 },
401 ty
402 );
403 }
404
405 #[test]
406 fn from_string_to_type_fn_single_varg() {
407 let ty = Type::from("Fn(...String) -> Value");
408
409 assert_eq!(
410 Type::Fn {
411 args: vec![],
412 variadic_arg: Some(Type::String.into()),
413 returns: Type::Value.into()
414 },
415 ty
416 );
417 }
418
419 #[test]
420 fn from_string_to_type_fn_multiple_args() {
421 let ty = Type::from("Fn(String, Bool, String) -> String");
422
423 assert_eq!(
424 Type::Fn {
425 args: vec![Type::String, Type::Bool, Type::String],
426 variadic_arg: None,
427 returns: Type::String.into()
428 },
429 ty
430 );
431 }
432
433 #[test]
434 fn from_string_to_type_fn_multiple_args_and_varg() {
435 let ty = Type::from("Fn(String, Bool, String, ...Value) -> String");
436
437 assert_eq!(
438 Type::Fn {
439 args: vec![Type::String, Type::Bool, Type::String],
440 variadic_arg: Some(Type::Value.into()),
441 returns: Type::String.into()
442 },
443 ty
444 );
445 }
446}