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}