reqlang_expr/
value.rs

1//! The core value type used in the virtual machine.
2
3use std::fmt::Display;
4
5use crate::{
6    builtins::BuiltinFn,
7    errors::{ExprResult, RuntimeError},
8    types::Type,
9};
10
11#[derive(Debug, Clone, PartialEq)]
12pub enum Value {
13    String(String),
14    Fn(Box<BuiltinFn<'static>>),
15    Bool(bool),
16    Type(Box<Type>),
17}
18
19impl Value {
20    pub fn get_type(&self) -> Type {
21        self.clone().into()
22    }
23
24    pub fn get_string(&self) -> ExprResult<&str> {
25        match self {
26            Value::String(s) => Ok(s.as_str()),
27            _ => Err(vec![(
28                RuntimeError::TypeMismatch {
29                    expected: Type::String,
30                    actual: self.get_type(),
31                }
32                .into(),
33                0..0,
34            )]),
35        }
36    }
37
38    pub fn get_func(&self) -> ExprResult<Box<BuiltinFn>> {
39        match self {
40            Value::Fn(f) => Ok(f.clone()),
41            _ => Err(vec![(
42                RuntimeError::TypeMismatch {
43                    expected: Type::Fn {
44                        args: vec![],
45                        variadic_arg: Some(Type::Value.into()),
46                        returns: Type::Value.into(),
47                    },
48                    actual: self.get_type(),
49                }
50                .into(),
51                0..0,
52            )]),
53        }
54    }
55
56    pub fn get_bool(&self) -> ExprResult<bool> {
57        match self {
58            Value::Bool(s) => Ok(*s),
59            _ => Err(vec![(
60                RuntimeError::TypeMismatch {
61                    expected: Type::Bool,
62                    actual: self.get_type(),
63                }
64                .into(),
65                0..0,
66            )]),
67        }
68    }
69}
70
71impl Display for Value {
72    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
73        match self {
74            Value::String(string) => write!(f, "`{}`", string),
75            Value::Fn(builtin) => write!(f, "{builtin:?}"),
76            Value::Bool(value) => write!(f, "{}", value),
77            Value::Type(ty) => write!(f, "Type<{}>", ty),
78        }
79    }
80}
81
82impl From<bool> for Value {
83    fn from(value: bool) -> Self {
84        Self::Bool(value)
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    use pretty_assertions::assert_eq;
93
94    fn example_builtin(_args: Vec<Value>) -> ExprResult<Value> {
95        Ok(Value::String("".to_string()))
96    }
97
98    #[test]
99    fn get_bool_on_string() {
100        assert_eq!(
101            Err(vec![(
102                RuntimeError::TypeMismatch {
103                    expected: Type::Bool,
104                    actual: Type::String
105                }
106                .into(),
107                0..0
108            )]),
109            Value::String("string".to_string()).get_bool()
110        );
111    }
112
113    #[test]
114    fn get_bool_on_bool_true() {
115        assert_eq!(Ok(true), Value::Bool(true).get_bool());
116    }
117
118    #[test]
119    fn get_string_on_bool() {
120        assert_eq!(
121            Err(vec![(
122                RuntimeError::TypeMismatch {
123                    expected: Type::String,
124                    actual: Type::Bool
125                }
126                .into(),
127                0..0
128            )]),
129            Value::Bool(true).get_string()
130        );
131    }
132
133    #[test]
134    fn get_string_on_string() {
135        let value = Value::String("test".to_string());
136        assert_eq!(Ok("test"), value.get_string());
137    }
138
139    #[test]
140    fn get_func_on_string() {
141        let value = Value::String("not a function".to_string());
142        assert_eq!(
143            Err(vec![(
144                RuntimeError::TypeMismatch {
145                    expected: Type::Fn {
146                        args: vec![],
147                        variadic_arg: Some(Type::Value.into()),
148                        returns: Type::Value.into()
149                    },
150                    actual: Type::String
151                }
152                .into(),
153                0..0
154            )]),
155            value.get_func()
156        );
157    }
158
159    #[test]
160    fn get_func_on_func() {
161        let expected_fn: Box<BuiltinFn> = BuiltinFn {
162            name: "name",
163            args: &[],
164            return_type: Type::Unknown,
165            func: example_builtin,
166        }
167        .into();
168
169        let value = Value::Fn(expected_fn.clone());
170
171        assert_eq!(Ok(expected_fn), value.get_func());
172    }
173
174    #[test]
175    fn get_func_on_bool() {
176        let value = Value::Bool(true);
177        assert_eq!(
178            Err(vec![(
179                RuntimeError::TypeMismatch {
180                    expected: Type::Fn {
181                        args: vec![],
182                        variadic_arg: Some(Type::Value.into()),
183                        returns: Type::Value.into()
184                    },
185                    actual: Type::Bool
186                }
187                .into(),
188                0..0
189            )]),
190            value.get_func()
191        );
192    }
193
194    // #[test]
195    // fn get_bool_on_func() {
196    //     let dummy_fn: Rc<BuiltinFn> = Rc::new(|_, _| Ok(Value::Bool(true)));
197    //     assert_eq!(
198    //         Err(vec![(
199    //             RuntimeError::TypeMismatch {
200    //                 expected: Type::Bool,
201    //                 actual: Type::Fn
202    //             }
203    //             .into(),
204    //             0..0
205    //         )]),
206    //         Value::Fn(dummy_fn).get_bool()
207    //     );
208    // }
209}