darklua_core/process/evaluator/
lua_value.rs1use crate::nodes::{Expression, NumberExpression, StringExpression};
2
3#[derive(Debug, Clone, PartialEq)]
5pub enum LuaValue {
6 False,
7 Function,
8 Nil,
9 Number(f64),
10 String(Vec<u8>),
11 Table,
12 True,
13 Unknown,
14}
15
16impl LuaValue {
17 pub fn is_truthy(&self) -> Option<bool> {
36 match self {
37 Self::Unknown => None,
38 Self::Nil | Self::False => Some(false),
39 _ => Some(true),
40 }
41 }
42
43 pub fn map_if_truthy<F>(self, map: F) -> Self
47 where
48 F: Fn(Self) -> Self,
49 {
50 match self.is_truthy() {
51 Some(true) => map(self),
52 Some(false) => self,
53 _ => Self::Unknown,
54 }
55 }
56
57 pub fn map_if_truthy_else<F, G>(self, map: F, default: G) -> Self
60 where
61 F: Fn(Self) -> Self,
62 G: Fn() -> Self,
63 {
64 match self.is_truthy() {
65 Some(true) => map(self),
66 Some(false) => default(),
67 _ => Self::Unknown,
68 }
69 }
70
71 pub fn to_expression(self) -> Option<Expression> {
73 match self {
74 Self::False => Some(Expression::from(false)),
75 Self::True => Some(Expression::from(true)),
76 Self::Nil => Some(Expression::nil()),
77 Self::String(value) => Some(StringExpression::from_value(value).into()),
78 Self::Number(value) => Some(Expression::from(value)),
79 _ => None,
80 }
81 }
82
83 pub fn number_coercion(self) -> Self {
86 match &self {
87 Self::String(string) => str::from_utf8(string)
88 .ok()
89 .map(str::trim)
90 .and_then(|string| {
91 let number = if string.starts_with('-') {
92 string
93 .get(1..)
94 .and_then(|string| string.parse::<NumberExpression>().ok())
95 .map(|number| -number.compute_value())
96 } else {
97 string
98 .parse::<NumberExpression>()
99 .ok()
100 .map(|number| number.compute_value())
101 };
102
103 number.map(LuaValue::Number)
104 }),
105 _ => None,
106 }
107 .unwrap_or(self)
108 }
109
110 pub fn string_coercion(self) -> Self {
113 match &self {
114 Self::Number(value) => Some(Self::from(value.to_string())),
115 _ => None,
116 }
117 .unwrap_or(self)
118 }
119}
120
121impl Default for LuaValue {
122 fn default() -> Self {
123 Self::Unknown
124 }
125}
126
127impl From<bool> for LuaValue {
128 fn from(value: bool) -> Self {
129 if value {
130 Self::True
131 } else {
132 Self::False
133 }
134 }
135}
136
137impl From<String> for LuaValue {
138 fn from(value: String) -> Self {
139 Self::String(value.into_bytes())
140 }
141}
142
143impl From<&str> for LuaValue {
144 fn from(value: &str) -> Self {
145 Self::String(value.as_bytes().to_vec())
146 }
147}
148
149impl From<Vec<u8>> for LuaValue {
150 fn from(value: Vec<u8>) -> Self {
151 Self::String(value)
152 }
153}
154
155impl From<&[u8]> for LuaValue {
156 fn from(value: &[u8]) -> Self {
157 Self::String(value.to_vec())
158 }
159}
160
161impl<const N: usize> From<&[u8; N]> for LuaValue {
162 fn from(value: &[u8; N]) -> Self {
163 Self::String(value.to_vec())
164 }
165}
166
167impl From<f64> for LuaValue {
168 fn from(value: f64) -> Self {
169 Self::Number(value)
170 }
171}
172
173#[cfg(test)]
174mod test {
175 use super::*;
176
177 #[test]
178 fn unknown_lua_value_is_truthy_returns_none() {
179 assert!(LuaValue::Unknown.is_truthy().is_none());
180 }
181
182 #[test]
183 fn false_value_is_not_truthy() {
184 assert!(!LuaValue::False.is_truthy().unwrap());
185 }
186
187 #[test]
188 fn nil_value_is_not_truthy() {
189 assert!(!LuaValue::Nil.is_truthy().unwrap());
190 }
191
192 #[test]
193 fn true_value_is_truthy() {
194 assert!(LuaValue::True.is_truthy().unwrap());
195 }
196
197 #[test]
198 fn zero_value_is_truthy() {
199 assert!(LuaValue::Number(0_f64).is_truthy().unwrap());
200 }
201
202 #[test]
203 fn string_value_is_truthy() {
204 assert!(LuaValue::String(b"".to_vec()).is_truthy().unwrap());
205 }
206
207 #[test]
208 fn table_value_is_truthy() {
209 assert!(LuaValue::Table.is_truthy().unwrap());
210 }
211
212 mod number_coercion {
213 use super::*;
214
215 macro_rules! number_coercion {
216 ($($name:ident ($string:literal) => $result:expr),*) => {
217 $(
218 #[test]
219 fn $name() {
220 assert_eq!(
221 LuaValue::String($string.into()).number_coercion(),
222 LuaValue::Number($result)
223 );
224 }
225 )*
226 };
227 }
228
229 macro_rules! no_number_coercion {
230 ($($name:ident ($string:literal)),*) => {
231 $(
232 #[test]
233 fn $name() {
234 assert_eq!(
235 LuaValue::String($string.into()).number_coercion(),
236 LuaValue::String($string.into())
237 );
238 }
239 )*
240 };
241 }
242
243 number_coercion!(
244 zero("0") => 0.0,
245 integer("12") => 12.0,
246 integer_with_leading_zeros("00012") => 12.0,
247 integer_with_ending_space("12 ") => 12.0,
248 integer_with_leading_space(" 123") => 123.0,
249 integer_with_leading_tab("\t123") => 123.0,
250 negative_integer("-3") => -3.0,
251 hex_zero("0x0") => 0.0,
252 hex_integer("0xA") => 10.0,
253 negative_hex_integer("-0xA") => -10.0,
254 float("0.5") => 0.5,
255 negative_float("-0.5") => -0.5,
256 float_starting_with_dot(".5") => 0.5
257 );
258
259 no_number_coercion!(
260 letter_suffix("123a"),
261 hex_prefix("0x"),
262 space_between_minus("- 1"),
263 two_seperated_digits(" 1 2")
264 );
265 }
266}