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 Fn {
12 args: Vec<Type>,
13 variadic_arg: Option<Box<Type>>,
14 returns: Box<Type>,
15 },
16 Bool,
17 Type(Box<Type>),
18 Unknown,
19}
20
21impl Type {
22 pub fn from(name: &str) -> Self {
23 match name {
24 "String" => Type::String,
25 "Bool" => Type::Bool,
26 "Value" => Type::Value,
27 _ => {
28 if let Some(captures) = Regex::new(r"^Type<(.+)>$").unwrap().captures(name) {
29 return Type::Type(Type::from(&captures[1]).into());
30 }
31
32 if let Some(captures) = Regex::new(
33 r"^Fn\((?P<args>(?:\w+(?:,\s*)?)*)\s*(?:\.\.\.(?P<varg>\w+))?\)\s*->\s*(?P<return>\w+)$",
34 ).unwrap().captures(name) {
35 let args_str = captures.name("args").map_or("", |m| m.as_str());
36 let variadic_str = captures.name("varg").map_or("", |m| m.as_str());
37 let return_type_str = captures.name("return").unwrap().as_str();
38
39 let args: Vec<Type> = if args_str.trim().is_empty() {
40 vec![]
41 } else {
42 args_str.split(',')
43 .filter_map(|s| {
44 let trimmed = s.trim();
45 if trimmed.is_empty() { None } else { Some(Type::from(trimmed)) }
46 })
47 .collect()
48 };
49
50 let variadic_arg = if variadic_str.is_empty() {
51 None
52 } else {
53 Some(Type::from(variadic_str).into())
54 };
55
56 let returns = Type::from(return_type_str).into();
57
58 return Type::Fn {
59 args,
60 variadic_arg,
61 returns,
62 };
63 }
64
65 Type::Unknown
66 }
67 }
68 }
69
70 pub fn name(&self) -> String {
71 match self {
72 Type::Value => "Value".to_string(),
73 Type::String => "String".to_string(),
74 Type::Fn {
75 args,
76 variadic_arg,
77 returns,
78 } => {
79 let mut args: Vec<String> = args.iter().map(|arg| arg.name()).collect();
80
81 if let Some(varg) = variadic_arg {
82 args.push(format!("...{}", varg.name()));
83 }
84
85 let args = args.join(", ");
86
87 let returns = returns.name();
88
89 format!("Fn({args}) -> {returns}")
90 }
91 Type::Bool => "Bool".to_string(),
92 Type::Type(ty) => format!("{}", ty.name()),
93 Type::Unknown => "Unknown".to_string(),
94 }
95 }
96
97 pub fn is_type(&self) -> bool {
98 match self {
99 Type::Type(_) => true,
100 _ => false,
101 }
102 }
103}
104
105impl Display for Type {
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 write!(f, "{}", self.name())
108 }
109}
110
111impl Debug for Type {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 write!(f, "{}", self.name())
114 }
115}
116
117impl From<Value> for Type {
118 fn from(value: Value) -> Self {
119 match value {
120 Value::String(_) => Type::String,
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 == false)
153 .map(|x| x.ty.clone())
154 .collect();
155 let varg = value
156 .args
157 .iter()
158 .find(|x| x.variadic == true)
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}