1use std::fmt::{Debug, Display};
2
3use crate::value::Value;
4
5#[derive(Clone, PartialEq)]
6pub enum Type {
7 Value,
8 String,
9 Fn {
10 args: Vec<Type>,
11 variadic_arg: Option<Box<Type>>,
12 returns: Box<Type>,
13 },
14 Bool,
15 Unknown,
16}
17
18impl Type {
19 pub fn name(&self) -> String {
20 match self {
21 Type::Value => "Value".to_string(),
22 Type::String => "String".to_string(),
23 Type::Fn {
24 args,
25 variadic_arg,
26 returns,
27 } => {
28 let mut args: Vec<String> = args.iter().map(|arg| arg.name()).collect();
29
30 if let Some(varg) = variadic_arg {
31 args.push(format!("...{}", varg.name()));
32 }
33
34 let args = args.join(", ");
35
36 let returns = returns.name();
37
38 format!("Fn({args}) -> {returns}")
39 }
40 Type::Bool => "Bool".to_string(),
41 Type::Unknown => "Unknown".to_string(),
42 }
43 }
44}
45
46impl Display for Type {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 write!(f, "{}", self.name())
49 }
50}
51
52impl Debug for Type {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 write!(f, "{}", self.name())
55 }
56}
57
58impl From<Value> for Type {
59 fn from(value: Value) -> Self {
60 match value {
61 Value::String(_) => Type::String,
62 Value::Fn(builtin_fn) => {
63 let mut args: Vec<Type> =
64 builtin_fn.args.iter().map(|arg| arg.ty.clone()).collect();
65
66 let variadic_arg = builtin_fn
67 .args
68 .last()
69 .filter(|arg| arg.variadic)
70 .map(|arg| Box::new(arg.ty.clone()));
71
72 if variadic_arg.is_some() {
73 args.pop();
74 }
75
76 Type::Fn {
77 args,
78 variadic_arg,
79 returns: Box::new(builtin_fn.return_type.clone()),
80 }
81 }
82 Value::Bool(_) => Type::Bool,
83 Value::Type(ty) => *ty.clone(),
84 }
85 }
86}
87
88#[cfg(test)]
89mod from_tests {
90 use std::rc::Rc;
91
92 use crate::prelude::{BuiltinFn, FnArg};
93
94 use super::*;
95
96 #[test]
97 fn test_from_type_value() {
98 let type_value = Value::Type(Type::String.into());
99 let ty: Type = type_value.into();
100 assert_eq!(Type::String, ty);
101 }
102
103 #[test]
104 fn test_get_type_type_value() {
105 let string_value = Value::Type(Type::String.into());
106 let ty: Type = string_value.get_type();
107 assert_eq!(Type::String, ty);
108 }
109
110 #[test]
111 fn test_from_string_value() {
112 let string_value = Value::String("test".to_string());
113 let ty: Type = string_value.into();
114 assert_eq!(Type::String, ty);
115 }
116
117 #[test]
118 fn test_get_type_string_value() {
119 let string_value = Value::String("test".to_string());
120 let ty: Type = string_value.get_type();
121 assert_eq!(Type::String, ty);
122 }
123
124 #[test]
125 fn test_from_bool_value() {
126 let bool_value = Value::Bool(true);
127 let ty: Type = bool_value.into();
128 assert_eq!(Type::Bool, ty);
129 }
130
131 #[test]
132 fn test_get_type_bool_value() {
133 let bool_value = Value::Bool(true);
134 let ty: Type = bool_value.get_type();
135 assert_eq!(Type::Bool, ty);
136 }
137
138 #[test]
139 fn test_from_fn_value() {
140 let builtin_fn = Value::Fn(Rc::new(BuiltinFn {
141 name: "test".to_string(),
142 args: vec![FnArg::new("a", Type::Value)],
143 return_type: Type::String,
144 func: Rc::new(|_| Value::String("".to_string())),
145 }));
146
147 let ty: Type = builtin_fn.into();
148
149 assert_eq!(
150 Type::Fn {
151 args: vec![Type::Value],
152 variadic_arg: None,
153 returns: Box::new(Type::String),
154 },
155 ty
156 );
157 }
158
159 #[test]
160 fn test_get_type_fn_value() {
161 let builtin_fn = Value::Fn(Rc::new(BuiltinFn {
162 name: "test".to_string(),
163 args: vec![FnArg::new("a", Type::Value)],
164 return_type: Type::String,
165 func: Rc::new(|_| Value::String("".to_string())),
166 }));
167
168 let ty: Type = builtin_fn.get_type();
169
170 assert_eq!(
171 Type::Fn {
172 args: vec![Type::Value],
173 variadic_arg: None,
174 returns: Box::new(Type::String),
175 },
176 ty
177 );
178 }
179}
180
181#[cfg(test)]
182mod name_and_display_tests {
183 use super::*;
184
185 #[test]
186 fn test_name_value() {
187 assert_eq!("Value", Type::Value.name());
188 }
189
190 #[test]
191 fn test_name_string() {
192 assert_eq!("String", Type::String.name());
193 }
194
195 #[test]
196 fn test_name_fn_0_args() {
197 assert_eq!(
198 "Fn() -> String",
199 Type::Fn {
200 args: vec![],
201 variadic_arg: None,
202 returns: Type::String.into(),
203 }
204 .name()
205 );
206 }
207
208 #[test]
209 fn test_name_fn_0_args_and_varadic() {
210 assert_eq!(
211 "Fn(...Value) -> String",
212 Type::Fn {
213 args: vec![],
214 variadic_arg: Some(Type::Value.into()),
215 returns: Type::String.into(),
216 }
217 .name()
218 );
219 }
220
221 #[test]
222 fn test_name_fn_1_args() {
223 assert_eq!(
224 "Fn(Value) -> String",
225 Type::Fn {
226 args: vec![Type::Value],
227 variadic_arg: None,
228 returns: Type::String.into(),
229 }
230 .name()
231 );
232 }
233
234 #[test]
235 fn test_name_fn_2_args() {
236 assert_eq!(
237 "Fn(Value, String) -> String",
238 Type::Fn {
239 args: vec![Type::Value, Type::String],
240 variadic_arg: None,
241 returns: Type::String.into(),
242 }
243 .name()
244 );
245 }
246
247 #[test]
248 fn test_name_fn_2_args_and_varadic() {
249 assert_eq!(
250 "Fn(Value, String, ...Value) -> String",
251 Type::Fn {
252 args: vec![Type::Value, Type::String],
253 variadic_arg: Some(Type::Value.into()),
254 returns: Type::String.into(),
255 }
256 .name()
257 );
258 }
259
260 #[test]
261 fn test_name_bool() {
262 assert_eq!("Bool", Type::Bool.name());
263 }
264}