Skip to content

Commit 2d7a4bc

Browse files
committed
Merge branch 'issue-296-aip-31-div-and-testing-other-fn' into issue-296-aip-31-targeting
2 parents 5b6fd97 + 7a50b66 commit 2d7a4bc

File tree

1 file changed

+170
-24
lines changed

1 file changed

+170
-24
lines changed

primitives/src/targeting/eval.rs

Lines changed: 170 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::BigNum;
22
use serde::{Deserialize, Serialize};
33
use serde_json::{value::Value as SerdeValue, Number};
4-
use std::{convert::TryFrom, fmt, str::FromStr};
4+
use std::{convert::TryFrom, fmt, ops::Div, str::FromStr};
55

66
pub type Map = serde_json::value::Map<String, SerdeValue>;
77

@@ -49,6 +49,10 @@ impl Value {
4949
pub fn new_string(string: &str) -> Self {
5050
Self::String(string.to_string())
5151
}
52+
53+
pub fn new_number(number: impl Into<Number>) -> Self {
54+
Self::Number(number.into())
55+
}
5256
}
5357

5458
impl TryFrom<SerdeValue> for Value {
@@ -77,6 +81,8 @@ impl TryFrom<SerdeValue> for Value {
7781
#[serde(rename_all = "camelCase")]
7882
// TODO: https://github.com/AdExNetwork/adex-validator-stack-rust/issues/296
7983
pub enum Function {
84+
/// Math `div`
85+
Div(Box<Rule>, Box<Rule>),
8086
If(Box<Rule>, Box<Rule>),
8187
And(Box<Rule>, Box<Rule>),
8288
Intersects(Box<Rule>, Box<Rule>),
@@ -98,8 +104,8 @@ impl From<Value> for Rule {
98104
}
99105

100106
impl Function {
101-
pub fn new_if(lhs: impl Into<Rule>, rhs: impl Into<Rule>) -> Self {
102-
Self::If(Box::new(lhs.into()), Box::new(rhs.into()))
107+
pub fn new_if(condition: impl Into<Rule>, then: impl Into<Rule>) -> Self {
108+
Self::If(Box::new(condition.into()), Box::new(then.into()))
103109
}
104110

105111
pub fn new_and(lhs: impl Into<Rule>, rhs: impl Into<Rule>) -> Self {
@@ -113,6 +119,10 @@ impl Function {
113119
pub fn new_get(key: &str) -> Self {
114120
Self::Get(key.to_string())
115121
}
122+
123+
pub fn new_bn(value: impl Into<Value>) -> Self {
124+
Self::Bn(value.into())
125+
}
116126
}
117127

118128
impl Value {
@@ -129,6 +139,24 @@ impl Value {
129139
_ => Err(Error::TypeError),
130140
}
131141
}
142+
143+
pub fn try_bignum(self) -> Result<BigNum, Error> {
144+
BigNum::try_from(self)
145+
}
146+
}
147+
148+
impl TryFrom<Value> for BigNum {
149+
type Error = Error;
150+
fn try_from(value: Value) -> Result<Self, Self::Error> {
151+
match value {
152+
Value::String(string) => BigNum::from_str(&string).map_err(|_| Error::TypeError),
153+
Value::BigNum(big_num) => Ok(big_num),
154+
Value::Number(number) => {
155+
BigNum::from_str(&number.to_string()).map_err(|_| Error::TypeError)
156+
}
157+
_ => Err(Error::TypeError),
158+
}
159+
}
132160
}
133161

134162
/// Evaluates a Rule to be applied and has 3 outcomes:
@@ -149,16 +177,53 @@ fn eval(input: &Input, output: &mut Output, rule: &Rule) -> Result<Option<Value>
149177

150178
// basic operators
151179
let value = match function {
180+
Function::Div(first_rule, second_rule) => {
181+
let value = match first_rule.eval(input, output)?.ok_or(Error::TypeError)? {
182+
Value::Number(first_number) => {
183+
match second_rule.eval(input, output)?.ok_or(Error::TypeError)? {
184+
Value::Number(second_number) => {
185+
if let Some(num) = first_number.as_f64() {
186+
let divided =
187+
num.div(second_number.as_f64().ok_or(Error::TypeError)?);
188+
189+
Value::Number(Number::from_f64(divided).ok_or(Error::TypeError)?)
190+
} else if let Some(num) = first_number.as_i64() {
191+
let rhs = second_number.as_i64().ok_or(Error::TypeError)?;
192+
let divided = num.checked_div(rhs).ok_or(Error::TypeError)?;
193+
194+
Value::Number(divided.into())
195+
} else if let Some(num) = first_number.as_u64() {
196+
let rhs = second_number.as_u64().ok_or(Error::TypeError)?;
197+
let divided = num.checked_div(rhs).ok_or(Error::TypeError)?;
198+
199+
Value::Number(divided.into())
200+
} else {
201+
return Err(Error::TypeError);
202+
}
203+
}
204+
_ => return Err(Error::TypeError),
205+
}
206+
}
207+
Value::BigNum(first_bignum) => {
208+
let second_bignum = second_rule
209+
.eval(input, output)?
210+
.ok_or(Error::TypeError)?
211+
.try_bignum()?;
212+
213+
Value::BigNum(first_bignum.div(second_bignum))
214+
}
215+
_ => return Err(Error::TypeError),
216+
};
217+
218+
Some(value)
219+
}
152220
Function::If(first_rule, second_rule) => {
153221
let eval_if = eval(input, output, first_rule)?
154222
.ok_or(Error::TypeError)?
155223
.try_bool()?;
156224

157225
if eval_if {
158-
let bool = eval(input, output, second_rule)?
159-
.ok_or(Error::TypeError)?
160-
.try_bool()?;
161-
Some(Value::Bool(bool))
226+
eval(input, output, second_rule)?
162227
} else {
163228
None
164229
}
@@ -185,24 +250,9 @@ fn eval(input: &Input, output: &mut Output, rule: &Rule) -> Result<Option<Value>
185250
}
186251
Function::Get(key) => Some(input.try_get(key)?),
187252
Function::Bn(value) => {
188-
let big_num = match value {
189-
Value::String(string) => {
190-
let big_num =
191-
BigNum::from_str(string.as_str()).map_err(|_| Error::TypeError)?;
192-
193-
Value::BigNum(big_num)
194-
}
195-
Value::BigNum(big_num) => Value::BigNum(big_num.clone()),
196-
Value::Number(number) => {
197-
let big_num =
198-
BigNum::from_str(&number.to_string()).map_err(|_| Error::TypeError)?;
253+
let big_num = value.clone().try_bignum()?;
199254

200-
Value::BigNum(big_num)
201-
}
202-
_ => return Err(Error::TypeError),
203-
};
204-
205-
Some(big_num)
255+
Some(Value::BigNum(big_num))
206256
}
207257
};
208258

@@ -293,4 +343,100 @@ mod test {
293343
result.expect("Sould return Non-NULL result!")
294344
);
295345
}
346+
347+
#[test]
348+
fn test_and_eval() {
349+
let input = Input::default();
350+
let mut output = Output {
351+
show: true,
352+
boost: 1.0,
353+
price: Default::default(),
354+
};
355+
356+
let cases = [
357+
(true, true, true),
358+
(false, false, false),
359+
(false, true, false),
360+
(true, false, false),
361+
];
362+
363+
for (lhs, rhs, expected) in cases.iter() {
364+
let rule = Rule::Function(Function::new_and(Value::Bool(*lhs), Value::Bool(*rhs)));
365+
let expected = Some(Value::Bool(*expected));
366+
367+
assert_eq!(Ok(expected), rule.eval(&input, &mut output));
368+
}
369+
}
370+
371+
#[test]
372+
fn test_if_eval() {
373+
let input = Input::default();
374+
let mut output = Output {
375+
show: true,
376+
boost: 1.0,
377+
price: Default::default(),
378+
};
379+
380+
let then = Value::String("yes".to_string());
381+
382+
let rule = Rule::Function(Function::new_if(Value::Bool(true), then.clone()));
383+
384+
assert_eq!(Ok(Some(then.clone())), rule.eval(&input, &mut output));
385+
386+
let rule = Rule::Function(Function::new_if(Value::Bool(false), then));
387+
388+
assert_eq!(Ok(None), rule.eval(&input, &mut output));
389+
}
390+
391+
#[test]
392+
fn test_bn_eval_from_actual_number_value_string_bignum_or_number() {
393+
let input = Input::default();
394+
let mut output = Output {
395+
show: true,
396+
boost: 1.0,
397+
price: Default::default(),
398+
};
399+
400+
let cases = vec![
401+
(Value::new_string("1000"), Value::BigNum(1000.into())),
402+
(Value::new_number(5000), Value::BigNum(5000.into())),
403+
(Value::BigNum(2.into()), Value::BigNum(2.into())),
404+
// rounded floats should work!
405+
(
406+
Value::Number(Number::from_f64(2.0).expect("should create float number")),
407+
Value::BigNum(2.into()),
408+
),
409+
];
410+
411+
for (from, expected) in cases.into_iter() {
412+
let rule = Rule::Function(Function::new_bn(from));
413+
414+
assert_eq!(Ok(Some(expected)), rule.eval(&input, &mut output));
415+
}
416+
}
417+
418+
#[test]
419+
fn test_bn_eval_from_actual_incorrect_value() {
420+
let input = Input::default();
421+
let mut output = Output {
422+
show: true,
423+
boost: 1.0,
424+
price: Default::default(),
425+
};
426+
427+
let error_cases = vec![
428+
Value::new_string("text"),
429+
// BigNums can only be possitive
430+
Value::new_number(-100),
431+
Value::Bool(true),
432+
Value::Array(vec![Value::Bool(false)]),
433+
Value::Number(Number::from_f64(2.5).expect("should create float number")),
434+
];
435+
436+
for error_case in error_cases.into_iter() {
437+
let rule = Rule::Function(Function::new_bn(error_case));
438+
439+
assert_eq!(Err(Error::TypeError), rule.eval(&input, &mut output));
440+
}
441+
}
296442
}

0 commit comments

Comments
 (0)