Skip to main content

bubbles/value/
types.rs

1//! The [`Value`] type representing all runtime values.
2
3use core::fmt;
4
5/// All possible runtime values used in expressions and variables.
6#[derive(Debug, Clone, PartialEq)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub enum Value {
9    /// A floating-point number.
10    Number(f64),
11    /// A UTF-8 string.
12    Text(String),
13    /// A boolean.
14    Bool(bool),
15}
16
17impl Value {
18    /// Returns whether the value is truthy under permissive coercion.
19    #[must_use]
20    pub fn is_truthy(&self) -> bool {
21        match self {
22            Self::Bool(v) => *v,
23            Self::Number(v) => *v != 0.0,
24            Self::Text(v) => !v.is_empty(),
25        }
26    }
27}
28
29impl fmt::Display for Value {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        match self {
32            Self::Number(v) => {
33                // Omit ".0" suffix so `{$n}` renders as "2" not "2.0".
34                // `{v:.0}` rounds to 0 decimal places; since `v.fract() == 0.0`
35                // the result is always exact - and no cast is needed.
36                if v.fract() == 0.0 && v.abs() < 1e15 {
37                    write!(f, "{v:.0}")
38                } else {
39                    write!(f, "{v}")
40                }
41            }
42            Self::Text(v) => f.write_str(v),
43            Self::Bool(v) => write!(f, "{v}"),
44        }
45    }
46}
47
48impl From<f64> for Value {
49    fn from(v: f64) -> Self {
50        Self::Number(v)
51    }
52}
53
54impl From<bool> for Value {
55    fn from(v: bool) -> Self {
56        Self::Bool(v)
57    }
58}
59
60impl From<String> for Value {
61    fn from(v: String) -> Self {
62        Self::Text(v)
63    }
64}
65
66impl From<&str> for Value {
67    fn from(v: &str) -> Self {
68        Self::Text(v.to_owned())
69    }
70}
71
72#[cfg(test)]
73#[path = "types_tests.rs"]
74mod tests;