Skip to content

Commit

Permalink
fix: fixed condition parsing and display (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
ShubhranshuSanjeev committed May 15, 2024
1 parent e249b95 commit 023bc6b
Show file tree
Hide file tree
Showing 11 changed files with 216 additions and 209 deletions.
19 changes: 8 additions & 11 deletions crates/frontend/src/components/condition_pills/condition_pills.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
use super::utils::{extract_and_format, parse_conditions};
use crate::components::condition_pills::types::ConditionOperator;

use super::types::Condition;
use leptos::*;
use serde_json::Value;

#[component]
pub fn context_pills(context: Value) -> impl IntoView {
let condition = extract_and_format(&context);
let ctx_values = parse_conditions(condition.clone());

pub fn condition_pills(#[prop(into)] conditions: Vec<Condition>) -> impl IntoView {
view! {
{ctx_values
{conditions
.into_iter()
.map(|condition| {
let dimension = condition.left_operand;
let op = condition.operator;
let val = condition.right_operand;
let operator = op.clone();
view! {
<span class="inline-flex items-center rounded-md bg-gray-50 px-2 py-1 text-xs ring-1 ring-inset ring-purple-700/10 shadow-md gap-x-2">
<span class="font-mono font-medium context_condition text-gray-500">
{dimension}
</span>
<span class="font-mono font-medium text-gray-650 context_condition ">
{op}
{op.to_string()}
</span>

{match operator.trim() {
"BETWEEN" => {
{match op {
ConditionOperator::Between => {
let split_val: Vec<String> = val
.clone()
.split(",")
Expand Down
141 changes: 140 additions & 1 deletion crates/frontend/src/components/condition_pills/types.rs
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") => {
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
)
}
}
147 changes: 16 additions & 131 deletions crates/frontend/src/components/condition_pills/utils.rs
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()
}
Loading

0 comments on commit 023bc6b

Please sign in to comment.