pax_runtime_api/pax_value/
functions.rs1use crate::{
2    math::{Transform2, Vector2},
3    Color, ColorChannel, Fill, PaxValue, Rotation,
4};
5use once_cell::sync::Lazy;
6use std::{
7    collections::HashMap,
8    sync::{Arc, RwLock},
9};
10
11use super::{CoercionRules, ToPaxValue};
12
13type FunctionType = Arc<dyn Fn(Vec<PaxValue>) -> Result<PaxValue, String> + Send + Sync>;
14
15static FUNCTIONS: Lazy<Arc<RwLock<HashMap<String, HashMap<String, FunctionType>>>>> =
16    Lazy::new(|| Arc::new(RwLock::new(HashMap::new())));
17
18pub fn print_all_functions() {
19    let functions = FUNCTIONS.read().unwrap();
20    log::warn!("Total scopes: {}", functions.len());
21    for (scope, funcs) in functions.iter() {
22        log::warn!("Scope: {}", scope);
23        for (name, _) in funcs.iter() {
24            log::warn!("  |{}|", name);
25        }
26    }
27}
28
29pub fn register_function(scope: String, name: String, func: FunctionType) {
30    let mut functions = FUNCTIONS.write().unwrap();
31    functions
32        .entry(scope)
33        .or_insert_with(HashMap::new)
34        .insert(name, func);
35}
36
37pub fn call_function(scope: String, name: String, args: Vec<PaxValue>) -> Result<PaxValue, String> {
38    let functions = FUNCTIONS.read().unwrap();
39    let scope_funcs = functions
40        .get(&scope)
41        .ok_or_else(|| format!("Scope {} not found", scope))?;
42    let func = scope_funcs
43        .get(&name)
44        .ok_or_else(|| format!("Function {} not found in scope {}", name, scope))?;
45    func(args)
46}
47
48fn add(args: Vec<PaxValue>) -> Result<PaxValue, String> {
50    if args.len() != 2 {
51        return Err("Expected 2 arguments for function add".to_string());
52    }
53    Ok(args[0].clone() + args[1].clone())
54}
55
56fn sub_or_neg(args: Vec<PaxValue>) -> Result<PaxValue, String> {
57    if args.len() == 1 {
58        Ok(-args[0].clone())
59    } else if args.len() == 2 {
60        Ok(args[0].clone() - args[1].clone())
61    } else {
62        Err("Expected 1 or 2 arguments for function sub_or_neg".to_string())
63    }
64}
65
66fn mul(args: Vec<PaxValue>) -> Result<PaxValue, String> {
67    if args.len() != 2 {
68        return Err("Expected 2 arguments for function mul".to_string());
69    }
70    Ok(args[0].clone() * args[1].clone())
71}
72
73fn div(args: Vec<PaxValue>) -> Result<PaxValue, String> {
74    if args.len() != 2 {
75        return Err("Expected 2 arguments for function div".to_string());
76    }
77    Ok(args[0].clone() / args[1].clone())
78}
79
80fn exp(args: Vec<PaxValue>) -> Result<PaxValue, String> {
81    if args.len() != 2 {
82        return Err("Expected 2 arguments for function exp".to_string());
83    }
84    Ok(args[0].clone().pow(args[1].clone()))
85}
86
87fn mod_(args: Vec<PaxValue>) -> Result<PaxValue, String> {
88    if args.len() != 2 {
89        return Err("Expected 2 arguments for function mod_".to_string());
90    }
91    Ok(args[0].clone() % args[1].clone())
92}
93
94fn rel_eq(args: Vec<PaxValue>) -> Result<PaxValue, String> {
95    if args.len() != 2 {
96        return Err("Expected 2 arguments for function rel_eq".to_string());
97    }
98    Ok(PaxValue::Bool(args[0] == args[1]))
99}
100
101fn rel_gt(args: Vec<PaxValue>) -> Result<PaxValue, String> {
102    if args.len() != 2 {
103        return Err("Expected 2 arguments for function rel_gt".to_string());
104    }
105    Ok(PaxValue::Bool(args[0] > args[1]))
106}
107
108fn rel_gte(args: Vec<PaxValue>) -> Result<PaxValue, String> {
109    if args.len() != 2 {
110        return Err("Expected 2 arguments for function rel_gte".to_string());
111    }
112    Ok(PaxValue::Bool(args[0] >= args[1]))
113}
114
115fn rel_lt(args: Vec<PaxValue>) -> Result<PaxValue, String> {
116    if args.len() != 2 {
117        return Err("Expected 2 arguments for function rel_lt".to_string());
118    }
119    Ok(PaxValue::Bool(args[0] < args[1]))
120}
121
122fn rel_lte(args: Vec<PaxValue>) -> Result<PaxValue, String> {
123    if args.len() != 2 {
124        return Err("Expected 2 arguments for function rel_lte".to_string());
125    }
126    Ok(PaxValue::Bool(args[0] <= args[1]))
127}
128
129fn rel_neq(args: Vec<PaxValue>) -> Result<PaxValue, String> {
130    if args.len() != 2 {
131        return Err("Expected 2 arguments for function rel_neq".to_string());
132    }
133    Ok(PaxValue::Bool(args[0] != args[1]))
134}
135
136fn bool_and(args: Vec<PaxValue>) -> Result<PaxValue, String> {
137    if args.len() != 2 {
138        return Err("Expected 2 arguments for function bool_and".to_string());
139    }
140    Ok(args[0].clone().op_and(args[1].clone()))
141}
142
143fn bool_or(args: Vec<PaxValue>) -> Result<PaxValue, String> {
144    if args.len() != 2 {
145        return Err("Expected 2 arguments for function bool_or".to_string());
146    }
147    Ok(args[0].clone().op_or(args[1].clone()))
148}
149
150fn bool_not(args: Vec<PaxValue>) -> Result<PaxValue, String> {
151    if args.len() != 1 {
152        return Err("Expected 1 argument for function bool_not".to_string());
153    }
154    Ok(args[0].clone().op_not())
155}
156
157fn min(args: Vec<PaxValue>) -> Result<PaxValue, String> {
158    if args.len() != 2 {
159        return Err("Expected 2 arguments for function min".to_string());
160    }
161    Ok(args[0].clone().min(args[1].clone()))
162}
163
164fn max(args: Vec<PaxValue>) -> Result<PaxValue, String> {
165    if args.len() != 2 {
166        return Err("Expected 2 arguments for function max".to_string());
167    }
168    Ok(args[0].clone().max(args[1].clone()))
169}
170
171fn len(args: Vec<PaxValue>) -> Result<PaxValue, String> {
172    if args.len() != 1 {
173        return Err("len function takes a single argument".to_string());
174    }
175    match args.into_iter().next().unwrap() {
176        PaxValue::Vec(vec) => Ok(vec.len().to_pax_value()),
177        e => Err(format!("can't get length of {e:?}")),
178    }
179}
180
181fn rgb(args: Vec<PaxValue>) -> Result<PaxValue, String> {
182    if args.len() != 3 {
183        return Err("Expected 3 arguments for function rgb".to_string());
184    }
185    let r = ColorChannel::try_coerce(args[0].clone())?;
186    let g = ColorChannel::try_coerce(args[1].clone())?;
187    let b = ColorChannel::try_coerce(args[2].clone())?;
188    Ok(Color::rgb(r, g, b).to_pax_value())
189}
190
191fn rgba(args: Vec<PaxValue>) -> Result<PaxValue, String> {
192    if args.len() != 4 {
193        return Err("Expected 4 arguments for function rgba".to_string());
194    }
195    let r = ColorChannel::try_coerce(args[0].clone())?;
196    let g = ColorChannel::try_coerce(args[1].clone())?;
197    let b = ColorChannel::try_coerce(args[2].clone())?;
198    let a = ColorChannel::try_coerce(args[3].clone())?;
199    Ok(Color::rgba(r, g, b, a).to_pax_value())
200}
201
202fn hsl(args: Vec<PaxValue>) -> Result<PaxValue, String> {
203    if args.len() != 3 {
204        return Err("Expected 3 arguments for function hsl".to_string());
205    }
206    let h = Rotation::try_coerce(args[0].clone())?;
207    let s = ColorChannel::try_coerce(args[1].clone())?;
208    let l = ColorChannel::try_coerce(args[2].clone())?;
209    Ok(Color::hsl(h, s, l).to_pax_value())
210}
211
212fn hsla(args: Vec<PaxValue>) -> Result<PaxValue, String> {
213    if args.len() != 4 {
214        return Err("Expected 4 arguments for function hsla".to_string());
215    }
216    let h = Rotation::try_coerce(args[0].clone())?;
217    let s = ColorChannel::try_coerce(args[1].clone())?;
218    let l = ColorChannel::try_coerce(args[2].clone())?;
219    let a = ColorChannel::try_coerce(args[3].clone())?;
220    Ok(Color::hsla(h, s, l, a).to_pax_value())
221}
222
223fn hex(args: Vec<PaxValue>) -> Result<PaxValue, String> {
224    if args.len() != 1 {
225        return Err("Expected 1 argument for function hex".to_string());
226    }
227    let hex = String::try_coerce(args[0].clone())?;
228    Ok(Color::from_hex(&hex).to_pax_value())
229}
230
231pub trait HelperFunctions {
232    fn register_all_functions() {}
233}
234
235pub struct Functions;
236
237impl Functions {
238    pub fn register_all_functions() {
239        register_function("Math".to_string(), "+".to_string(), Arc::new(add));
241        register_function("Math".to_string(), "-".to_string(), Arc::new(sub_or_neg));
242        register_function("Math".to_string(), "*".to_string(), Arc::new(mul));
243        register_function("Math".to_string(), "/".to_string(), Arc::new(div));
244        register_function("Math".to_string(), "^".to_string(), Arc::new(exp));
245        register_function("Math".to_string(), "%%".to_string(), Arc::new(mod_));
246        register_function("Math".to_string(), "==".to_string(), Arc::new(rel_eq));
247        register_function("Math".to_string(), ">".to_string(), Arc::new(rel_gt));
248        register_function("Math".to_string(), ">=".to_string(), Arc::new(rel_gte));
249        register_function("Math".to_string(), "<".to_string(), Arc::new(rel_lt));
250        register_function("Math".to_string(), "<=".to_string(), Arc::new(rel_lte));
251        register_function("Math".to_string(), "!=".to_string(), Arc::new(rel_neq));
252        register_function("Math".to_string(), "&&".to_string(), Arc::new(bool_and));
253        register_function("Math".to_string(), "||".to_string(), Arc::new(bool_or));
254        register_function("Math".to_string(), "!".to_string(), Arc::new(bool_not));
255        register_function("Math".to_string(), "min".to_string(), Arc::new(min));
256        register_function("Math".to_string(), "max".to_string(), Arc::new(max));
257        register_function("Math".to_string(), "len".to_string(), Arc::new(len));
258        register_function("Color".to_string(), "rgb".to_string(), Arc::new(rgb));
260        register_function("Color".to_string(), "rgba".to_string(), Arc::new(rgba));
261        register_function("Color".to_string(), "hsl".to_string(), Arc::new(hsl));
262        register_function("Color".to_string(), "hsla".to_string(), Arc::new(hsla));
263        register_function("Color".to_string(), "#".to_string(), Arc::new(hex));
264        crate::Transform2D::register_all_functions();
266    }
267
268    pub fn has_function(scope: &str, name: &str) -> bool {
269        let functions = FUNCTIONS.read().unwrap();
270        if let Some(scope_funcs) = functions.get(scope) {
271            scope_funcs.contains_key(name)
272        } else {
273            false
274        }
275    }
276}
277
278impl HelperFunctions for crate::Size {}
279
280impl HelperFunctions for crate::Color {}
281
282impl HelperFunctions for crate::Rotation {}
283
284impl HelperFunctions for String {}
285
286impl HelperFunctions for crate::Numeric {}
287
288impl HelperFunctions for bool {}
289
290impl HelperFunctions for Fill {}
291
292impl HelperFunctions for crate::PaxValue {}
293
294impl HelperFunctions for crate::ColorChannel {}
295
296impl HelperFunctions for crate::Stroke {}
297
298impl HelperFunctions for u8 {}
299impl HelperFunctions for u16 {}
300impl HelperFunctions for u32 {}
301impl HelperFunctions for u64 {}
302impl HelperFunctions for u128 {}
303impl HelperFunctions for usize {}
304
305impl HelperFunctions for i8 {}
306impl HelperFunctions for i16 {}
307impl HelperFunctions for i32 {}
308impl HelperFunctions for i64 {}
309impl HelperFunctions for i128 {}
310impl HelperFunctions for isize {}
311
312impl HelperFunctions for f32 {}
313impl HelperFunctions for f64 {}
314impl<T> HelperFunctions for Vec<T> {}
315
316impl<T: HelperFunctions> HelperFunctions for Option<T> {}
317
318impl HelperFunctions for crate::Transform2D {
319    fn register_all_functions() {
320        register_function(
321            "Transform2D".to_string(),
322            "scale".to_string(),
323            Arc::new(|args| {
324                if args.len() != 2 {
325                    return Err("Expected 2 arguments for function scale".to_string());
326                }
327                let x = crate::Size::try_coerce(args[0].clone())?;
328                let y = crate::Size::try_coerce(args[1].clone())?;
329                Ok(crate::Transform2D::scale(x, y).to_pax_value())
330            }),
331        );
332        register_function(
333            "Transform2D".to_string(),
334            "rotate".to_string(),
335            Arc::new(|args| {
336                if args.len() != 1 {
337                    return Err("Expected 1 argument for function rotate".to_string());
338                }
339                let z = crate::Rotation::try_coerce(args[0].clone())?;
340                Ok(crate::Transform2D::rotate(z).to_pax_value())
341            }),
342        );
343        register_function(
344            "Transform2D".to_string(),
345            "translate".to_string(),
346            Arc::new(|args| {
347                if args.len() != 2 {
348                    return Err("Expected 2 arguments for function translate".to_string());
349                }
350                let x = crate::Size::try_coerce(args[0].clone())?;
351                let y = crate::Size::try_coerce(args[1].clone())?;
352                Ok(crate::Transform2D::translate(x, y).to_pax_value())
353            }),
354        );
355        register_function(
356            "Transform2D".to_string(),
357            "anchor".to_string(),
358            Arc::new(|args| {
359                if args.len() != 2 {
360                    return Err("Expected 2 arguments for function anchor".to_string());
361                }
362                let x = crate::Size::try_coerce(args[0].clone())?;
363                let y = crate::Size::try_coerce(args[1].clone())?;
364                Ok(crate::Transform2D::anchor(x, y).to_pax_value())
365            }),
366        );
367    }
368}
369
370impl HelperFunctions for Transform2 {
371    fn register_all_functions() {
372        register_function(
373            "Transform2".to_string(),
374            "identity".to_string(),
375            Arc::new(|args| {
376                if args.len() != 0 {
377                    return Err("Expected 0 arguments for function identity".to_string());
378                }
379                Ok(Transform2::identity().to_pax_value())
380            }),
381        );
382        register_function(
383            "Transform2".to_string(),
384            "scale".to_string(),
385            Arc::new(|args| {
386                if args.len() != 1 {
387                    return Err("Expected 1 argument for function scale".to_string());
388                }
389                let s = f64::try_coerce(args[0].clone())?;
390                Ok(Transform2::scale(s).to_pax_value())
391            }),
392        );
393
394        register_function(
395            "Transform2".to_string(),
396            "translate".to_string(),
397            Arc::new(|args| {
398                if args.len() != 1 {
399                    return Err("Expected 1 argument for function scale".to_string());
400                }
401                let s = Vector2::try_coerce(args[0].clone())?;
402                Ok(Transform2::translate(s).to_pax_value())
403            }),
404        );
405
406        register_function(
407            "Transform2".to_string(),
408            "rotate".to_string(),
409            Arc::new(|args| {
410                if args.len() != 1 {
411                    return Err("Expected 1 argument for function rotate".to_string());
412                }
413                let s = f64::try_coerce(args[0].clone())?;
414                Ok(Transform2::rotate(s).to_pax_value())
415            }),
416        );
417        register_function(
418            "Transform2".to_string(),
419            "skew".to_string(),
420            Arc::new(|args| {
421                if args.len() != 1 {
422                    return Err("Expected 1 argument for function skew".to_string());
423                }
424                let s = Vector2::try_coerce(args[0].clone())?;
425                Ok(Transform2::skew(s).to_pax_value())
426            }),
427        );
428    }
429}