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 pub fn length(&self) -> LuaValue {
122 match self {
123 Self::String(value) => LuaValue::Number(value.len() as f64),
124 _ => LuaValue::Unknown,
125 }
126 }
127}
128
129impl Default for LuaValue {
130 fn default() -> Self {
131 Self::Unknown
132 }
133}
134
135impl From<bool> for LuaValue {
136 fn from(value: bool) -> Self {
137 if value {
138 Self::True
139 } else {
140 Self::False
141 }
142 }
143}
144
145impl From<String> for LuaValue {
146 fn from(value: String) -> Self {
147 Self::String(value.into_bytes())
148 }
149}
150
151impl From<&str> for LuaValue {
152 fn from(value: &str) -> Self {
153 Self::String(value.as_bytes().to_vec())
154 }
155}
156
157impl From<Vec<u8>> for LuaValue {
158 fn from(value: Vec<u8>) -> Self {
159 Self::String(value)
160 }
161}
162
163impl From<&[u8]> for LuaValue {
164 fn from(value: &[u8]) -> Self {
165 Self::String(value.to_vec())
166 }
167}
168
169impl<const N: usize> From<&[u8; N]> for LuaValue {
170 fn from(value: &[u8; N]) -> Self {
171 Self::String(value.to_vec())
172 }
173}
174
175impl From<f64> for LuaValue {
176 fn from(value: f64) -> Self {
177 Self::Number(value)
178 }
179}
180
181#[cfg(test)]
182mod test {
183 use super::*;
184
185 #[test]
186 fn unknown_lua_value_is_truthy_returns_none() {
187 assert!(LuaValue::Unknown.is_truthy().is_none());
188 }
189
190 #[test]
191 fn false_value_is_not_truthy() {
192 assert!(!LuaValue::False.is_truthy().unwrap());
193 }
194
195 #[test]
196 fn nil_value_is_not_truthy() {
197 assert!(!LuaValue::Nil.is_truthy().unwrap());
198 }
199
200 #[test]
201 fn true_value_is_truthy() {
202 assert!(LuaValue::True.is_truthy().unwrap());
203 }
204
205 #[test]
206 fn zero_value_is_truthy() {
207 assert!(LuaValue::Number(0_f64).is_truthy().unwrap());
208 }
209
210 #[test]
211 fn string_value_is_truthy() {
212 assert!(LuaValue::String(b"".to_vec()).is_truthy().unwrap());
213 }
214
215 #[test]
216 fn table_value_is_truthy() {
217 assert!(LuaValue::Table.is_truthy().unwrap());
218 }
219
220 mod number_coercion {
221 use super::*;
222
223 macro_rules! number_coercion {
224 ($($name:ident ($string:literal) => $result:expr),*) => {
225 $(
226 #[test]
227 fn $name() {
228 assert_eq!(
229 LuaValue::String($string.into()).number_coercion(),
230 LuaValue::Number($result)
231 );
232 }
233 )*
234 };
235 }
236
237 macro_rules! no_number_coercion {
238 ($($name:ident ($string:literal)),*) => {
239 $(
240 #[test]
241 fn $name() {
242 assert_eq!(
243 LuaValue::String($string.into()).number_coercion(),
244 LuaValue::String($string.into())
245 );
246 }
247 )*
248 };
249 }
250
251 number_coercion!(
252 zero("0") => 0.0,
253 integer("12") => 12.0,
254 integer_with_leading_zeros("00012") => 12.0,
255 integer_with_ending_space("12 ") => 12.0,
256 integer_with_leading_space(" 123") => 123.0,
257 integer_with_leading_tab("\t123") => 123.0,
258 negative_integer("-3") => -3.0,
259 hex_zero("0x0") => 0.0,
260 hex_integer("0xA") => 10.0,
261 negative_hex_integer("-0xA") => -10.0,
262 float("0.5") => 0.5,
263 negative_float("-0.5") => -0.5,
264 float_starting_with_dot(".5") => 0.5
265 );
266
267 no_number_coercion!(
268 letter_suffix("123a"),
269 hex_prefix("0x"),
270 space_between_minus("- 1"),
271 two_seperated_digits(" 1 2")
272 );
273 }
274}