weakauras_codec_lua_value/
lib.rs

1// Copyright 2020-2025 Velithris
2// SPDX-License-Identifier: MIT
3
4//! This library provides types that behave similarly to Lua types.
5//!
6//! # Crate features
7//!
8//! * **arbitrary** - Implement `arbitrary::Arbitrary` for [`LuaValue`]. **Disabled** by default.
9//! * **fnv** - Use `fnv` instead of `BTreeMap` as the implementation of [`LuaValue::Map`]. **Disabled** by default.
10//! * **indexmap** - Use `indexmap` instead of `BTreeMap` as the implementation of [`LuaValue::Map`]. **Disabled** by default.
11//! * **serde** - Allow serializing and deserializing [`LuaValue`] using `serde`. **Disabled** by default.
12
13#![deny(missing_docs)]
14
15/// Error types.
16pub mod error;
17
18#[cfg(all(not(feature = "indexmap"), feature = "fnv"))]
19pub use fnv::FnvHashMap as Map;
20#[cfg(feature = "indexmap")]
21pub use indexmap::IndexMap as Map;
22#[cfg(not(any(feature = "indexmap", feature = "fnv")))]
23pub use std::collections::BTreeMap as Map;
24
25use crate::error::TryFromLuaValueError;
26use core::convert::TryFrom;
27
28#[cfg(feature = "arbitrary")]
29use arbitrary::Arbitrary;
30#[cfg(feature = "serde")]
31use serde::{
32    de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor},
33    ser::{Serialize, Serializer},
34};
35
36/// A tagged union representing all
37/// possible values in Lua.
38#[allow(missing_docs)] // Variants are self-explanatory.
39#[derive(Debug, Clone)]
40#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
41pub enum LuaValue {
42    Map(Map<LuaMapKey, LuaValue>),
43    Array(Vec<LuaValue>),
44    String(String),
45    Number(f64),
46    Boolean(bool),
47    Null,
48}
49
50impl From<String> for LuaValue {
51    fn from(value: String) -> Self {
52        Self::String(value)
53    }
54}
55
56impl From<&str> for LuaValue {
57    fn from(value: &str) -> Self {
58        Self::String(value.to_owned())
59    }
60}
61
62impl From<&mut str> for LuaValue {
63    fn from(value: &mut str) -> Self {
64        Self::String(value.to_owned())
65    }
66}
67
68impl From<f64> for LuaValue {
69    fn from(value: f64) -> Self {
70        Self::Number(value)
71    }
72}
73
74impl From<bool> for LuaValue {
75    fn from(value: bool) -> Self {
76        Self::Boolean(value)
77    }
78}
79
80/// A key of a Lua map.
81#[repr(transparent)]
82#[derive(Clone)]
83pub struct LuaMapKey(LuaValue);
84impl LuaMapKey {
85    #[inline(always)]
86    /// Get a reference to the underlying value.
87    pub fn as_value(&self) -> &LuaValue {
88        &self.0
89    }
90}
91
92impl From<LuaMapKey> for LuaValue {
93    fn from(value: LuaMapKey) -> Self {
94        value.0
95    }
96}
97
98impl TryFrom<LuaValue> for LuaMapKey {
99    type Error = TryFromLuaValueError;
100
101    fn try_from(value: LuaValue) -> Result<Self, Self::Error> {
102        match value {
103            LuaValue::Null => Err(TryFromLuaValueError::KeyCannotBeNull),
104            LuaValue::Number(inner) if inner.is_nan() => Err(TryFromLuaValueError::KeyCannotBeNan),
105            _ => Ok(Self(value)),
106        }
107    }
108}
109
110use core::hash::{Hash, Hasher};
111impl Hash for LuaValue {
112    fn hash<H: Hasher>(&self, state: &mut H) {
113        match self {
114            LuaValue::Map(m) => state.write_usize((m as *const Map<LuaMapKey, LuaValue>).addr()),
115            LuaValue::Array(v) => state.write_usize((v as *const Vec<LuaValue>).addr()),
116            LuaValue::String(s) => s.hash(state),
117            LuaValue::Number(n) => state.write_u64(n.to_bits()),
118            LuaValue::Boolean(b) => b.hash(state),
119            LuaValue::Null => state.write_u8(0),
120        }
121    }
122}
123
124use core::cmp::Ordering;
125impl PartialOrd for LuaValue {
126    fn partial_cmp(&self, other: &LuaValue) -> Option<Ordering> {
127        Some(self.cmp(other))
128    }
129}
130impl Ord for LuaValue {
131    // Number > String > Boolean > Map > Null
132    fn cmp(&self, other: &LuaValue) -> Ordering {
133        match (self, other) {
134            (LuaValue::Number(n1), LuaValue::Number(n2)) => {
135                n1.partial_cmp(n2)
136                    .unwrap_or_else(|| match (n1.is_nan(), n2.is_nan()) {
137                        (true, false) => Ordering::Less,
138                        (false, true) => Ordering::Greater,
139                        _ => Ordering::Equal,
140                    })
141            }
142            (LuaValue::Number(_), _) => Ordering::Greater,
143            (_, LuaValue::Number(_)) => Ordering::Less,
144            (LuaValue::String(s1), LuaValue::String(s2)) => s1.cmp(s2),
145            (LuaValue::String(_), LuaValue::Boolean(_))
146            | (LuaValue::String(_), LuaValue::Map(_))
147            | (LuaValue::String(_), LuaValue::Array(_)) => Ordering::Greater,
148            (LuaValue::Boolean(_), LuaValue::String(_))
149            | (LuaValue::Map(_), LuaValue::String(_))
150            | (LuaValue::Array(_), LuaValue::String(_)) => Ordering::Less,
151            (LuaValue::Boolean(b1), LuaValue::Boolean(b2)) => b1.cmp(b2),
152            (LuaValue::Boolean(_), LuaValue::Map(_))
153            | (LuaValue::Boolean(_), LuaValue::Array(_)) => Ordering::Greater,
154            (LuaValue::Map(_), LuaValue::Boolean(_))
155            | (LuaValue::Array(_), LuaValue::Boolean(_)) => Ordering::Less,
156            (LuaValue::Map(m1), LuaValue::Map(m2)) => {
157                let p1 = (m1 as *const Map<LuaMapKey, LuaValue>).addr();
158                let p2 = (m2 as *const Map<LuaMapKey, LuaValue>).addr();
159                p1.cmp(&p2)
160            }
161            (LuaValue::Map(_), LuaValue::Array(_)) => Ordering::Greater,
162            (LuaValue::Array(_), LuaValue::Map(_)) => Ordering::Less,
163            (LuaValue::Array(v1), LuaValue::Array(v2)) => {
164                let p1 = (v1 as *const Vec<LuaValue>).addr();
165                let p2 = (v2 as *const Vec<LuaValue>).addr();
166                p1.cmp(&p2)
167            }
168            (LuaValue::Null, LuaValue::Null) => Ordering::Equal,
169            (LuaValue::Null, _) => Ordering::Less,
170            (_, LuaValue::Null) => Ordering::Greater,
171        }
172    }
173}
174impl PartialEq for LuaValue {
175    fn eq(&self, other: &LuaValue) -> bool {
176        match (self, other) {
177            (LuaValue::Map(m1), LuaValue::Map(m2)) => {
178                let p1 = (m1 as *const Map<LuaMapKey, LuaValue>).addr();
179                let p2 = (m2 as *const Map<LuaMapKey, LuaValue>).addr();
180                p1.eq(&p2)
181            }
182            (LuaValue::Array(v1), LuaValue::Array(v2)) => {
183                let p1 = (v1 as *const Vec<LuaValue>).addr();
184                let p2 = (v2 as *const Vec<LuaValue>).addr();
185                p1.eq(&p2)
186            }
187            (LuaValue::String(s1), LuaValue::String(s2)) => s1.eq(s2),
188            (LuaValue::Number(n1), LuaValue::Number(n2)) => n1.eq(n2),
189            (LuaValue::Boolean(b1), LuaValue::Boolean(b2)) => b1.eq(b2),
190            (LuaValue::Null, LuaValue::Null) => true,
191            _ => false,
192        }
193    }
194}
195impl Eq for LuaValue {}
196
197impl Hash for LuaMapKey {
198    #[inline(always)]
199    fn hash<H: Hasher>(&self, state: &mut H) {
200        self.0.hash(state)
201    }
202}
203impl PartialOrd for LuaMapKey {
204    #[inline(always)]
205    fn partial_cmp(&self, other: &LuaMapKey) -> Option<Ordering> {
206        Some(self.0.cmp(&other.0))
207    }
208}
209impl Ord for LuaMapKey {
210    #[inline(always)]
211    fn cmp(&self, other: &LuaMapKey) -> Ordering {
212        self.0.cmp(&other.0)
213    }
214}
215impl PartialEq for LuaMapKey {
216    #[inline(always)]
217    fn eq(&self, other: &LuaMapKey) -> bool {
218        self.0.eq(&other.0)
219    }
220}
221impl Eq for LuaMapKey {}
222
223#[cfg(feature = "serde")]
224use std::borrow::Cow;
225impl LuaMapKey {
226    #[cfg(feature = "serde")]
227    fn to_string(&self) -> Cow<'_, str> {
228        match self.0 {
229            LuaValue::String(ref v) => Cow::from(v),
230            LuaValue::Number(v) => Cow::from(v.to_string()),
231            LuaValue::Boolean(v) => Cow::from(v.to_string()),
232            LuaValue::Map(ref m) => Cow::from(format!("map at {m:p}")),
233            LuaValue::Array(ref m) => Cow::from(format!("array at {m:p}")),
234            LuaValue::Null => unsafe { core::hint::unreachable_unchecked() },
235        }
236    }
237}
238
239use core::fmt::{self, Debug};
240impl Debug for LuaMapKey {
241    #[inline(always)]
242    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243        Debug::fmt(&self.0, f)
244    }
245}
246
247#[cfg(feature = "arbitrary")]
248impl<'a> Arbitrary<'a> for LuaMapKey {
249    #[inline(always)]
250    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
251        let key = LuaValue::arbitrary(u)?;
252        LuaMapKey::try_from(key).map_err(|_| arbitrary::Error::IncorrectFormat)
253    }
254
255    #[inline(always)]
256    fn arbitrary_take_rest(u: arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
257        let key = LuaValue::arbitrary_take_rest(u)?;
258        LuaMapKey::try_from(key).map_err(|_| arbitrary::Error::IncorrectFormat)
259    }
260
261    #[inline(always)]
262    fn size_hint(depth: usize) -> (usize, Option<usize>) {
263        LuaValue::size_hint(depth)
264    }
265
266    #[inline(always)]
267    fn try_size_hint(
268        depth: usize,
269    ) -> arbitrary::Result<(usize, Option<usize>), arbitrary::MaxRecursionReached> {
270        LuaValue::try_size_hint(depth)
271    }
272}
273
274#[cfg(feature = "serde")]
275impl Serialize for LuaValue {
276    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
277    where
278        S: Serializer,
279    {
280        use serde::ser::{SerializeMap, SerializeSeq};
281
282        match self {
283            LuaValue::String(s) => serializer.serialize_str(s),
284            LuaValue::Number(n) => serializer.serialize_f64(*n),
285            LuaValue::Boolean(b) => serializer.serialize_bool(*b),
286            LuaValue::Null => serializer.serialize_none(),
287            LuaValue::Map(m) => {
288                let mut map = serializer.serialize_map(Some(m.len()))?;
289                for (k, v) in m {
290                    map.serialize_entry(&LuaMapKey::to_string(k), v)?;
291                }
292                map.end()
293            }
294            LuaValue::Array(v) => {
295                let mut seq = serializer.serialize_seq(Some(v.len()))?;
296                for el in v {
297                    seq.serialize_element(el)?;
298                }
299                seq.end()
300            }
301        }
302    }
303}
304
305#[cfg(feature = "serde")]
306impl<'de> Deserialize<'de> for LuaValue {
307    fn deserialize<D>(deserializer: D) -> Result<LuaValue, D::Error>
308    where
309        D: Deserializer<'de>,
310    {
311        struct LuaValueVisitor;
312
313        impl<'de> Visitor<'de> for LuaValueVisitor {
314            type Value = LuaValue;
315
316            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
317                formatter.write_str("a Lua value")
318            }
319
320            fn visit_none<E>(self) -> Result<LuaValue, E> {
321                Ok(LuaValue::Null)
322            }
323
324            fn visit_unit<E>(self) -> Result<LuaValue, E> {
325                Ok(LuaValue::Null)
326            }
327
328            fn visit_bool<E>(self, value: bool) -> Result<LuaValue, E> {
329                Ok(LuaValue::Boolean(value))
330            }
331
332            fn visit_i64<E>(self, value: i64) -> Result<LuaValue, E>
333            where
334                E: de::Error,
335            {
336                let value_f64 = value as f64;
337                if value_f64 as i64 == value {
338                    Ok(LuaValue::Number(value_f64))
339                } else {
340                    Err(de::Error::custom("can't represent as f64"))
341                }
342            }
343
344            fn visit_u64<E>(self, value: u64) -> Result<LuaValue, E>
345            where
346                E: de::Error,
347            {
348                let value_f64 = value as f64;
349                if value_f64 as u64 == value {
350                    Ok(LuaValue::Number(value_f64))
351                } else {
352                    Err(de::Error::custom("can't represent as f64"))
353                }
354            }
355
356            fn visit_f64<E>(self, value: f64) -> Result<LuaValue, E> {
357                Ok(LuaValue::Number(value))
358            }
359
360            fn visit_str<E>(self, value: &str) -> Result<LuaValue, E>
361            where
362                E: de::Error,
363            {
364                self.visit_string(String::from(value))
365            }
366
367            fn visit_string<E>(self, value: String) -> Result<LuaValue, E> {
368                Ok(LuaValue::String(value))
369            }
370
371            fn visit_seq<V>(self, mut seq: V) -> Result<LuaValue, V::Error>
372            where
373                V: SeqAccess<'de>,
374            {
375                let mut result = Vec::with_capacity(seq.size_hint().unwrap_or(16));
376
377                while let Some(element) = seq.next_element()? {
378                    result.push(element);
379                }
380
381                Ok(LuaValue::Array(result))
382            }
383
384            fn visit_map<V>(self, mut map: V) -> Result<LuaValue, V::Error>
385            where
386                V: MapAccess<'de>,
387            {
388                #[cfg(any(feature = "indexmap", feature = "fnv"))]
389                let mut result = Map::with_capacity(map.size_hint().unwrap_or(16));
390                #[cfg(not(any(feature = "indexmap", feature = "fnv")))]
391                let mut result = Map::new();
392
393                while let Some(key) = map.next_key()? {
394                    let key = LuaMapKey::try_from(match key {
395                        LuaValue::String(s) => match s.parse::<i32>() {
396                            Ok(n) => LuaValue::Number(n as f64),
397                            Err(_) => LuaValue::String(s),
398                        },
399                        v => v,
400                    })
401                    .map_err(de::Error::custom)?;
402
403                    let value = map.next_value()?;
404
405                    result.insert(key, value);
406                }
407
408                Ok(LuaValue::Map(result))
409            }
410        }
411
412        deserializer.deserialize_any(LuaValueVisitor)
413    }
414}