1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#![allow(clippy::should_implement_trait)]
use {
super::ValueCore,
crate::{Convert, ConvertFrom, Result, Value, ValueError},
};
macro_rules! natural_binary_op {
($name: ident, $trait: ident, $op: tt) => {
pub fn $name<Core>(self, other: Self) -> Result<Self>
where
Core: ValueCore + $trait<Output = Core>,
{
let (left, right) = (Core::convert_from(self)?, Core::convert_from(other)?);
let result = left $op right;
Ok(result.into())
}
};
}
macro_rules! natural_binary_ops {
($(($name: ident, $trait: ident, $op: tt, $generic_name: ident)),+) => {
use std::ops::{$($trait),+};
impl Value {
$(
natural_binary_op!($name, $trait, $op);
generic!($name, $generic_name);
)+
}
}
}
macro_rules! boolean_binary_op {
($name: ident, $op: tt) => {
pub fn $name(self, other: Self) -> Result<Self>
{
let (left, right): (bool, bool) = (self.convert()?, other.convert()?);
let result = left $op right;
Ok(result.into())
}
};
}
macro_rules! boolean_binary_ops {
($(($name: ident, $op: tt)),+) => {
impl Value {
$(boolean_binary_op!($name, $op);)+
}
}
}
macro_rules! comparative_binary_op {
($name: ident, $op: tt) => {
pub fn $name(self, other: Self) -> Result<Self> {
Ok(Value::Bool(self $op other))
}
};
}
macro_rules! comparative_binary_ops {
($(($name: ident, $op: tt)),+) => {
impl Value {
$(comparative_binary_op!($name, $op);)+
}
}
}
macro_rules! generic {
($name: ident, $generic_name: ident) => {
pub fn $generic_name(self, other: Self) -> Result<Self> {
if matches!(self, Value::Null) || matches!(other, Value::Null) {
Ok(Value::Null)
} else if !i64::convert_from(self.clone()).is_err()
&& !i64::convert_from(other.clone()).is_err()
{
self.$name::<i64>(other)
} else if !f64::convert_from(self.clone()).is_err()
&& !f64::convert_from(other.clone()).is_err()
{
self.$name::<f64>(other)
} else {
Err(ValueError::OnlySupportsNumeric(
if f64::convert_from(self.clone()).is_err() {
self
} else {
other
},
stringify!($name),
)
.into())
}
}
};
}
natural_binary_ops!(
(add, Add, +, generic_add),
(subtract, Sub, -, generic_subtract),
(multiply, Mul, *, generic_multiply),
(divide, Div, /, generic_divide),
(modulus, Rem, %, generic_modulus)
);
boolean_binary_ops!(
(and, &),
(or, |),
(xor, ^)
);
comparative_binary_ops!(
(eq, ==),
(not_eq, !=),
(gt, >),
(gt_eq, >=),
(lt, <),
(lt_eq, <=)
);
impl Value {
pub fn string_concat(self, other: Self) -> Result<Self> {
if matches!(self, Value::Null) || matches!(other, Value::Null) {
Ok(Value::Null)
} else {
Ok(format!(
"{}{}",
String::convert_from(self)?,
String::convert_from(other)?
)
.into())
}
}
}