pax_runtime_api/pax_value/
arithmetic.rs

1use std::{
2    cmp::Ordering,
3    ops::{Add, Div, Mul, Neg, Not, Rem, Sub},
4};
5
6use crate::{Numeric, PaxValue, Percent, Size, ToPaxValue};
7
8use super::{PaxAny, ToFromPaxAny};
9
10const ANY_ARITH_UNSUPPORTED: &'static str =
11    "types that are not representable as PaxValues are not supported in arithmetic expressions";
12//----------------------------PaxValue Arithmetic-----------------------------
13impl Add for PaxValue {
14    type Output = Self;
15
16    fn add(self, rhs: Self) -> Self::Output {
17        match (self, rhs) {
18            // Basic types
19            (PaxValue::Numeric(a), PaxValue::Numeric(b)) => (a + b).to_pax_value(),
20            (PaxValue::String(a), PaxValue::String(b)) => (a + &b).to_pax_value(),
21            (PaxValue::String(a), PaxValue::Numeric(b)) => (a + &b.to_string()).to_pax_value(),
22            (PaxValue::Numeric(a), PaxValue::String(b)) => (a.to_string() + &b).to_pax_value(),
23
24            // Size and Percent
25            (PaxValue::Size(a), PaxValue::Size(b)) => (a + b).to_pax_value(),
26            (PaxValue::Percent(a), PaxValue::Percent(b)) => (Percent(a.0 + b.0)).to_pax_value(),
27            (PaxValue::Percent(a), PaxValue::Size(b))
28            | (PaxValue::Size(b), PaxValue::Percent(a)) => (Size::Percent(a.0) + b).to_pax_value(),
29            (PaxValue::Bool(a), PaxValue::Numeric(b))
30            | (PaxValue::Numeric(b), PaxValue::Bool(a)) => {
31                (Numeric::I64(a as i64) + b).to_pax_value()
32            }
33            (PaxValue::Size(a), PaxValue::Numeric(b))
34            | (PaxValue::Numeric(b), PaxValue::Size(a)) => match a {
35                Size::Pixels(px) => Size::Pixels(px + b).to_pax_value(),
36                Size::Percent(per) => Size::Percent(per + b).to_pax_value(),
37                Size::Combined(px, per) => Size::Combined(px + b, per + b).to_pax_value(),
38            },
39            (PaxValue::Numeric(a), PaxValue::Percent(b)) => Size::Combined(a, b.0).to_pax_value(),
40            (PaxValue::Percent(a), PaxValue::Numeric(b)) => Size::Combined(b, a.0).to_pax_value(),
41            (a, b) => {
42                log::warn!("can't add {:?} and {:?}", a, b);
43                PaxValue::default()
44            }
45        }
46    }
47}
48
49impl Mul for PaxValue {
50    type Output = Self;
51
52    fn mul(self, rhs: Self) -> Self::Output {
53        match (self, rhs) {
54            (PaxValue::Numeric(a), PaxValue::Numeric(b)) => (a * b).to_pax_value(),
55            (PaxValue::Bool(a), PaxValue::Numeric(b))
56            | (PaxValue::Numeric(b), PaxValue::Bool(a)) => {
57                (Numeric::I64(a as i64) * b).to_pax_value()
58            }
59            (PaxValue::Bool(a), PaxValue::Percent(b))
60            | (PaxValue::Percent(b), PaxValue::Bool(a)) => {
61                if a {
62                    return PaxValue::Percent(b);
63                } else {
64                    return PaxValue::Percent(Percent(0.into()));
65                }
66            }
67            (PaxValue::Size(a), PaxValue::Numeric(b))
68            | (PaxValue::Numeric(b), PaxValue::Size(a)) => match a {
69                Size::Pixels(px) => Size::Pixels(px * b).to_pax_value(),
70                Size::Percent(per) => Size::Percent(per * b).to_pax_value(),
71                Size::Combined(px, per) => Size::Combined(px * b, per * b).to_pax_value(),
72            },
73            (a, b) => {
74                log::warn!("can't multiply {:?} and {:?}", a, b);
75                PaxValue::default()
76            }
77        }
78    }
79}
80
81impl Sub for PaxValue {
82    type Output = Self;
83
84    fn sub(self, rhs: Self) -> Self::Output {
85        match (self, rhs) {
86            (PaxValue::Numeric(a), PaxValue::Numeric(b)) => (a - b).to_pax_value(),
87            // Size and Percent
88            (PaxValue::Size(a), PaxValue::Size(b)) => (a - b).to_pax_value(),
89            (PaxValue::Percent(a), PaxValue::Percent(b)) => (Percent(a.0 - b.0)).to_pax_value(),
90            (PaxValue::Percent(a), PaxValue::Size(b)) => (Size::Percent(a.0) - b).to_pax_value(),
91            (PaxValue::Size(a), PaxValue::Percent(b)) => (a - Size::Percent(b.0)).to_pax_value(),
92            (PaxValue::Size(a), PaxValue::Numeric(b))
93            | (PaxValue::Numeric(b), PaxValue::Size(a)) => match a {
94                Size::Pixels(px) => Size::Pixels(px - b).to_pax_value(),
95                Size::Percent(per) => Size::Percent(per - b).to_pax_value(),
96                Size::Combined(px, per) => Size::Combined(px - b, per - b).to_pax_value(),
97            },
98            (a, b) => {
99                log::warn!("can't subtract {:?} and {:?}", a, b);
100                PaxValue::default()
101            }
102        }
103    }
104}
105
106impl Div for PaxValue {
107    type Output = Self;
108
109    fn div(self, rhs: Self) -> Self::Output {
110        match (self, rhs) {
111            (PaxValue::Numeric(a), PaxValue::Numeric(b)) => (a / b).to_pax_value(),
112            (PaxValue::Size(a), PaxValue::Numeric(b))
113            | (PaxValue::Numeric(b), PaxValue::Size(a)) => match a {
114                Size::Pixels(px) => Size::Pixels(px / b).to_pax_value(),
115                Size::Percent(per) => Size::Percent(per / b).to_pax_value(),
116                Size::Combined(px, per) => Size::Combined(px / b, per / b).to_pax_value(),
117            },
118            (a, b) => {
119                log::warn!("can't divide {:?} and {:?}", a, b);
120                PaxValue::default()
121            }
122        }
123    }
124}
125
126impl Neg for PaxValue {
127    type Output = Self;
128
129    fn neg(self) -> Self::Output {
130        match self {
131            PaxValue::Numeric(a) => (-a).to_pax_value(),
132            PaxValue::Size(a) => (-a).to_pax_value(),
133            PaxValue::Percent(p) => (-p.0).to_pax_value(),
134            PaxValue::Rotation(r) => (-r).to_pax_value(),
135            a => {
136                log::warn!("can't negate {:?}", a);
137                PaxValue::default()
138            }
139        }
140    }
141}
142
143impl PartialOrd for PaxValue {
144    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
145        match (self, rhs) {
146            (PaxValue::Bool(a), PaxValue::Bool(b)) => a.partial_cmp(b),
147            (PaxValue::Numeric(a), PaxValue::Numeric(b)) => a.partial_cmp(b),
148            (PaxValue::String(a), PaxValue::String(b)) => a.partial_cmp(b),
149            (a, b) => {
150                log::warn!("can't compare {:?} and {:?}", a, b);
151                None
152            }
153        }
154    }
155}
156
157impl Rem for PaxValue {
158    type Output = Self;
159
160    fn rem(self, rhs: Self) -> Self::Output {
161        match (self, rhs) {
162            (PaxValue::Numeric(a), PaxValue::Numeric(b)) => (a % b).to_pax_value(),
163            (a, b) => {
164                log::warn!("can't take remainder of {:?} and {:?}", a, b);
165                PaxValue::default()
166            }
167        }
168    }
169}
170
171impl Not for PaxValue {
172    type Output = PaxValue;
173
174    fn not(self) -> Self::Output {
175        match self {
176            PaxValue::Bool(v) => PaxValue::Bool(!v),
177            v => {
178                log::warn!("can't use not(!) operator on {:?}", v);
179                PaxValue::default()
180            }
181        }
182    }
183}
184
185impl PaxValue {
186    // the operator && currently can't be overloaded, we use this function instead
187    // NOTE: this does not short circuit in the normal way that && does
188    pub fn op_and(self, rhs: Self) -> Self {
189        match (self, rhs) {
190            (PaxValue::Bool(a), PaxValue::Bool(b)) => PaxValue::Bool(a && b),
191            (a, b) => {
192                //panic!("&& operator not valid for {:?} and {:?}", a, b);
193                log::warn!("&& operator not valid for {:?} and {:?}", a, b);
194                PaxValue::default()
195            }
196        }
197    }
198    // the operator || currently can't be overloaded, we use this function instead
199    // NOTE: this does not short circuit in the normal way that && does
200    pub fn op_or(self, rhs: Self) -> Self {
201        match (self, rhs) {
202            (PaxValue::Bool(a), PaxValue::Bool(b)) => PaxValue::Bool(a || b),
203            (a, b) => {
204                //panic!("&& operator not valid for {:?} and {:?}", a, b)
205                log::warn!("&& operator not valid for {:?} and {:?}", a, b);
206                PaxValue::default()
207            }
208        }
209    }
210
211    pub fn op_not(self) -> Self {
212        match self {
213            PaxValue::Bool(v) => PaxValue::Bool(!v),
214            v => {
215                log::warn!("! operator not valid for {:?}", v);
216                PaxValue::default()
217            }
218        }
219    }
220
221    // exponentiation, self ^ exp
222    pub fn pow(self, exp: Self) -> Self {
223        match (self, exp) {
224            (PaxValue::Numeric(a), PaxValue::Numeric(b)) => a.pow(b).to_pax_value(),
225            (a, b) => {
226                log::warn!("exponentiation not valid between {:?} and {:?}", a, b);
227                PaxValue::default()
228            }
229        }
230    }
231
232    pub fn min(self, rhs: Self) -> Self {
233        match (self, rhs) {
234            (PaxValue::Numeric(a), PaxValue::Numeric(b)) => a.min(b).to_pax_value(),
235            (a, b) => {
236                log::warn!("min not valid between {:?} and {:?}", a, b);
237                PaxValue::default()
238            }
239        }
240    }
241
242    pub fn max(self, rhs: Self) -> Self {
243        match (self, rhs) {
244            (PaxValue::Numeric(a), PaxValue::Numeric(b)) => a.max(b).to_pax_value(),
245            (a, b) => {
246                log::warn!("max not valid between {:?} and {:?}", a, b);
247                PaxValue::default()
248            }
249        }
250    }
251}
252
253//----------------------------PaxAny Arithmetic-----------------------------
254impl Add for PaxAny {
255    type Output = Self;
256
257    fn add(self, rhs: Self) -> Self::Output {
258        match (self, rhs) {
259            (PaxAny::Builtin(a), PaxAny::Builtin(b)) => (a + b).to_pax_any(),
260            _ => panic!("{}", ANY_ARITH_UNSUPPORTED),
261        }
262    }
263}
264
265impl Mul for PaxAny {
266    type Output = Self;
267
268    fn mul(self, rhs: Self) -> Self::Output {
269        match (self, rhs) {
270            (PaxAny::Builtin(a), PaxAny::Builtin(b)) => (a * b).to_pax_any(),
271            _ => panic!("{}", ANY_ARITH_UNSUPPORTED),
272        }
273    }
274}
275
276impl Sub for PaxAny {
277    type Output = Self;
278
279    fn sub(self, rhs: Self) -> Self::Output {
280        match (self, rhs) {
281            (PaxAny::Builtin(a), PaxAny::Builtin(b)) => (a - b).to_pax_any(),
282            _ => panic!("{}", ANY_ARITH_UNSUPPORTED),
283        }
284    }
285}
286
287impl Div for PaxAny {
288    type Output = Self;
289
290    fn div(self, rhs: Self) -> Self::Output {
291        match (self, rhs) {
292            (PaxAny::Builtin(a), PaxAny::Builtin(b)) => (a / b).to_pax_any(),
293            _ => panic!("{}", ANY_ARITH_UNSUPPORTED),
294        }
295    }
296}
297
298impl Rem for PaxAny {
299    type Output = Self;
300
301    fn rem(self, rhs: Self) -> Self::Output {
302        match (self, rhs) {
303            (PaxAny::Builtin(a), PaxAny::Builtin(b)) => (a % b).to_pax_any(),
304            _ => panic!("{}", ANY_ARITH_UNSUPPORTED),
305        }
306    }
307}
308
309impl Neg for PaxAny {
310    type Output = Self;
311
312    fn neg(self) -> Self::Output {
313        match self {
314            PaxAny::Builtin(a) => (-a).to_pax_any(),
315            _ => panic!("{}", ANY_ARITH_UNSUPPORTED),
316        }
317    }
318}
319
320impl PartialEq for PaxAny {
321    fn eq(&self, rhs: &Self) -> bool {
322        match (self, rhs) {
323            (PaxAny::Builtin(a), PaxAny::Builtin(b)) => a == b,
324            _ => panic!("{}", ANY_ARITH_UNSUPPORTED),
325        }
326    }
327}
328
329impl PartialOrd for PaxAny {
330    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
331        match (self, rhs) {
332            (PaxAny::Builtin(a), PaxAny::Builtin(b)) => a.partial_cmp(b),
333            _ => panic!("{}", ANY_ARITH_UNSUPPORTED),
334        }
335    }
336}
337
338impl Not for PaxAny {
339    type Output = PaxAny;
340
341    fn not(self) -> Self::Output {
342        match self {
343            PaxAny::Builtin(v) => (!v).to_pax_any(),
344            _ => panic!("{}", ANY_ARITH_UNSUPPORTED),
345        }
346    }
347}
348
349impl PaxAny {
350    // the operator && currently can't be overloaded, we use this function instead
351    // NOTE: this does not short circuit in the normal way that && does
352    pub fn op_and(self, rhs: Self) -> Self {
353        match (self, rhs) {
354            (PaxAny::Builtin(a), PaxAny::Builtin(b)) => a.op_and(b).to_pax_any(),
355            _ => panic!("{}", ANY_ARITH_UNSUPPORTED),
356        }
357    }
358    // the operator || currently can't be overloaded, we use this function instead
359    // NOTE: this does not short circuit in the normal way that && does
360    pub fn op_or(self, rhs: Self) -> Self {
361        match (self, rhs) {
362            (PaxAny::Builtin(a), PaxAny::Builtin(b)) => a.op_or(b).to_pax_any(),
363            _ => panic!("{}", ANY_ARITH_UNSUPPORTED),
364        }
365    }
366
367    // exponentiation, self ^ exp
368    pub fn pow(self, exp: Self) -> Self {
369        match (self, exp) {
370            (PaxAny::Builtin(a), PaxAny::Builtin(b)) => a.pow(b).to_pax_any(),
371            _ => panic!("{}", ANY_ARITH_UNSUPPORTED),
372        }
373    }
374}