-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: fixed condition parsing and display #37
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,145 @@ | ||
use std::fmt::Display; | ||
|
||
use serde_json::{Map, Value}; | ||
|
||
use crate::types::Context; | ||
|
||
#[derive(Debug, Clone)] | ||
pub enum ConditionOperator { | ||
Is, | ||
In, | ||
Has, | ||
Between, | ||
Other(String), | ||
} | ||
|
||
impl Display for ConditionOperator { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
Self::Has => f.write_str("has"), | ||
Self::Is => f.write_str("is"), | ||
Self::In => f.write_str("in"), | ||
Self::Between => f.write_str("between"), | ||
Self::Other(o) => f.write_str(o), | ||
} | ||
} | ||
} | ||
|
||
impl From<(String, &Vec<Value>)> for ConditionOperator { | ||
fn from(value: (String, &Vec<Value>)) -> Self { | ||
let (operator, operands) = value; | ||
let operand_0 = operands.get(0); | ||
let operand_1 = operands.get(1); | ||
let operand_2 = operands.get(2); | ||
match (operator.as_str(), operand_0, operand_1, operand_2) { | ||
// assuming there will be only two operands, one with the dimension name and other with the value | ||
("==", _, _, None) => ConditionOperator::Is, | ||
("<=", Some(_), Some(Value::Object(a)), Some(_)) if a.contains_key("var") => { | ||
ConditionOperator::Between | ||
} | ||
// assuming there will be only two operands, one with the dimension name and other with the value | ||
("in", Some(Value::Object(a)), Some(_), None) if a.contains_key("var") => { | ||
ConditionOperator::In | ||
} | ||
// assuming there will be only two operands, one with the dimension name and other with the value | ||
("in", Some(_), Some(Value::Object(a)), None) if a.contains_key("var") => { | ||
ConditionOperator::Has | ||
} | ||
_ => ConditionOperator::Other(operator), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone)] | ||
pub struct Condition { | ||
pub left_operand: String, | ||
pub operator: String, | ||
pub operator: ConditionOperator, | ||
pub right_operand: String, | ||
} | ||
|
||
impl TryFrom<&Map<String, Value>> for Condition { | ||
type Error = &'static str; | ||
fn try_from(source: &Map<String, Value>) -> Result<Self, Self::Error> { | ||
if let Some(operator) = source.keys().next() { | ||
let emty_vec = vec![]; | ||
let operands = source[operator].as_array().unwrap_or(&emty_vec); | ||
|
||
let operator = ConditionOperator::from((operator.to_owned(), operands)); | ||
|
||
let dimension_name = operands | ||
.iter() | ||
.find_map(|item| match item.as_object() { | ||
Some(o) if o.contains_key("var") => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we have better name instead of "o" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hey, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you are using o in |
||
Some(o["var"].as_str().unwrap_or("")) | ||
} | ||
_ => None, | ||
}) | ||
.unwrap_or(""); | ||
|
||
let other_operands = operands | ||
.iter() | ||
.filter_map(|item| { | ||
if item.is_object() && item.as_object().unwrap().contains_key("var") { | ||
return None; | ||
} | ||
|
||
match item { | ||
Value::Null => String::from("null"), | ||
Value::String(v) => v.clone(), | ||
_ => format!("{}", item), | ||
} | ||
.into() | ||
}) | ||
.collect::<Vec<String>>() | ||
.join(","); | ||
|
||
return Ok(Condition { | ||
operator, | ||
left_operand: dimension_name.to_owned(), | ||
right_operand: other_operands, | ||
}); | ||
} | ||
|
||
Err("not a valid condition map") | ||
} | ||
} | ||
|
||
impl TryFrom<&Value> for Condition { | ||
type Error = &'static str; | ||
fn try_from(value: &Value) -> Result<Self, Self::Error> { | ||
let obj = value | ||
.as_object() | ||
.ok_or("not a valid condition value, should be an object")?; | ||
Condition::try_from(obj) | ||
} | ||
} | ||
|
||
impl TryFrom<&Context> for Vec<Condition> { | ||
type Error = &'static str; | ||
fn try_from(context: &Context) -> Result<Self, Self::Error> { | ||
context | ||
.condition | ||
.as_object() | ||
.ok_or("failed to parse context.condition as an object") | ||
.and_then(|obj| match obj.get("and") { | ||
Some(v) => v | ||
.as_array() | ||
.ok_or("failed to parse value of and as array") | ||
.and_then(|arr| { | ||
arr.iter() | ||
.map(|condition| Condition::try_from(condition)) | ||
.collect::<Result<Vec<Condition>, &'static str>>() | ||
}), | ||
None => Condition::try_from(obj).and_then(|v| Ok(vec![v])), | ||
}) | ||
} | ||
} | ||
|
||
impl Into<String> for Condition { | ||
fn into(self) -> String { | ||
format!( | ||
"{} {} {}", | ||
self.left_operand, self.operator, self.right_operand | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,135 +1,20 @@ | ||
use std::mem::swap; | ||
|
||
use super::types::Condition; | ||
use serde_json::Value; | ||
|
||
pub fn parse_conditions(input: Vec<Condition>) -> Vec<Condition> { | ||
let mut conditions = Vec::new(); | ||
|
||
// Split the string by "&&" and iterate over each condition | ||
for condition in input { | ||
let mut key = condition.left_operand; | ||
let mut op = condition.operator; | ||
let mut val = condition.right_operand; | ||
if op == "in" { | ||
swap(&mut key, &mut val) | ||
} | ||
// Add a space after key | ||
key.push(' '); | ||
match op.as_str() { | ||
"==" => { | ||
val = val.trim_matches('"').to_string(); | ||
op = "is".to_string(); | ||
} | ||
"<=" => { | ||
val = val.trim_matches('"').to_string(); | ||
op = "BETWEEN".to_string(); | ||
} | ||
_ => { | ||
val = val.trim_matches('"').to_string(); | ||
op = "has".to_string(); | ||
} | ||
} | ||
op.push(' '); | ||
|
||
conditions.push(Condition { | ||
left_operand: key, | ||
operator: op, | ||
right_operand: val, | ||
}); | ||
} | ||
|
||
conditions | ||
} | ||
|
||
pub fn extract_and_format(condition: &Value) -> Vec<Condition> { | ||
let mut formatted_conditions = Vec::new(); | ||
if condition.is_object() && condition.get("and").is_some() { | ||
// Handling complex "and" conditions | ||
let empty_vec = vec![]; | ||
let conditions_json = condition | ||
.get("and") | ||
.and_then(|val| val.as_array()) | ||
.unwrap_or(&empty_vec); // Default to an empty vector if not an array | ||
|
||
for cond in conditions_json { | ||
if let Some(formatted_condition) = format_condition(cond) { | ||
formatted_conditions.push(formatted_condition); | ||
} | ||
} | ||
} else if let Some(formatted_condition) = format_condition(condition) { | ||
// Handling single conditions | ||
formatted_conditions.push(formatted_condition); | ||
} | ||
formatted_conditions | ||
} | ||
|
||
fn format_condition(condition: &Value) -> Option<Condition> { | ||
if let Some(ref operator) = condition.as_object().and_then(|obj| obj.keys().next()) { | ||
let empty_vec = vec![]; | ||
let operands = condition[operator].as_array().unwrap_or(&empty_vec); | ||
|
||
// Handling the "in" operator differently | ||
if operator.as_str() == "in" { | ||
let left_operand = &operands[0]; | ||
let right_operand = &operands[1]; | ||
|
||
let left_str = if left_operand.is_string() { | ||
format!("\"{}\"", left_operand.as_str().unwrap()) | ||
} else { | ||
format!("{}", left_operand) | ||
}; | ||
|
||
if right_operand.is_object() && right_operand["var"].is_string() { | ||
let var_str = right_operand["var"].as_str().unwrap(); | ||
return Some(Condition { | ||
left_operand: left_str, | ||
operator: operator.to_string(), | ||
right_operand: var_str.to_string(), | ||
}); | ||
} | ||
} | ||
|
||
// Handline the "<=" operator differently | ||
if operator.as_str() == "<=" { | ||
let left_operand = &operands[0]; | ||
let right_operand = &operands[2]; | ||
let mid_operand = &operands[1]; | ||
|
||
let left_str = format!("{}", left_operand).trim_matches('"').to_string(); | ||
let right_str = format!("{}", right_operand).trim_matches('"').to_string(); | ||
|
||
if mid_operand.is_object() && mid_operand["var"].is_string() { | ||
let var_str = mid_operand["var"].as_str().unwrap(); | ||
return Some(Condition { | ||
left_operand: var_str.to_string(), | ||
operator: operator.to_string(), | ||
right_operand: left_str + "," + &right_str, | ||
}); | ||
} | ||
} | ||
// Handling regular operators | ||
if let Some(first_operand) = operands.get(0) { | ||
if first_operand.is_object() && first_operand["var"].is_string() { | ||
let key = first_operand["var"] | ||
.as_str() | ||
.unwrap_or("UnknownVar") | ||
.to_string(); | ||
if let Some(value) = operands.get(1) { | ||
let val_str = match value { | ||
Value::Null => "null".to_string(), | ||
Value::String(v) => v.clone(), | ||
_ => value.to_string(), | ||
}; | ||
return Some(Condition { | ||
left_operand: key.to_string(), | ||
operator: operator.to_string(), | ||
right_operand: val_str, | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
|
||
None | ||
pub fn extract_conditions(context: &Value) -> Vec<Condition> { | ||
context | ||
.as_object() | ||
.and_then(|obj| { | ||
obj.get("and") | ||
.and_then(|v| v.as_array()) | ||
.and_then(|arr| { | ||
Some( | ||
arr.iter() | ||
.filter_map(|condition| Condition::try_from(condition).ok()) | ||
.collect::<Vec<Condition>>(), | ||
) | ||
}) | ||
.or_else(|| Condition::try_from(obj).ok().and_then(|v| Some(vec![v]))) | ||
}) | ||
.unwrap_or_default() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use strum here ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was no way in strum to serialize this
Other(String)
into to just use the inner value, so went ahead with this approach.