Skip to content

Commit f4ece77

Browse files
committed
primitives - targeting - eval of if, and, intersects & get
1 parent 1f1953f commit f4ece77

File tree

1 file changed

+151
-40
lines changed

1 file changed

+151
-40
lines changed

primitives/src/targeting.rs

Lines changed: 151 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,86 @@
11
use crate::BigNum;
2+
use serde::{Deserialize, Serialize};
23
use serde_json::{
34
value::{Map as SerdeMap, Value as SerdeValue},
45
Number,
56
};
67
use std::convert::TryFrom;
8+
use std::fmt;
79

810
pub type Map = SerdeMap<String, SerdeValue>;
9-
pub type Rule = SerdeValue;
1011

12+
#[derive(Debug)]
1113
pub enum Error {
1214
TypeError,
1315
UnknownVariable,
1416
}
1517

16-
#[derive(Debug, Clone, PartialEq, Eq)]
17-
pub enum EvalValue {
18-
Null,
18+
impl fmt::Display for Error {
19+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20+
match self {
21+
Error::TypeError => write!(f, "TypeError: Wrong type"),
22+
Error::UnknownVariable => write!(f, "UnknownVariable: Unknown varialbe passed"),
23+
}
24+
}
25+
}
26+
27+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
28+
#[serde(try_from = "SerdeValue")]
29+
pub enum Value {
1930
Bool(bool),
2031
Number(Number),
2132
String(String),
22-
Array(Vec<EvalValue>),
33+
Array(Vec<Value>),
2334
BigNum(BigNum),
2435
}
2536

26-
impl TryFrom<SerdeValue> for EvalValue {
37+
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
38+
#[serde(untagged)]
39+
pub enum Rule {
40+
Function(Function),
41+
Value(Value),
42+
}
43+
44+
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
45+
#[serde(rename_all = "camelCase")]
46+
pub enum Function {
47+
If(Box<Rule>, Box<Rule>),
48+
And(Box<Rule>, Box<Rule>),
49+
Intersects(Box<Rule>, Box<Rule>),
50+
Get(String),
51+
}
52+
53+
impl TryFrom<SerdeValue> for Value {
2754
type Error = Error;
2855

2956
fn try_from(serde_value: SerdeValue) -> Result<Self, Self::Error> {
3057
match serde_value {
31-
SerdeValue::Null => Ok(Self::Null),
3258
SerdeValue::Bool(bool) => Ok(Self::Bool(bool)),
3359
SerdeValue::Number(number) => Ok(Self::Number(number)),
3460
SerdeValue::String(string) => Ok(Self::String(string)),
3561
SerdeValue::Array(serde_array) => {
3662
let array = serde_array
3763
.into_iter()
38-
.map(EvalValue::try_from)
64+
.map(Value::try_from)
3965
.collect::<Result<_, _>>()?;
4066
Ok(Self::Array(array))
4167
}
42-
SerdeValue::Object(_) => Err(Error::TypeError),
68+
SerdeValue::Object(_) | SerdeValue::Null => Err(Error::TypeError),
4369
}
4470
}
4571
}
4672

47-
impl EvalValue {
48-
pub fn try_bool(&self) -> Result<bool, Error> {
49-
match *self {
73+
impl Value {
74+
pub fn try_bool(self) -> Result<bool, Error> {
75+
match self {
5076
Self::Bool(b) => Ok(b),
5177
_ => Err(Error::TypeError),
5278
}
5379
}
5480

55-
pub fn try_array(&self) -> Result<Vec<EvalValue>, Error> {
56-
match *self {
57-
Self::Array(ref array) => Ok(array.to_vec()),
81+
pub fn try_array(self) -> Result<Vec<Value>, Error> {
82+
match self {
83+
Self::Array(array) => Ok(array),
5884
_ => Err(Error::TypeError),
5985
}
6086
}
@@ -69,39 +95,124 @@ impl EvalValue {
6995
/// - Array
7096
/// - Mutates output
7197
/// - Throws an error
72-
// TODO: Move to own module!
73-
pub fn eval(input: &Map, output: &mut Map, rule: &Rule) -> Result<EvalValue, Error> {
74-
let rule = match rule {
75-
Rule::Null => return Err(Error::TypeError),
76-
Rule::Object(map) => map,
77-
value => return EvalValue::try_from(value.to_owned()),
98+
pub fn eval(input: &Map, output: &mut Map, rule: &Rule) -> Result<Option<Value>, Error> {
99+
let function = match rule {
100+
Rule::Value(value) => return Ok(Some(value.clone())),
101+
Rule::Function(function) => function,
78102
};
79103

80104
// basic operators
81-
if let Some(SerdeValue::Array(array)) = rule.get("if") {
82-
let (first_rule, second_rule) = match array.get(0..=1) {
83-
Some(&[ref first_rule, ref second_rule]) => (first_rule, second_rule),
84-
_ => return Err(Error::TypeError),
85-
};
105+
let value = match function {
106+
Function::If(first_rule, second_rule) => {
107+
let eval_if = eval(input, output, first_rule)?
108+
.ok_or(Error::TypeError)?
109+
.try_bool()?;
110+
111+
if eval_if {
112+
let bool = eval(input, output, second_rule)?
113+
.ok_or(Error::TypeError)?
114+
.try_bool()?;
115+
Some(Value::Bool(bool))
116+
} else {
117+
None
118+
}
119+
}
120+
Function::And(first_rule, second_rule) => {
121+
let a = eval(input, output, first_rule)?
122+
.ok_or(Error::TypeError)?
123+
.try_bool()?;
124+
let b = eval(input, output, second_rule)?
125+
.ok_or(Error::TypeError)?
126+
.try_bool()?;
127+
128+
Some(Value::Bool(a && b))
129+
}
130+
Function::Intersects(first_rule, second_rule) => {
131+
let a = eval(input, output, first_rule)?
132+
.ok_or(Error::TypeError)?
133+
.try_array()?;
134+
let b = eval(input, output, second_rule)?
135+
.ok_or(Error::TypeError)?
136+
.try_array()?;
86137

87-
let eval_if = eval(input, output, first_rule)?.try_bool()?;
138+
Some(Value::Bool(a.iter().any(|x| b.contains(x))))
139+
}
140+
Function::Get(key) => {
141+
let input_value = input.get(key).ok_or(Error::UnknownVariable)?;
88142

89-
if eval_if {
90-
let bool = eval(input, output, second_rule)?.try_bool()?;
91-
return Ok(EvalValue::Bool(bool));
143+
Some(Value::try_from(input_value.clone())?)
92144
}
93-
} else if let Some(SerdeValue::Array(array)) = rule.get("intersects") {
94-
// lists
95-
let (first_rule, second_rule) = match array.get(0..=1) {
96-
Some(&[ref first_rule, ref second_rule]) => (first_rule, second_rule),
97-
_ => return Err(Error::TypeError),
98-
};
145+
};
146+
147+
Ok(value)
148+
}
149+
150+
#[cfg(test)]
151+
mod test {
152+
use super::*;
153+
154+
#[test]
155+
fn deserialzes_intersects_rule() {
156+
let json = r#"{"intersects": [{ "get": "adSlot.categories" }, ["News", "Bitcoin"]]}"#;
157+
158+
let parsed_rule = serde_json::from_str::<Rule>(json).expect("Should deserialize");
99159

100-
let a = eval(input, output, first_rule)?.try_array()?;
101-
let b = eval(input, output, second_rule)?.try_array()?;
160+
let mut expected_map = SerdeMap::new();
161+
expected_map.insert(
162+
"get".to_string(),
163+
SerdeValue::String("adSlot.categories".to_string()),
164+
);
102165

103-
return Ok(EvalValue::Bool(a.iter().any(|x| b.contains(x))));
166+
let expected = Rule::Function(Function::Intersects(
167+
Box::new(Rule::Function(Function::Get(
168+
"adSlot.categories".to_string(),
169+
))),
170+
Box::new(Rule::Value(Value::Array(vec![
171+
Value::String("News".to_string()),
172+
Value::String("Bitcoin".to_string()),
173+
]))),
174+
));
175+
176+
assert_eq!(expected, parsed_rule)
104177
}
105178

106-
Ok(EvalValue::Null)
179+
/// ```json
180+
/// {
181+
/// "intersects": [
182+
/// {
183+
/// "get": "publisherId"
184+
/// },
185+
/// [
186+
/// "0xd5860D6196A4900bf46617cEf088ee6E6b61C9d6",
187+
/// "0xd5860D6196A4900bf46617cEf088ee6E6b61C9d3"
188+
/// ]
189+
/// ]
190+
/// }
191+
/// ```
192+
#[test]
193+
fn test_simple_intersect_eval() {
194+
let input: Map = vec![(
195+
"publisherId".to_string(),
196+
SerdeValue::Array(vec![SerdeValue::String(
197+
"0xd5860D6196A4900bf46617cEf088ee6E6b61C9d6".to_string(),
198+
)]),
199+
)]
200+
.into_iter()
201+
.collect();
202+
let mut output = SerdeMap::new();
203+
204+
let publishers = vec![
205+
Value::String("0xd5860D6196A4900bf46617cEf088ee6E6b61C9d6".to_string()),
206+
Value::String("0xd5860D6196A4900bf46617cEf088ee6E6b61C9d3".to_string()),
207+
];
208+
209+
let rules = Rule::Function(Function::Intersects(
210+
Box::new(Rule::Function(Function::Get("publisherId".to_string()))),
211+
Box::new(Rule::Value(Value::Array(publishers))),
212+
));
213+
214+
let result = eval(&input, &mut output, &rules).expect("Should eval rules");
215+
216+
assert_eq!(Value::Bool(true), result.expect("Sould be Some!"));
217+
}
107218
}

0 commit comments

Comments
 (0)