rink_core/runtime/
value.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5use super::Substance;
6use crate::loader::Context;
7use crate::parsing::datetime;
8use crate::types::{GenericDateTime, Number};
9use chrono::{DateTime, FixedOffset};
10use chrono_tz::Tz;
11use std::ops::{Add, Div, Mul, Neg, Sub};
12
13#[derive(Clone, Debug)]
14pub enum Value {
15    Number(Number),
16    DateTime(GenericDateTime),
17    Substance(Substance),
18}
19
20pub trait Show {
21    /// Provides a string representation of something, using information contained in a Context.
22    fn show(&self, context: &Context) -> String;
23}
24
25impl Show for DateTime<FixedOffset> {
26    fn show(&self, context: &Context) -> String {
27        if let Some(h) = context.humanize(*self) {
28            format!("{} ({})", self, h)
29        } else {
30            self.to_string()
31        }
32    }
33}
34
35impl Show for DateTime<Tz> {
36    fn show(&self, context: &Context) -> String {
37        if let Some(h) = context.humanize(*self) {
38            format!("{} ({})", self, h)
39        } else {
40            self.to_string()
41        }
42    }
43}
44
45impl Show for GenericDateTime {
46    fn show(&self, context: &Context) -> String {
47        match *self {
48            GenericDateTime::Fixed(ref date) => date.show(context),
49            GenericDateTime::Timezone(ref date) => date.show(context),
50        }
51    }
52}
53
54impl Show for Value {
55    fn show(&self, context: &Context) -> String {
56        match *self {
57            Value::Number(ref num) => num.show(context),
58            Value::DateTime(ref dt) => dt.show(context),
59            Value::Substance(ref v) => v.show(context),
60        }
61    }
62}
63
64impl Value {
65    pub fn pow(&self, exp: &Value) -> Result<Value, String> {
66        match (self, exp) {
67            (&Value::Number(ref left), &Value::Number(ref right)) => {
68                left.pow(right).map(Value::Number)
69            }
70            (_, _) => Err("Operation is not defined".to_string()),
71        }
72    }
73}
74
75impl<'a, 'b> Add<&'b Value> for &'a Value {
76    type Output = Result<Value, String>;
77
78    fn add(self, other: &Value) -> Result<Value, String> {
79        match (self, other) {
80            (&Value::Number(ref left), &Value::Number(ref right)) => (left + right)
81                .ok_or_else(|| {
82                    "Addition of units with mismatched units is not meaningful".to_string()
83                })
84                .map(Value::Number),
85            (&Value::DateTime(ref left), &Value::Number(ref right))
86            | (&Value::Number(ref right), &Value::DateTime(ref left)) => match *left {
87                GenericDateTime::Fixed(left) => left
88                    .checked_add_signed(datetime::to_duration(right)?)
89                    .map(GenericDateTime::Fixed),
90                GenericDateTime::Timezone(left) => left
91                    .checked_add_signed(datetime::to_duration(right)?)
92                    .map(GenericDateTime::Timezone),
93            }
94            .ok_or_else(|| {
95                "Implementation error: value is out of range representable by datetime".to_string()
96            })
97            .map(Value::DateTime),
98            (&Value::Substance(ref left), &Value::Substance(ref right)) => {
99                left.add(right).map(Value::Substance)
100            }
101            (_, _) => Err("Operation is not defined".to_string()),
102        }
103    }
104}
105
106impl<'a, 'b> Sub<&'b Value> for &'a Value {
107    type Output = Result<Value, String>;
108
109    fn sub(self, other: &Value) -> Result<Value, String> {
110        match (self, other) {
111            (&Value::Number(ref left), &Value::Number(ref right)) => (left - right)
112                .ok_or_else(|| {
113                    "Subtraction of units with mismatched units is not meaningful".to_string()
114                })
115                .map(Value::Number),
116            (&Value::DateTime(ref left), &Value::Number(ref right))
117            | (&Value::Number(ref right), &Value::DateTime(ref left)) => match *left {
118                GenericDateTime::Fixed(left) => left
119                    .checked_sub_signed(datetime::to_duration(right)?)
120                    .map(GenericDateTime::Fixed),
121                GenericDateTime::Timezone(left) => left
122                    .checked_sub_signed(datetime::to_duration(right)?)
123                    .map(GenericDateTime::Timezone),
124            }
125            .ok_or_else(|| {
126                "Implementation error: value is out of range representable by datetime".to_string()
127            })
128            .map(Value::DateTime),
129            (&Value::DateTime(ref left), &Value::DateTime(ref right)) => {
130                datetime::from_duration(&match (left, right) {
131                    (&GenericDateTime::Fixed(ref left), &GenericDateTime::Fixed(ref right)) => {
132                        *left - *right
133                    }
134                    (&GenericDateTime::Fixed(ref left), &GenericDateTime::Timezone(ref right)) => {
135                        *left - right.with_timezone(left.offset())
136                    }
137                    (
138                        &GenericDateTime::Timezone(ref left),
139                        &GenericDateTime::Timezone(ref right),
140                    ) => *left - *right,
141                    (&GenericDateTime::Timezone(ref left), &GenericDateTime::Fixed(ref right)) => {
142                        left.with_timezone(right.offset()) - *right
143                    }
144                })
145                .map(Value::Number)
146            }
147            (_, _) => Err("Operation is not defined".to_string()),
148        }
149    }
150}
151
152impl<'a> Neg for &'a Value {
153    type Output = Result<Value, String>;
154
155    fn neg(self) -> Self::Output {
156        match *self {
157            Value::Number(ref num) => (-num)
158                .ok_or_else(|| "Bug: Negation should not fail".to_string())
159                .map(Value::Number),
160            _ => Err("Operation is not defined".to_string()),
161        }
162    }
163}
164
165impl<'a, 'b> Mul<&'b Value> for &'a Value {
166    type Output = Result<Value, String>;
167
168    fn mul(self, other: &Value) -> Result<Value, String> {
169        match (self, other) {
170            (&Value::Number(ref left), &Value::Number(ref right)) => (left * right)
171                .ok_or_else(|| "Bug: Mul should not fail".to_string())
172                .map(Value::Number),
173            (&Value::Number(ref co), &Value::Substance(ref sub))
174            | (&Value::Substance(ref sub), &Value::Number(ref co)) => {
175                (sub * co).map(Value::Substance)
176            }
177            (_, _) => Err("Operation is not defined".to_string()),
178        }
179    }
180}
181
182impl<'a, 'b> Div<&'b Value> for &'a Value {
183    type Output = Result<Value, String>;
184
185    fn div(self, other: &Value) -> Result<Value, String> {
186        match (self, other) {
187            (&Value::Number(ref left), &Value::Number(ref right)) => (left / right)
188                .ok_or_else(|| "Division by zero".to_string())
189                .map(Value::Number),
190            (&Value::Substance(ref sub), &Value::Number(ref co)) => {
191                (sub / co).map(Value::Substance)
192            }
193            (_, _) => Err("Operation is not defined".to_string()),
194        }
195    }
196}
197
198impl Value {
199    pub fn shl(&self, other: &Value) -> Result<Value, String> {
200        match (self, other) {
201            (&Value::Number(ref left), &Value::Number(ref right)) => {
202                left.shl(right).map(Value::Number)
203            }
204            (_, _) => Err("Operation is not defined".to_string()),
205        }
206    }
207
208    pub fn shr(&self, other: &Value) -> Result<Value, String> {
209        match (self, other) {
210            (&Value::Number(ref left), &Value::Number(ref right)) => {
211                left.shr(right).map(Value::Number)
212            }
213            (_, _) => Err("Operation is not defined".to_string()),
214        }
215    }
216
217    pub fn rem(&self, other: &Value) -> Result<Value, String> {
218        match (self, other) {
219            (&Value::Number(ref left), &Value::Number(ref right)) => {
220                left.rem(right).map(Value::Number)
221            }
222            (_, _) => Err("Operation is not defined".to_string()),
223        }
224    }
225
226    pub fn and(&self, other: &Value) -> Result<Value, String> {
227        match (self, other) {
228            (&Value::Number(ref left), &Value::Number(ref right)) => {
229                left.and(right).map(Value::Number)
230            }
231            (_, _) => Err("Operation is not defined".to_string()),
232        }
233    }
234
235    pub fn or(&self, other: &Value) -> Result<Value, String> {
236        match (self, other) {
237            (&Value::Number(ref left), &Value::Number(ref right)) => {
238                left.or(right).map(Value::Number)
239            }
240            (_, _) => Err("Operation is not defined".to_string()),
241        }
242    }
243
244    pub fn xor(&self, other: &Value) -> Result<Value, String> {
245        match (self, other) {
246            (&Value::Number(ref left), &Value::Number(ref right)) => {
247                left.xor(right).map(Value::Number)
248            }
249            (_, _) => Err("Operation is not defined".to_string()),
250        }
251    }
252}