//! 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() -> QueryState { 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 pub fn select(mut self, fields: &str) -> Self { unimplemented!(); } /// Adds the `distinct` keyword to a query pub fn distinct(mut self) -> Self { unimplemented!(); } /// Specify the database table to select from pub fn from(mut self, table_name: &str) -> 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) -> Self { unimplemented!(); } /// Generates an OR Like clause pub fn or_like(mut self, field: &str, value: Box, position: LikeWildcard) -> Self { unimplemented!(); } /// Generates a NOI Like clause pub fn not_like(mut self, field: &str, value: Box, position: LikeWildcard) -> Self { unimplemented!(); } /// Generates an OR NOT Like clause pub fn or_not_like(mut self, field: &str, value: Box, position: LikeWildcard) -> Self { unimplemented!(); } // -------------------------------------------------------------------------- // ! Having methods // -------------------------------------------------------------------------- /// Add a `having` clause to the query pub fn having(mut self, key:&str, value: Box) -> Self { unimplemented!(); } /// Add a `having` clause to the query, prefixed with an `or` pub fn or_having(mut self, key:&str, value: Box) -> Self { unimplemented!(); } // -------------------------------------------------------------------------- // ! 'Where' methods // -------------------------------------------------------------------------- /// Specify a condition for the `where` clause of the query pub fn r#where(mut self, key: &str, value: Box) -> Self { // @TODO actually implement setting the keys for the where self.state.where_values.push(value); self } /// Specify a condition for the `where` clause of the query, prefixed with `or` pub fn or_where(mut self, key: &str, value: Box) -> Self { unimplemented!(); } /// Specify a `where in` clause for the query pub fn where_in(mut self, key: &str, value: Vec>) -> Self { unimplemented!(); } /// Specify a `where in` clause for the query, prefixed with `or` pub fn or_where_in(mut self, key: &str, value: Vec>) -> Self { unimplemented!(); } /// Specify a `where not in` clause for the query pub fn where_not_in(mut self, key: &str, value: Vec>) -> 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>) -> 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) -> 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>) -> Self { for (key, value) in data { self = self.set(&key, value); } self } /// Add a table join to the query pub fn join(mut self, table: &str, condition: &str, join_type: JoinType) -> Self { unimplemented!(); } /// Add a group by clause to the query pub fn group_by(mut self, field: &str) -> Self { unimplemented!(); } /// Add an order by clause to the query pub fn order_by(mut self, field: &str, direction: OrderDirection) -> Self { unimplemented!(); } /// Add a limit to the query pub fn limit(mut self, limit: u32) -> Self { self.state.limit = Some(limit); self } /// Add an offset to the query pub fn offset(mut self, offset: u32) -> Self { self.state.offset = Some(offset); self } // -------------------------------------------------------------------------- // ! Query Grouping Methods // -------------------------------------------------------------------------- /// Start a logical grouping in the current query pub fn group_start(mut self) -> Self { unimplemented!(); } /// Start a logical grouping, prefixed with `not` pub fn not_group_start(mut self) -> Self { unimplemented!(); } /// Start a logical grouping, prefixed with `or` pub fn or_group_start(mut self) -> Self { unimplemented!(); } /// Start a logical grouping, prefixed with `or not` pub fn or_not_group_start(mut self) -> Self { unimplemented!(); } /// End the current logical grouping pub fn group_end(mut self) -> 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) -> u32 { 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 qb = QueryBuilder::new() .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 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"))); let qb = 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); } }