Skip to content

Commit

Permalink
feat: JsonLogic algebraic representation
Browse files Browse the repository at this point in the history
  • Loading branch information
ShubhranshuSanjeev committed Dec 4, 2024
1 parent ab126a5 commit 71868e6
Show file tree
Hide file tree
Showing 4 changed files with 373 additions and 19 deletions.
94 changes: 82 additions & 12 deletions crates/frontend/src/components/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@ use crate::{
schema::{EnumVariants, HtmlDisplay, JsonSchemaType, SchemaType},
};

use crate::logic::Operator;

#[derive(Debug, Clone, PartialEq)]
pub enum InputType {
Text,
Number,
Integer,

Toggle,
Monaco,
Select(EnumVariants),

Disabled,
}

Expand Down Expand Up @@ -64,7 +64,30 @@ impl From<(SchemaType, EnumVariants)> for InputType {
}
}

fn str_to_value(s: &str, type_: &JsonSchemaType) -> Result<Value, String> {
impl From<(SchemaType, EnumVariants, Operator)> for InputType {
fn from(
(schema_type, enum_variants, operator): (SchemaType, EnumVariants, Operator),
) -> Self {
if operator == Operator::In {
return InputType::Text;
}

if !enum_variants.is_empty() {
return InputType::Select(enum_variants);
}

match schema_type {
SchemaType::Single(JsonSchemaType::Number) => InputType::Number,
SchemaType::Single(JsonSchemaType::Integer) => InputType::Integer,
SchemaType::Single(JsonSchemaType::Boolean) => InputType::Toggle,
SchemaType::Single(JsonSchemaType::Null) => InputType::Disabled,
_ => InputType::Text,
}
}
}

// TODO: Also add schema validation in frontend :::::
fn parse(s: &str, type_: &JsonSchemaType) -> Result<Value, String> {
match type_ {
JsonSchemaType::String => Ok(Value::String(s.to_string())),
JsonSchemaType::Number => s
Expand All @@ -90,14 +113,55 @@ fn str_to_value(s: &str, type_: &JsonSchemaType) -> Result<Value, String> {
}
}

fn parse_input_value(value: String, schema_type: SchemaType) -> Result<Value, String> {
fn parse_with_operator(
s: &str,
type_: &JsonSchemaType,
op: &Operator,
) -> Result<Value, String> {
match op {
Operator::In => match type_ {
JsonSchemaType::String => serde_json::from_str::<Vec<String>>(s)
.map(|v| json!(v))
.map_err(|_| "not a valid array of strings".to_string()),
JsonSchemaType::Number => serde_json::from_str::<Vec<f64>>(s)
.map(|v| json!(v))
.map_err(|_| "not a valid array of numbers".to_string()),
JsonSchemaType::Integer => serde_json::from_str::<Vec<i64>>(s)
.map(|v| json!(v))
.map_err(|_| "not a valid array of integers".to_string()),
JsonSchemaType::Boolean => serde_json::from_str::<Vec<bool>>(s)
.map(|v| json!(v))
.map_err(|_| "not a valid array of booleans".to_string()),
JsonSchemaType::Array => serde_json::from_str::<Vec<Value>>(s)
.map(|v| json!(v))
.map_err(|_| "not a valid array of arrays".to_string()),
JsonSchemaType::Object => serde_json::from_str::<Vec<Map<String, Value>>>(s)
.map(|v| json!(v))
.map_err(|_| "not a valid array of objects".to_string()),
JsonSchemaType::Null if s == "null" => Ok(Value::Null),
JsonSchemaType::Null => Err("not a null value".to_string()),
},
_ => parse(s, type_),
}
}

fn parse_input(
value: String,
schema_type: SchemaType,
op: &Option<Operator>,
) -> Result<Value, String> {
let parse_single = |r#type: &JsonSchemaType| match op {
Some(op) => parse_with_operator(&value, r#type, op),
None => parse(&value, r#type),
};

match schema_type {
SchemaType::Single(ref type_) => str_to_value(&value, type_),
SchemaType::Single(ref r#type) => parse_single(r#type),
SchemaType::Multiple(mut types) => {
types.sort_by(|a, b| a.precedence().cmp(&b.precedence()));

for type_ in types.iter() {
let v = str_to_value(&value, type_);
for r#type in types.iter() {
let v = parse_single(r#type);
if v.is_ok() {
return v;
}
Expand Down Expand Up @@ -183,6 +247,7 @@ fn basic_input(
value: Value,
schema_type: SchemaType,
on_change: Callback<Value, ()>,
#[prop(default = None)] operator: Option<Operator>,
) -> impl IntoView {
let schema_type = store_value(schema_type);
let (error_rs, error_ws) = create_signal::<Option<String>>(None);
Expand All @@ -208,7 +273,7 @@ fn basic_input(
value=value.html_display()
on:change=move |e| {
let v = event_target_value(&e);
match parse_input_value(v, schema_type.get_value()) {
match parse_input(v, schema_type.get_value(), &operator) {
Ok(v) => {
on_change.call(v);
error_ws.set(None);
Expand Down Expand Up @@ -244,6 +309,7 @@ pub fn monaco_input(
value: Value,
on_change: Callback<Value, ()>,
schema_type: SchemaType,
#[prop(default = None)] operator: Option<Operator>,
) -> impl IntoView {
let id = store_value(id);
let schema_type = store_value(schema_type);
Expand Down Expand Up @@ -273,7 +339,7 @@ pub fn monaco_input(
logging::log!("Saving editor value: {}", editor_value);

let parsed_value =
parse_input_value(editor_value.clone(), schema_type.get_value());
parse_input(editor_value.clone(), schema_type.get_value(), &operator);
match parsed_value {
Ok(v) => {
logging::log!("Saving parsed value: {}", editor_value);
Expand Down Expand Up @@ -421,24 +487,25 @@ pub fn monaco_input(
pub fn input(
value: Value,
schema_type: SchemaType,
on_change: Callback<Value, ()>,
#[prop(into)] on_change: Callback<Value, ()>,
#[prop(into)] r#type: InputType,
#[prop(default = false)] disabled: bool,
#[prop(into, default = String::new())] id: String,
#[prop(into, default = String::new())] class: String,
#[prop(into, default = String::new())] name: String,
#[prop(default = None)] operator: Option<Operator>,
) -> impl IntoView {
match r#type {
InputType::Toggle => match value.as_bool() {
Some(ref v) => {
view! { <Toggle value=v.clone() on_change class name/> }.into_view()
}
None => view! { <span>An error occured</span> }.into_view(),
None => view! { <Toggle value=false on_change class name/> }.into_view(),
},
InputType::Select(ref options) => view! { <Select id name class value on_change disabled options=options.0.clone()/> }
.into_view(),
InputType::Monaco => {
view! { <MonacoInput id class value on_change schema_type/> }.into_view()
view! { <MonacoInput id class value on_change schema_type operator/> }.into_view()
}
_ => {
view! {
Expand All @@ -452,8 +519,11 @@ pub fn input(
value=value
schema_type=schema_type
on_change=on_change
operator=operator
/>
}
}
}
}


1 change: 1 addition & 0 deletions crates/frontend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod api;
pub mod app;
pub mod components;
pub mod hoc;
pub mod logic;
pub mod pages;
pub mod providers;
pub mod schema;
Expand Down
Loading

0 comments on commit 71868e6

Please sign in to comment.