diff --git a/src/database/mod.rs b/src/database/mod.rs index a91ab52..700d5d6 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -8,8 +8,8 @@ pub mod index; pub mod relation; pub mod row; pub mod table; -pub mod select_processor; -pub mod query_builder; +pub mod query; + #[derive(Serialize, Deserialize)] pub struct Database { diff --git a/src/database/query/mod.rs b/src/database/query/mod.rs new file mode 100644 index 0000000..ee26020 --- /dev/null +++ b/src/database/query/mod.rs @@ -0,0 +1,3 @@ +pub mod select_processor; +pub mod query_builder; +pub mod select_parser; \ No newline at end of file diff --git a/src/database/query_builder.rs b/src/database/query/query_builder.rs similarity index 100% rename from src/database/query_builder.rs rename to src/database/query/query_builder.rs diff --git a/src/database/query/select_parser.rs b/src/database/query/select_parser.rs new file mode 100644 index 0000000..3600af1 --- /dev/null +++ b/src/database/query/select_parser.rs @@ -0,0 +1,28 @@ +use serde_json::{Map, Value, json}; + +pub struct SelectParser {} +impl SelectParser { + pub fn parse_selector_recursive(node: Vec<&str>) -> Value { + let mut output = Map::new(); + + for key in node { + if let Some(dot_index) = key.find('.') { + let next_key = key[..dot_index].to_string(); + let next_node = key[dot_index + 1..].to_string(); + + let mut nested_value = SelectParser::parse_selector_recursive(vec![&next_node]); + + if output.contains_key(&next_key) { + 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)); + } + } + + json!(output) + } +} \ No newline at end of file diff --git a/src/database/select_processor.rs b/src/database/query/select_processor.rs similarity index 76% rename from src/database/select_processor.rs rename to src/database/query/select_processor.rs index a31dc69..696a90a 100644 --- a/src/database/select_processor.rs +++ b/src/database/query/select_processor.rs @@ -1,10 +1,7 @@ -use std::collections::HashMap; - use serde_json::{json, Map, Value}; -use crate::database::relation::{OneToMany, OneToOne}; +use crate::database::{relation::{OneToMany, OneToOne}, Database, row::Row, query::select_parser::SelectParser}; -use super::{row::Row, Database}; pub struct SelectProcessor {} @@ -17,7 +14,7 @@ impl SelectProcessor { row: &Row, select: Vec<&str>, ) -> Map { - let asd = SelectProcessor::parse_node(select); + let asd = SelectParser::parse_selector_recursive(select); println!("Debugging parsed selector {asd}"); SelectProcessor::recursive_traverse_resolver(database, table_name, row, &asd) } @@ -46,8 +43,9 @@ impl SelectProcessor { } output.insert(key.to_owned(), json!(row_vec)); } else { + let foreign_row = relation_rows.as_object().unwrap(); let parsed_row = SelectProcessor::recursive_traverse_resolver( - database, table_name, &row, &value, + database, table_name, foreign_row, &value, ); output.insert(key.to_owned(), json!(parsed_row)); @@ -101,28 +99,4 @@ impl SelectProcessor { fn is_valid_key(key: &str) -> bool { key.chars().all(|c| c.is_ascii_lowercase() || c == '_') } - - pub fn parse_node(node: Vec<&str>) -> Value { - let mut output = Map::new(); - - for key in node { - if let Some(dot_index) = key.find('.') { - let next_key = key[..dot_index].to_string(); - let next_node = key[dot_index + 1..].to_string(); - - let mut nested_value = SelectProcessor::parse_node(vec![&next_node]); - - if output.contains_key(&next_key) { - 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)); - } - } - - json!(output) - } } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index c64a5bc..ebe5620 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -2,4 +2,5 @@ mod database_test; mod index_test; mod relation_test; mod table_test; -mod select_processor_test; \ No newline at end of file +mod select_processor_test; +mod select_parser_test; \ No newline at end of file diff --git a/src/tests/select_parser_test.rs b/src/tests/select_parser_test.rs new file mode 100644 index 0000000..70c8cd6 --- /dev/null +++ b/src/tests/select_parser_test.rs @@ -0,0 +1,43 @@ +#[cfg(test)] +mod select_parser_test { + use crate::database::{ + query::select_parser::SelectParser + }; + use assert_json_diff::assert_json_include; + use serde_json::json; + + #[test] + fn should_parse_selector_recursive() { + let output = + SelectParser::parse_selector_recursive(vec!["id", "food", "relation.*", "relation.food.*"]); + assert_json_include!( + actual: &output, + expected: &json!({ + "id": "id", + "food": "food", + "relation": { + "*": "*", + "food": { + "*": "*" + } + } + }) + ); + } + #[test] + fn should_parse_selector_recursive_one_level() { + let output = SelectParser::parse_selector_recursive(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": { + "*": "*", + } + }) + ); + } +} diff --git a/src/tests/select_processor_test.rs b/src/tests/select_processor_test.rs index aff7441..c6e2ebe 100644 --- a/src/tests/select_processor_test.rs +++ b/src/tests/select_processor_test.rs @@ -3,22 +3,23 @@ #[cfg(test)] mod select_processor_test { use crate::database::{ - relation::{OneToMany, Relation}, - select_processor::SelectProcessor, + relation::{OneToMany, Relation, OneToOne}, table::Table, - Database, + Database, query::select_processor::SelectProcessor, }; use assert_json_diff::assert_json_include; use serde_json::json; pub fn initialize() -> Database { let cat_relations = - hashmap!["cat_food" => Relation::new("Food".to_string(), "one_to_many".to_string())]; + hashmap!["cat_food" => Relation::new("Food".to_string(), "one_to_many".to_string()), + "cat_age_group" => Relation::new("AgeGroup".to_string(), "one_to_one".to_string())]; let cat_table: Table = Table::new("Cats", "id", None, Some(cat_relations)); let food_table: Table = Table::new("Food", "id", None, None); + let age_table: Table = Table::new("AgeGroup", "id", None, None); - let mut database: Database = Database::new(vec![cat_table, food_table]); + let mut database: Database = Database::new(vec![cat_table, food_table,age_table]); _ = database .get_table_mut("Food".to_string()) @@ -28,7 +29,16 @@ mod select_processor_test { .get_table_mut("Food".to_string()) .unwrap() .insert_row(row!["id" => 12, "name" => "Dry Feed"]); - _ = database.get_table_mut("Cats".to_string()).unwrap().insert_row(row!["id" => 1, "name" => "Ozzy", "breed" => "mixed", "foods" => OneToMany::new(vec![123u64,12u64], "cat_food".to_string())]); + _ = database + .get_table_mut("AgeGroup".to_string()) + .unwrap() + .insert_row(row!["id" => 99, "age" => "Young"]); + _ = database.get_table_mut("Cats".to_string()).unwrap().insert_row(row![ + "id" => 1, + "name" => "Ozzy", + "breed" => "mixed", + "foods" => OneToMany::new(vec![123u64,12u64], "cat_food".to_string()), + "group" => OneToOne::new(99,"cat_age_group".to_string())]); return database; } @@ -51,6 +61,22 @@ mod select_processor_test { ]] ); } +#[test] + fn should_succed_select_processor_one_level_resolved_onetoone() { + let database = initialize(); + + let cat_table = database.get_table("Cats".to_string()).unwrap(); + let cat_1 = cat_table.find_by_pk(1u64).unwrap(); + let select = vec!["id", "name", "group.age"]; + let output = SelectProcessor::selector(&database, &cat_table.name, cat_1, select); + assert_json_include!( + actual: &output, + expected: + &row!["id" => 1, "name" =>"Ozzy", "group"=> row![ + "age" => "Young" + ]] + ); + } #[test] fn should_succed_select_processor_zero_level_resolved() { let database = initialize(); @@ -84,40 +110,4 @@ mod select_processor_test { }) ); } - - #[test] - fn should_parse_node() { - 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": { - "*": "*", - } - }) - ); - } }