//! QueryBuilder //! //! The QueryBuilder creates sql queries from chained methods use std::any::Any; use std::collections::HashMap; use crate::drivers::DatabaseDriver; /// The position of the wildcard(s) /// for a `like` clause #[derive(Debug)] pub enum LikeWildcard { /// Wildcard before search term /// eg. `%foo` Before, /// Wildcard after the search term /// eg. `foo%` After, /// Wildcards surrounding the search term /// eg. `%foo%` Both, } /// The type of SQL join #[derive(Debug)] pub enum JoinType { Inner, Outer, Left, Right, LeftOuter, RightOuter, } /// The sort direction #[derive(Debug)] pub enum OrderDirection { /// Sort Ascending Asc, /// Sort Descending Desc, } #[derive(Debug)] enum QueryClauseType { AndGroupStart, GroupEnd, GroupStart, Like, OrGroupStart, Where, WhereIn, } #[derive(Debug)] struct QueryClause { clause_type: QueryClauseType, conjunction: String, string: String, } impl QueryClause { pub fn new(clause_type: QueryClauseType, conjunction: String, string: String) -> Self { QueryClause { clause_type, conjunction, string, } } } #[derive(Debug)] struct QueryState { select_string: String, from_string: String, set_string: String, order_string: String, group_string: String, // Keys for insert/update statement set_array_keys: Vec, // Order by clause order_array: HashMap, // Group by clause group_array: HashMap, // Values to apply to prepared statements values: Vec>, // Values to apply to where clauses in prepared statements where_values: Vec>, limit: Option, offset: Option, // Query components for complex selects query_map: Vec, // Query components for having clauses having_map: Vec, } impl Default for QueryState { fn default() -> Self { QueryState { select_string: String::from(""), from_string: String::from(""), set_string: String::from(""), order_string: String::from(""), group_string: String::from(""), set_array_keys: vec![], order_array: HashMap::new(), group_array: HashMap::new(), values: vec![], where_values: vec![], limit: None, offset: None, query_map: vec![], having_map: vec![], } } } impl QueryState { pub fn new() -> Self { QueryState::default() } } /// The struct representing a query builder #[derive(Default, Debug)] pub struct QueryBuilder { state: QueryState, driver: Option>, } impl QueryBuilder { /// Creates a new QueryBuilder instance pub fn new() -> Self { QueryBuilder { state: QueryState::new(), driver: None, } } // -------------------------------------------------------------------------- // ! Select Queries // -------------------------------------------------------------------------- /// Set the fields to select from the database as a string pub fn select(&mut self, fields: &str) -> &mut Self { unimplemented!(); } /// Set the fields to select from the database as a Vector pub fn select_vec(&mut self, fields: Vec<&str>) -> &mut Self { let fields = fields.join(","); self.select(&fields) } /// Adds the `distinct` keyword to a query pub fn distinct(&mut self) -> &mut Self { unimplemented!(); } /// Specify the database table to select from pub fn from(&mut self, table_name: &str) -> &mut Self { // @TODO properly escape the table name self.state.from_string = table_name.to_string(); self } // -------------------------------------------------------------------------- // ! 'Like' methods // -------------------------------------------------------------------------- /// Creates a `like` clause in the sql statement pub fn like(&mut self, field: &str, value: Box, position: LikeWildcard) -> &mut Self { unimplemented!(); } /// Generates an OR Like clause pub fn or_like( &mut self, field: &str, value: Box, position: LikeWildcard, ) -> &mut Self { unimplemented!(); } /// Generates a NOI Like clause pub fn not_like( &mut self, field: &str, value: Box, position: LikeWildcard, ) -> &mut Self { unimplemented!(); } /// Generates an OR NOT Like clause pub fn or_not_like( &mut self, field: &str, value: Box, position: LikeWildcard, ) -> &mut Self { unimplemented!(); } // -------------------------------------------------------------------------- // ! Having methods // -------------------------------------------------------------------------- /// Add a `having` clause to the query pub fn having(&mut self, key: &str, value: Box) -> &mut Self { unimplemented!(); } /// Add a `having` clause to the query, prefixed with an `or` pub fn or_having(&mut self, key: &str, value: Box) -> &mut Self { unimplemented!(); } // -------------------------------------------------------------------------- // ! 'Where' methods // -------------------------------------------------------------------------- /// Specify a condition for the `where` clause of the query pub fn r#where(&mut self, key: &str, op: &str, value: Box) -> &mut Self { // @TODO actually implement setting the keys for the where self.state.where_values.push(value); self } // Specify a condition for a `where` clause where a column has a value pub fn where_eq(&mut self, key: &str, value: Box) -> &mut Self { self.r#where(key, "=", value) } /// Specify a condition for the `where` clause of the query, prefixed with `or` pub fn or_where(&mut self, key: &str, value: Box) -> &mut Self { unimplemented!(); } /// Specify a `where in` clause for the query pub fn where_in(&mut self, key: &str, value: Vec>) -> &mut Self { unimplemented!(); } /// Specify a `where in` clause for the query, prefixed with `or` pub fn or_where_in(&mut self, key: &str, value: Vec>) -> &mut Self { unimplemented!(); } /// Specify a `where not in` clause for the query pub fn where_not_in(&mut self, key: &str, value: Vec>) -> &mut Self { unimplemented!(); } /// Specify a `where not in` clause for the query, prefixed with `or` pub fn or_where_not_in(&mut self, key: &str, value: Vec>) -> &mut Self { unimplemented!(); } // -------------------------------------------------------------------------- // ! Other Query Modifier methods // -------------------------------------------------------------------------- /// Set a key and value for an insert or update query pub fn set(&mut self, key: &str, value: Box) -> &mut Self { // @TODO figure a way to make this easier to use self.state.set_array_keys.push(key.to_string()); self.state.values.push(value); self } /// Set a map of data for an insert or update query pub fn set_map(&mut self, data: HashMap>) -> &mut Self { for (key, value) in data { self.set(&key, value); } self } /// Add a table join to the query pub fn join(&mut self, table: &str, condition: &str, join_type: JoinType) -> &mut Self { unimplemented!(); } /// Add a group by clause to the query pub fn group_by(&mut self, field: &str) -> &mut Self { unimplemented!(); } /// Add an order by clause to the query pub fn order_by(&mut self, field: &str, direction: OrderDirection) -> &mut Self { unimplemented!(); } /// Add a limit to the query pub fn limit(&mut self, limit: usize) -> &mut Self { self.state.limit = Some(limit); self } /// Add an offset to the query pub fn offset(&mut self, offset: usize) -> &mut Self { self.state.offset = Some(offset); self } // -------------------------------------------------------------------------- // ! Query Grouping Methods // -------------------------------------------------------------------------- /// Start a logical grouping in the current query pub fn group_start(&mut self) -> &mut Self { unimplemented!(); } /// Start a logical grouping, prefixed with `not` pub fn not_group_start(&mut self) -> &mut Self { unimplemented!(); } /// Start a logical grouping, prefixed with `or` pub fn or_group_start(&mut self) -> &mut Self { unimplemented!(); } /// Start a logical grouping, prefixed with `or not` pub fn or_not_group_start(&mut self) -> &mut Self { unimplemented!(); } /// End the current logical grouping pub fn group_end(&mut self) -> &mut Self { unimplemented!(); } // -------------------------------------------------------------------------- // ! Query execution methods // -------------------------------------------------------------------------- /// Execute the built query pub fn get(self) -> Box { unimplemented!(); } /// Count all the rows in the specified database table pub fn count_all(self, table: &str) -> usize { unimplemented!(); } /// Execute the generated insert query pub fn insert(&mut self, table: &str) { // @TODO determine query result type unimplemented!(); } /// Execute the generated update query pub fn update(&mut self, table: &str) { // @TODO determine query result type unimplemented!(); } /// Execute the generated delete query pub fn delete(&mut self, table: &str) { unimplemented!(); } // -------------------------------------------------------------------------- // ! SQL Returning Methods // -------------------------------------------------------------------------- /// Get the generated SQL for a select query pub fn get_compiled_select(self) -> String { unimplemented!(); } /// Get the generated SQL for an insert query pub fn get_compiled_insert(self) -> String { unimplemented!(); } /// Get the generated SQL for an update query pub fn get_compiled_update(self) -> String { unimplemented!(); } /// Get the generated SQL for a delete query pub fn get_compiled_delete(self) -> String { unimplemented!(); } // -------------------------------------------------------------------------- // ! Miscellaneous Methods // -------------------------------------------------------------------------- /// Get a new instance of the query builder pub fn reset_query(&mut self) -> Self { QueryBuilder::new() } } #[cfg(test)] mod tests { use super::*; #[test] fn set_key_value() { let mut qb = QueryBuilder::new(); qb.set("foo", Box::new("bar")); assert_eq!(qb.state.set_array_keys[0], "foo"); assert!(qb.state.values[0].is::<&str>()); // @TODO find a way to make this kind of operation much more ergonomic assert_eq!(*qb.state.values[0].downcast_ref::<&str>().unwrap(), "bar"); } #[test] fn set_hashmap() { let mut qb = QueryBuilder::new(); let mut authors: HashMap> = HashMap::new(); authors.insert( String::from("Chinua Achebe"), Box::new(String::from("Nigeria")), ); authors.insert( String::from("Rabindranath Tagore"), Box::new(String::from("India")), ); authors.insert(String::from("Anita Nair"), Box::new(String::from("India"))); qb.set_map(authors); // assert_eq!(qb.state.set_array_keys[0], "Chinua Achebe"); assert_eq!(qb.state.set_array_keys.len(), 3); assert_eq!(qb.state.values.len(), 3); } }