diff --git a/src/lib.rs b/src/lib.rs index b2c4c69..ced0e93 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,12 @@ //! # StringQB //! //! A query builder using mostly strings, with methods following common SQL syntax +//! +//! ``` +//! use stringqb::prelude::*; +//! +//! +//! ``` #![warn(missing_docs)] // Temporarily silence unused variables and uncalled code warnings // @TODO remove when most of the code is implemented @@ -25,12 +31,15 @@ pub mod prelude { pub use crate::query_builder::QueryBuilder; #[cfg(feature = "postgres")] + /// Postgres Driver pub use crate::drivers::postgres::PostgresDriver; #[cfg(feature = "sqlite")] + /// SQLite Driver pub use crate::drivers::sqlite::SQLiteDriver; #[cfg(feature = "mysql")] + /// MySQL Driver pub use crate::drivers::mysql::MySQLDriver; // MSSQL is missing on purpose, as it is not a real priority to actually implement diff --git a/src/query_builder.rs b/src/query_builder.rs index 181cafd..6d3c50f 100644 --- a/src/query_builder.rs +++ b/src/query_builder.rs @@ -1,8 +1,6 @@ //! Query Builder //! //! The QueryBuilder creates sql queries from chained methods -mod query_state; - use std::any::Any; use std::collections::HashMap; @@ -10,7 +8,6 @@ use crate::drivers::{DatabaseDriver, DefaultDriver}; use crate::enums::*; use crate::fns::split_map_join; -use query_state::QueryState; use regex::Regex; #[derive(Debug)] @@ -25,7 +22,7 @@ enum QueryType { #[derive(Debug)] pub struct QueryBuilder { /// The struct to store the query builder info - pub state: QueryState, + state: QueryState, driver: Box, } @@ -158,15 +155,15 @@ impl QueryBuilder { /// # use stringqb::prelude::*; /// # let mut qb = stringqb::query_builder::QueryBuilder::default(); /// // Search for a value that ends with "foo" - /// qb.like("field", Box::new("foo"), LikeWildcard::Before); + /// qb.like("field", String::from("foo"), LikeWildcard::Before); /// /// // Search for a value that starts with "foo" - /// qb.like("field", Box::new("foo"), LikeWildcard::After); + /// qb.like("field", String::from("foo"), LikeWildcard::After); /// /// // Search for a value that has "foo" in it - /// qb.like("field", Box::new("foo"), LikeWildcard::Both); + /// qb.like("field", String::from("foo"), LikeWildcard::Both); /// ``` - pub fn like(&mut self, field: &str, value: Box, position: LikeWildcard) -> &mut Self { + pub fn like(&mut self, field: &str, value: (impl Any), position: LikeWildcard) -> &mut Self { self._like(field, value, position, "LIKE", "AND") } @@ -174,7 +171,7 @@ impl QueryBuilder { pub fn or_like( &mut self, field: &str, - value: Box, + value: (impl Any), position: LikeWildcard, ) -> &mut Self { self._like(field, value, position, "LIKE", "OR") @@ -184,7 +181,7 @@ impl QueryBuilder { pub fn not_like( &mut self, field: &str, - value: Box, + value: (impl Any), position: LikeWildcard, ) -> &mut Self { self._like(field, value, position, "NOT LIKE", "AND") @@ -194,7 +191,7 @@ impl QueryBuilder { pub fn or_not_like( &mut self, field: &str, - value: Box, + value: (impl Any), position: LikeWildcard, ) -> &mut Self { self._like(field, value, position, "NOT LIKE", "OR") @@ -215,12 +212,12 @@ impl QueryBuilder { /// // Other operators can be used with a separating space /// qb.having("clues >=", vec![Box::new(4)]); /// ``` - pub fn having(&mut self, key: &str, value: Vec>) -> &mut Self { + pub fn having(&mut self, key: &str, value: Vec<(impl Any)>) -> &mut Self { self._having(key, value, "AND") } /// Add a `having` clause to the query, prefixed with an `or` - pub fn or_having(&mut self, key: &str, value: Vec>) -> &mut Self { + pub fn or_having(&mut self, key: &str, value: Vec<(impl Any)>) -> &mut Self { self._having(key, value, "OR") } @@ -229,7 +226,7 @@ impl QueryBuilder { // -------------------------------------------------------------------------- /// Alias method for `r#where`. - pub fn filter(&mut self, key: &str, value: Box) -> &mut Self { + pub fn filter(&mut self, key: &str, value: (impl Any)) -> &mut Self { self.r#where(key, value) } @@ -246,32 +243,32 @@ impl QueryBuilder { /// // Other operators can be used with a separating space /// qb.r#where("key >", Box::new(4)); /// ``` - pub fn r#where(&mut self, key: &str, value: Box) -> &mut Self { + pub fn r#where(&mut self, key: &str, value: (impl Any)) -> &mut Self { self._where_string(key, value, "AND") } /// 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 { + pub fn or_where(&mut self, key: &str, value: (impl Any)) -> &mut Self { self._where_string(key, value, "OR") } /// Specify a `where in` clause for the query - pub fn where_in(&mut self, key: &str, values: Vec>) -> &mut Self { + pub fn where_in(&mut self, key: &str, values: Vec<(impl Any)>) -> &mut Self { self._where_in(key, values, "IN", "AND") } /// Specify a `where in` clause for the query, prefixed with `or` - pub fn or_where_in(&mut self, key: &str, values: Vec>) -> &mut Self { + pub fn or_where_in(&mut self, key: &str, values: Vec<(impl Any)>) -> &mut Self { self._where_in(key, values, "IN", "OR") } /// Specify a `where not in` clause for the query - pub fn where_not_in(&mut self, key: &str, values: Vec>) -> &mut Self { + pub fn where_not_in(&mut self, key: &str, values: Vec<(impl Any)>) -> &mut Self { self._where_in(key, values, "NOT IN", "AND") } /// Specify a `where not in` clause for the query, prefixed with `or` - pub fn or_where_not_in(&mut self, key: &str, values: Vec>) -> &mut Self { + pub fn or_where_not_in(&mut self, key: &str, values: Vec<(impl Any)>) -> &mut Self { self._where_in(key, values, "NOT IN", "OR") } @@ -280,16 +277,16 @@ impl QueryBuilder { // -------------------------------------------------------------------------- /// Set a key and value for an insert or update query - pub fn set(&mut self, key: &str, value: Box) -> &mut Self { + pub fn set(&mut self, key: &str, value: (impl Any)) -> &mut Self { // @TODO figure a way to make this easier to use let key = self.driver.quote_identifier(key); - self.state.append_set_array_keys(&key).append_values(value); + self.state.append_set_array_keys(&key).append_values(Box::new(value)); self } /// Set a map of data for an insert or update query - pub fn set_map(&mut self, data: HashMap>) -> &mut Self { + pub fn set_map(&mut self, data: HashMap) -> &mut Self { for (key, value) in data { self.set(&key, value); } @@ -520,7 +517,7 @@ impl QueryBuilder { fn _like( &mut self, field: &str, - value: Box, + value: (impl Any), position: LikeWildcard, like: &str, conj: &str, @@ -529,13 +526,14 @@ impl QueryBuilder { let like = format!("{} {} ?", field, like); - let string_val = value.downcast::().unwrap(); + let value: Box = Box::new(value); + let string_val = value.downcast_ref::().unwrap(); // @TODO Properly parse types of `value` for string formatting let value = match position { - LikeWildcard::Before => format!("%{}", *string_val), - LikeWildcard::After => format!("{}%s", *string_val), - LikeWildcard::Both => format!("%{}%", *string_val), + LikeWildcard::Before => format!("%{}", string_val), + LikeWildcard::After => format!("{}%s", string_val), + LikeWildcard::Both => format!("%{}%", string_val), }; self.state @@ -545,7 +543,7 @@ impl QueryBuilder { self } - fn _having(&mut self, key: &str, values: Vec>, conj: &str) -> &mut Self { + fn _having(&mut self, key: &str, values: Vec<(impl Any)>, conj: &str) -> &mut Self { let keys = self._where(key, values); for k in keys { @@ -573,9 +571,9 @@ impl QueryBuilder { self } - fn _where(&mut self, key: &str, values: Vec>) -> Vec { + fn _where(&mut self, key: &str, values: Vec<(impl Any)>) -> Vec { for x in values { - self.state.append_where_values(x); + self.state.append_where_values(Box::new(x)); } vec![String::from(key)] @@ -584,7 +582,7 @@ impl QueryBuilder { fn _where_in( &mut self, key: &str, - values: Vec>, + values: Vec<(impl Any)>, in_str: &str, conj: &str, ) -> &mut Self { @@ -592,7 +590,7 @@ impl QueryBuilder { let placeholders = vec!["?"; values.len()]; for value in values { - self.state.append_where_values(value); + self.state.append_where_values(Box::new(value)); } let str = format!("{} {} ({}) ", key, in_str, placeholders.join(",")); @@ -603,7 +601,7 @@ impl QueryBuilder { self } - fn _where_string(&mut self, key: &str, value: Box, conj: &str) -> &mut Self { + fn _where_string(&mut self, key: &str, value: impl Any, conj: &str) -> &mut Self { let keys = self._where(key, vec![value]); for k in keys { @@ -704,6 +702,233 @@ impl QueryBuilder { } } +#[derive(Debug)] +struct QueryClause { + clause_type: QueryClauseType, + conjunction: String, + string: String, +} + +impl QueryClause { + pub fn new(clause_type: QueryClauseType, conjunction: &str, string: &str) -> Self { + QueryClause { + clause_type, + conjunction: conjunction.to_string(), + string: string.to_string(), + } + } + + pub fn to_string(&self) -> String { + format!("{}{}", self.conjunction, self.string) + } +} + +#[derive(Debug)] +struct QueryState { + pub explain: bool, + + 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_map: HashMap, + + // Group by clause + group_array: Vec, + + // Values to apply to prepared statements + values: Vec>, + + // Values to apply to where clauses in prepared statements + where_values: Vec>, + + pub limit: Option, + + pub 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 { + explain: false, + + 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_map: HashMap::new(), + group_array: vec![], + values: vec![], + where_values: vec![], + + limit: None, + offset: None, + + query_map: vec![], + having_map: vec![], + } + } +} + +impl QueryState { + pub fn new() -> Self { + QueryState::default() + } + + pub fn append_select_string(&mut self, s: &str) -> &mut Self { + self.select_string += s; + + self + } + + pub fn prepend_select_string(&mut self, s: &str) -> &mut Self { + self.select_string = String::from(s) + &self.select_string; + + self + } + + pub fn append_group_array(&mut self, field: &str) -> &mut Self { + self.group_array.push(String::from(field)); + + self + } + + pub fn append_having_map(&mut self, conj: &str, s: &str) -> &mut Self { + let conj = if self.having_map.len() == 0 { + String::from(" HAVING ") + } else { + format!(" {} ", conj) + }; + + self.having_map + .push(QueryClause::new(QueryClauseType::Having, &conj, s)); + + self + } + + pub fn append_order_map(&mut self, key: &str, dir: &str) -> &mut Self { + self.order_map.insert(String::from(key), String::from(dir)); + + self + } + + pub fn append_set_array_keys(&mut self, key: &str) -> &mut Self { + self.set_array_keys.push(key.to_string()); + + self + } + + pub fn append_values(&mut self, val: Box) -> &mut Self { + self.values.push(val); + + self + } + + pub fn append_where_values(&mut self, val: Box) -> &mut Self { + self.where_values.push(val); + + self + } + + pub fn append_query_map( + &mut self, + clause_type: QueryClauseType, + conj: &str, + s: &str, + ) -> &mut Self { + let conj = if self.query_map.len() == 0 { + " WHERE " + } else { + conj + }; + + self.query_map.push(QueryClause::new(clause_type, conj, s)); + + self + } + + pub fn get_from_string(&self) -> &str { + &self.from_string + } + + pub fn get_group_array(&self) -> &Vec { + &self.group_array + } + + pub fn get_group_string(&self) -> &str { + &self.group_string + } + + pub fn get_having_map(&self) -> &Vec { + &self.having_map + } + + pub fn get_query_map(&self) -> &Vec { + &self.query_map + } + + pub fn get_select_string(&self) -> &str { + &self.select_string + } + + pub fn get_set_array_keys(&self) -> &Vec { + &self.set_array_keys + } + + pub fn get_set_string(&self) -> &str { + &self.set_string + } + + pub fn get_order_map(&self) -> &HashMap { + &self.order_map + } + + pub fn get_order_string(&self) -> &str { + &self.order_string + } + + pub fn get_values(&self) -> &Vec> { + &self.values + } + + pub fn get_where_values(&self) -> &Vec> { + &self.where_values + } + + pub fn set_from_string(&mut self, s: &str) -> &mut Self { + self.from_string = String::from(s); + + self + } + + pub fn set_group_string(&mut self, s: &str) -> &mut Self { + self.group_string = String::from(s); + + self + } + + pub fn set_order_string(&mut self, order_string: &str) -> &mut Self { + self.order_string = String::from(order_string); + + self + } +} + #[cfg(test)] mod tests { use super::*; @@ -712,7 +937,7 @@ mod tests { fn set_key_value() { let mut qb = QueryBuilder::default(); - qb.set("foo", Box::new("bar")); + qb.set("foo", "bar"); assert_eq!(qb.state.get_set_array_keys()[0], "\"foo\""); assert!(qb.state.get_values()[0].is::<&str>()); @@ -728,16 +953,16 @@ mod tests { fn set_hashmap() { let mut qb = QueryBuilder::default(); - let mut authors: HashMap> = HashMap::new(); + let mut authors = HashMap::new(); authors.insert( String::from("Chinua Achebe"), - Box::new(String::from("Nigeria")), + String::from("Nigeria"), ); authors.insert( String::from("Rabindranath Tagore"), - Box::new(String::from("India")), + String::from("India"), ); - authors.insert(String::from("Anita Nair"), Box::new(String::from("India"))); + authors.insert(String::from("Anita Nair"), String::from("India")); qb.set_map(authors); @@ -753,12 +978,12 @@ mod tests { qb.from("test").where_in( "foo", vec![ - Box::new(0), - Box::new(1), - Box::new(2), - Box::new(3), - Box::new(4), - Box::new(5), + 0, + 1, + 2, + 3, + 4, + 5, ], ); diff --git a/src/query_builder/query_state.rs b/src/query_builder/query_state.rs deleted file mode 100644 index d8e1f21..0000000 --- a/src/query_builder/query_state.rs +++ /dev/null @@ -1,228 +0,0 @@ -use super::*; - -#[derive(Debug)] -pub struct QueryClause { - pub clause_type: QueryClauseType, - conjunction: String, - string: String, -} - -impl QueryClause { - pub fn new(clause_type: QueryClauseType, conjunction: &str, string: &str) -> Self { - QueryClause { - clause_type, - conjunction: conjunction.to_string(), - string: string.to_string(), - } - } - - pub fn to_string(&self) -> String { - format!("{}{}", self.conjunction, self.string) - } -} - -#[derive(Debug)] -pub struct QueryState { - pub explain: bool, - - 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_map: HashMap, - - // Group by clause - group_array: Vec, - - // Values to apply to prepared statements - values: Vec>, - - // Values to apply to where clauses in prepared statements - where_values: Vec>, - - pub limit: Option, - - pub 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 { - explain: false, - - 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_map: HashMap::new(), - group_array: vec![], - values: vec![], - where_values: vec![], - - limit: None, - offset: None, - - query_map: vec![], - having_map: vec![], - } - } -} - -impl QueryState { - pub fn new() -> Self { - QueryState::default() - } - - pub fn append_select_string(&mut self, s: &str) -> &mut Self { - self.select_string += s; - - self - } - - pub fn prepend_select_string(&mut self, s: &str) -> &mut Self { - self.select_string = String::from(s) + &self.select_string; - - self - } - - pub fn append_group_array(&mut self, field: &str) -> &mut Self { - self.group_array.push(String::from(field)); - - self - } - - pub fn append_having_map(&mut self, conj: &str, s: &str) -> &mut Self { - let conj = if self.having_map.len() == 0 { - String::from(" HAVING ") - } else { - format!(" {} ", conj) - }; - - self.having_map - .push(QueryClause::new(QueryClauseType::Having, &conj, s)); - - self - } - - pub fn append_order_map(&mut self, key: &str, dir: &str) -> &mut Self { - self.order_map.insert(String::from(key), String::from(dir)); - - self - } - - pub fn append_set_array_keys(&mut self, key: &str) -> &mut Self { - self.set_array_keys.push(key.to_string()); - - self - } - - pub fn append_values(&mut self, val: Box) -> &mut Self { - self.values.push(val); - - self - } - - pub fn append_where_values(&mut self, val: Box) -> &mut Self { - self.where_values.push(val); - - self - } - - pub fn append_query_map( - &mut self, - clause_type: QueryClauseType, - conj: &str, - s: &str, - ) -> &mut Self { - let conj = if self.query_map.len() == 0 { - " WHERE " - } else { - conj - }; - - self.query_map.push(QueryClause::new(clause_type, conj, s)); - - self - } - - pub fn get_from_string(&self) -> &str { - &self.from_string - } - - pub fn get_group_array(&self) -> &Vec { - &self.group_array - } - - pub fn get_group_string(&self) -> &str { - &self.group_string - } - - pub fn get_having_map(&self) -> &Vec { - &self.having_map - } - - pub fn get_query_map(&self) -> &Vec { - &self.query_map - } - - pub fn get_select_string(&self) -> &str { - &self.select_string - } - - pub fn get_set_array_keys(&self) -> &Vec { - &self.set_array_keys - } - - pub fn get_set_string(&self) -> &str { - &self.set_string - } - - pub fn get_order_map(&self) -> &HashMap { - &self.order_map - } - - pub fn get_order_string(&self) -> &str { - &self.order_string - } - - pub fn get_values(&self) -> &Vec> { - &self.values - } - - pub fn get_where_values(&self) -> &Vec> { - &self.where_values - } - - pub fn set_from_string(&mut self, s: &str) -> &mut Self { - self.from_string = String::from(s); - - self - } - - pub fn set_group_string(&mut self, s: &str) -> &mut Self { - self.group_string = String::from(s); - - self - } - - pub fn set_order_string(&mut self, order_string: &str) -> &mut Self { - self.order_string = String::from(order_string); - - self - } -}