Skip to main content

lumen_engine/node/
property.rs

1use crate::{
2    error::{LumenError, PropertyError},
3    expr::Expression,
4};
5
6use super::{ids::NodeId, vector};
7
8#[derive(Debug, Clone)]
9pub enum PropertyValue {
10    Float(f64),
11    Int(i64),
12    Bool(bool),
13    String(String),
14    Color([u8; 4]),
15    Paint(vector::paint::Paint),
16    Vec2((f64, f64)),
17    FloatVec(Vec<f64>),
18    IntVec(Vec<i64>),
19    StringVec(Vec<String>),
20}
21
22#[derive(Debug, Clone)]
23pub enum PropertyExpression {
24    Value(PropertyValue),
25    Expr(Expression),
26}
27
28impl PropertyValue {
29    pub(crate) fn invalid_type(
30        node_id: NodeId,
31        property_path: &str,
32        expected: &'static str,
33        actual: &'static str,
34    ) -> LumenError {
35        LumenError::Property(PropertyError::InvalidType {
36            node_id,
37            property_path: property_path.to_string(),
38            expected,
39            actual,
40        })
41    }
42
43    pub(crate) fn coerce_float(&self) -> Option<f64> {
44        match self {
45            Self::Float(value) => Some(*value),
46            Self::Int(value) => Some(*value as f64),
47            Self::String(value) => value.parse().ok(),
48            _ => None,
49        }
50    }
51
52    pub(crate) fn coerce_int(&self) -> Option<i64> {
53        match self {
54            Self::Int(value) => Some(*value),
55            Self::Float(value) => Some(*value as i64),
56            Self::Bool(value) => Some(i64::from(*value)),
57            Self::String(value) => value.parse().ok(),
58            _ => None,
59        }
60    }
61
62    pub(crate) fn coerce_bool(&self) -> Option<bool> {
63        match self {
64            Self::Bool(value) => Some(*value),
65            Self::Int(value) => Some(*value != 0),
66            Self::Float(value) => Some(*value != 0.0),
67            Self::String(value) => match value.to_ascii_lowercase().as_str() {
68                "true" | "1" => Some(true),
69                "false" | "0" => Some(false),
70                _ => None,
71            },
72            _ => None,
73        }
74    }
75
76    pub(crate) fn coerce_string(&self) -> Option<String> {
77        match self {
78            Self::String(value) => Some(value.clone()),
79            Self::Int(value) => Some(value.to_string()),
80            Self::Float(value) => Some(value.to_string()),
81            Self::Bool(value) => Some(value.to_string()),
82            _ => None,
83        }
84    }
85
86    pub fn resolve_float(
87        &self,
88        node_id: NodeId,
89        property_path: &str,
90        _ctx: &crate::expr::ExpressionContext<'_>,
91    ) -> crate::Result<f64> {
92        self.coerce_float().ok_or_else(|| {
93            Self::invalid_type(
94                node_id,
95                property_path,
96                "Float",
97                if matches!(self, Self::String(_)) {
98                    "String"
99                } else {
100                    "unsupported"
101                },
102            )
103        })
104    }
105
106    pub fn resolve_int(
107        &self,
108        node_id: NodeId,
109        property_path: &str,
110        _ctx: &crate::expr::ExpressionContext<'_>,
111    ) -> crate::Result<i64> {
112        self.coerce_int().ok_or_else(|| {
113            Self::invalid_type(
114                node_id,
115                property_path,
116                "Int",
117                if matches!(self, Self::String(_)) {
118                    "String"
119                } else {
120                    "unsupported"
121                },
122            )
123        })
124    }
125
126    pub fn resolve_bool(
127        &self,
128        node_id: NodeId,
129        property_path: &str,
130        _ctx: &crate::expr::ExpressionContext<'_>,
131    ) -> crate::Result<bool> {
132        self.coerce_bool()
133            .ok_or_else(|| Self::invalid_type(node_id, property_path, "Bool", "unsupported"))
134    }
135
136    pub fn resolve_string(
137        &self,
138        node_id: NodeId,
139        property_path: &str,
140        _ctx: &crate::expr::ExpressionContext<'_>,
141    ) -> crate::Result<String> {
142        self.coerce_string()
143            .ok_or_else(|| Self::invalid_type(node_id, property_path, "String", "unsupported"))
144    }
145
146    pub fn resolve_color(
147        &self,
148        node_id: NodeId,
149        property_path: &str,
150        _ctx: &crate::expr::ExpressionContext<'_>,
151    ) -> crate::Result<[u8; 4]> {
152        match self {
153            Self::Color(value) => Ok(*value),
154            _ => Err(Self::invalid_type(
155                node_id,
156                property_path,
157                "Color",
158                "unsupported",
159            )),
160        }
161    }
162
163    pub fn resolve_paint(
164        &self,
165        node_id: NodeId,
166        property_path: &str,
167        _ctx: &crate::expr::ExpressionContext<'_>,
168    ) -> crate::Result<vector::paint::Paint> {
169        match self {
170            Self::Paint(value) => Ok(value.clone()),
171            Self::Color(value) => Ok(vector::paint::Paint::solid(*value)),
172            _ => Err(Self::invalid_type(
173                node_id,
174                property_path,
175                "Paint",
176                "unsupported",
177            )),
178        }
179    }
180
181    pub fn resolve_vec2(
182        &self,
183        node_id: NodeId,
184        property_path: &str,
185        _ctx: &crate::expr::ExpressionContext<'_>,
186    ) -> crate::Result<(f64, f64)> {
187        match self {
188            Self::Vec2(value) => Ok(*value),
189            _ => Err(Self::invalid_type(
190                node_id,
191                property_path,
192                "Vec2",
193                "unsupported",
194            )),
195        }
196    }
197}