From 8d210c3da36d3ff1d1a4e8e769fd3c1ce246a42c Mon Sep 17 00:00:00 2001 From: Elias Renman Date: Thu, 23 Nov 2023 22:07:16 +0100 Subject: [PATCH] feat: select processor --- src/database/select_processor.rs | 110 ++++++++++++++++------------- src/tests/select_processor_test.rs | 63 ++++++++++------- 2 files changed, 97 insertions(+), 76 deletions(-) diff --git a/src/database/select_processor.rs b/src/database/select_processor.rs index 043b4d6..2910ce5 100644 --- a/src/database/select_processor.rs +++ b/src/database/select_processor.rs @@ -1,29 +1,13 @@ -use std::{borrow::Borrow, collections::HashMap, str::FromStr}; +use std::collections::HashMap; -use serde_json::{json, map::Values, Map, Value}; +use serde_json::{json, Map, Value}; use crate::database::relation::{OneToMany, OneToOne}; -use super::{row::Row, table::Table, Database}; +use super::{row::Row, Database}; pub struct SelectProcessor {} -/** -{ - select: [ - "id", - "name", - "relation.*" - ] -} -{ - select: [ - "id", - "name", - "relation.id" - ] -} -// -*/ + impl SelectProcessor { // recursively select relations @@ -31,31 +15,56 @@ impl SelectProcessor { database: &Database, table_name: &String, row: &Row, - select: Vec, - mut output: HashMap, + select: Vec<&str>, ) -> HashMap { - for value in select { - let key = value.to_string(); - if (key.eq("*")) { - return row.clone(); // recursive call - } else if (key.contains(".")) { - let split: Vec = key - .split(".") - .map(|part| String::from_str(part).unwrap()) - .collect(); + let asd = SelectProcessor::parse_node(select); + println!("Debugging parsed selector {asd}"); + SelectProcessor::recursive_traverse_resolver(database, table_name, row, &asd) + } - if (split.len() == 2 && split[1].eq("*")) { - let relation_rows = - SelectProcessor::resolve_relation(database, table_name, row, &split[0]); - output.insert(split[0].clone(), json!(relation_rows)); + fn recursive_traverse_resolver( + database: &Database, + table_name: &String, + row: &Row, + selector: &Value, + ) -> HashMap { + let object = selector.as_object().unwrap(); + let mut output: HashMap = HashMap::new(); + for (key, value) in object.into_iter() { + if value.is_object() { + let relation_rows: Value = + SelectProcessor::resolve_relation(database, table_name, row, key); + + if relation_rows.is_array() { + let mut row_vec: Vec> = vec![]; + for row in relation_rows.as_array().unwrap() { + let asd = Row::from(row.as_object().unwrap()); + let parsed_row = SelectProcessor::recursive_traverse_resolver( + database, table_name, &(row.as_object().unwrap()), &value, + ); + row_vec.push(parsed_row); + } + output.insert(key.to_owned(), json!(row_vec)); + } else { + let parsed_row = SelectProcessor::recursive_traverse_resolver( + database, table_name, &row, &value, + ); + + output.insert(key.to_owned(), json!(parsed_row)); } - // recursive call } else { - let value = row.get(&key); - match value.is_some() { - true => output.insert(key, value.unwrap().to_owned()), - false => panic!("Failed to read key: {key} on table: {table_name}"), - }; + if value.as_str().unwrap() == "*" { + output = row.to_owned(); + } else { + let val = row.get(key); + if val.is_none() { + let pretty_json = serde_json::to_string_pretty(row).unwrap(); + panic!( + "Value missing on key {key} in table {table_name} - row: {pretty_json}" + ); + } + output.insert(key.to_owned(), json!(val.unwrap())); + } } } return output; @@ -66,7 +75,7 @@ impl SelectProcessor { table_name: &String, row: &Row, key: &String, - ) -> Vec { + ) -> Value { let table = database.get_table(table_name.clone()).unwrap(); println!("Attempting to find relation for '{key}' in table: '{table_name}'"); @@ -79,12 +88,12 @@ impl SelectProcessor { let relation = table.get_relation(&relation_name).unwrap(); let relation_variation = relation.variation.as_str(); return match relation_variation { - "one_to_one" => vec![OneToOne::from_value(value.to_owned()) + "one_to_one" => json!(OneToOne::from_value(value.to_owned()) .get(table_name.clone(), database) - .unwrap()], - "one_to_many" => OneToMany::from_value(value.to_owned()) + .unwrap()), + "one_to_many" => json!(OneToMany::from_value(value.to_owned()) .get(table_name.clone(), database) - .unwrap(), + .unwrap()), other => panic!("Unsupported relationship type: {other}"), }; } @@ -93,7 +102,7 @@ impl SelectProcessor { key.chars().all(|c| c.is_ascii_lowercase() || c == '_') } - pub fn recursive_parse_select(node: Vec<&str>) -> Value { + pub fn parse_node(node: Vec<&str>) -> Value { let mut output = Map::new(); for key in node { @@ -101,14 +110,15 @@ impl SelectProcessor { let next_key = key[..dot_index].to_string(); let next_node = key[dot_index + 1..].to_string(); - let mut nested_value = SelectProcessor::recursive_parse_select(vec![&next_node]); + let mut nested_value = SelectProcessor::parse_node(vec![&next_node]); if output.contains_key(&next_key) { - let deep_map = output.get_mut(&next_key).unwrap().as_object_mut().unwrap(); - deep_map.append(nested_value.as_object_mut().unwrap()); + let current_value = output.get_mut(&next_key).unwrap().as_object_mut().unwrap(); + current_value.append(nested_value.as_object_mut().unwrap()); } else { output.insert(next_key, nested_value); } + } else { output.insert(key.to_string(), json!(key)); } } diff --git a/src/tests/select_processor_test.rs b/src/tests/select_processor_test.rs index d1e4bd7..aff7441 100644 --- a/src/tests/select_processor_test.rs +++ b/src/tests/select_processor_test.rs @@ -10,7 +10,6 @@ mod select_processor_test { }; use assert_json_diff::assert_json_include; use serde_json::json; - use std::collections::HashMap; pub fn initialize() -> Database { let cat_relations = @@ -39,9 +38,8 @@ mod select_processor_test { let cat_table = database.get_table("Cats".to_string()).unwrap(); let cat_1 = cat_table.find_by_pk(1u64).unwrap(); - let select = vec!["id".to_string(), "name".to_string(), "foods.*".to_string()]; - let output = - SelectProcessor::selector(&database, &cat_table.name, cat_1, select, HashMap::new()); + let select = vec!["id", "name", "foods.*"]; + let output = SelectProcessor::selector(&database, &cat_table.name, cat_1, select); assert_json_include!( actual: &output, expected: @@ -59,9 +57,8 @@ mod select_processor_test { let cat_table = database.get_table("Cats".to_string()).unwrap(); let cat_1 = cat_table.find_by_pk(1u64).unwrap(); - let select = vec!["id".to_string(), "name".to_string(), "breed".to_string()]; - let output = - SelectProcessor::selector(&database, &cat_table.name, cat_1, select, HashMap::new()); + let select = vec!["id", "name", "breed"]; + let output = SelectProcessor::selector(&database, &cat_table.name, cat_1, select); assert_json_include!( actual: &output, expected: &row!["id" => 1, "name" =>"Ozzy", "breed" => "mixed"] @@ -73,9 +70,8 @@ mod select_processor_test { let cat_table = database.get_table("Cats".to_string()).unwrap(); let cat_1 = cat_table.find_by_pk(1u64).unwrap(); - let select = vec!["*".to_string()]; - let output = - SelectProcessor::selector(&database, &cat_table.name, cat_1, select, HashMap::new()); + let select = vec!["*"]; + let output = SelectProcessor::selector(&database, &cat_table.name, cat_1, select); assert_json_include!( actual: &output, @@ -91,22 +87,37 @@ mod select_processor_test { #[test] fn should_parse_node() { - let output = SelectProcessor::recursive_parse_select(vec!["id","food","relation.*","relation.food.*"]); - let json = serde_json::to_string_pretty(&output).unwrap(); - println!("{json}"); - assert_json_include!( - actual: &output, - expected: - &json!({ - "id": "id", - "food": "food", - "relation": { - "*": "*", - "food": { - "*": "*" - } - } - }) + let output = + SelectProcessor::parse_node(vec!["id", "food", "relation.*", "relation.food.*"]); + let json = serde_json::to_string_pretty(&output).unwrap(); + assert_json_include!( + actual: &output, + expected: &json!({ + "id": "id", + "food": "food", + "relation": { + "*": "*", + "food": { + "*": "*" + } + } + }) + ); + } + #[test] + fn should_parse_node_one_level() { + let output = SelectProcessor::parse_node(vec!["id", "name", "foods.*"]); + let json = serde_json::to_string_pretty(&output).unwrap(); + println!("{json}"); + assert_json_include!( + actual: &output, + expected: &json!({ + "id": "id", + "name":"name", + "foods": { + "*": "*", + } + }) ); } }