Fewer boxes!

This commit is contained in:
Timothy Warren 2019-04-17 11:33:25 -04:00
parent f34a403584
commit 2aa7efa670
3 changed files with 279 additions and 273 deletions

View File

@ -1,6 +1,12 @@
//! # StringQB //! # StringQB
//! //!
//! A query builder using mostly strings, with methods following common SQL syntax //! A query builder using mostly strings, with methods following common SQL syntax
//!
//! ```
//! use stringqb::prelude::*;
//!
//!
//! ```
#![warn(missing_docs)] #![warn(missing_docs)]
// Temporarily silence unused variables and uncalled code warnings // Temporarily silence unused variables and uncalled code warnings
// @TODO remove when most of the code is implemented // @TODO remove when most of the code is implemented
@ -25,12 +31,15 @@ pub mod prelude {
pub use crate::query_builder::QueryBuilder; pub use crate::query_builder::QueryBuilder;
#[cfg(feature = "postgres")] #[cfg(feature = "postgres")]
/// Postgres Driver
pub use crate::drivers::postgres::PostgresDriver; pub use crate::drivers::postgres::PostgresDriver;
#[cfg(feature = "sqlite")] #[cfg(feature = "sqlite")]
/// SQLite Driver
pub use crate::drivers::sqlite::SQLiteDriver; pub use crate::drivers::sqlite::SQLiteDriver;
#[cfg(feature = "mysql")] #[cfg(feature = "mysql")]
/// MySQL Driver
pub use crate::drivers::mysql::MySQLDriver; pub use crate::drivers::mysql::MySQLDriver;
// MSSQL is missing on purpose, as it is not a real priority to actually implement // MSSQL is missing on purpose, as it is not a real priority to actually implement

View File

@ -1,8 +1,6 @@
//! Query Builder //! Query Builder
//! //!
//! The QueryBuilder creates sql queries from chained methods //! The QueryBuilder creates sql queries from chained methods
mod query_state;
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
@ -10,7 +8,6 @@ use crate::drivers::{DatabaseDriver, DefaultDriver};
use crate::enums::*; use crate::enums::*;
use crate::fns::split_map_join; use crate::fns::split_map_join;
use query_state::QueryState;
use regex::Regex; use regex::Regex;
#[derive(Debug)] #[derive(Debug)]
@ -25,7 +22,7 @@ enum QueryType {
#[derive(Debug)] #[derive(Debug)]
pub struct QueryBuilder { pub struct QueryBuilder {
/// The struct to store the query builder info /// The struct to store the query builder info
pub state: QueryState, state: QueryState,
driver: Box<dyn DatabaseDriver>, driver: Box<dyn DatabaseDriver>,
} }
@ -158,15 +155,15 @@ impl QueryBuilder {
/// # use stringqb::prelude::*; /// # use stringqb::prelude::*;
/// # let mut qb = stringqb::query_builder::QueryBuilder::default(); /// # let mut qb = stringqb::query_builder::QueryBuilder::default();
/// // Search for a value that ends with "foo" /// // 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" /// // 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 /// // 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<dyn Any>, 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") self._like(field, value, position, "LIKE", "AND")
} }
@ -174,7 +171,7 @@ impl QueryBuilder {
pub fn or_like( pub fn or_like(
&mut self, &mut self,
field: &str, field: &str,
value: Box<dyn Any>, value: (impl Any),
position: LikeWildcard, position: LikeWildcard,
) -> &mut Self { ) -> &mut Self {
self._like(field, value, position, "LIKE", "OR") self._like(field, value, position, "LIKE", "OR")
@ -184,7 +181,7 @@ impl QueryBuilder {
pub fn not_like( pub fn not_like(
&mut self, &mut self,
field: &str, field: &str,
value: Box<dyn Any>, value: (impl Any),
position: LikeWildcard, position: LikeWildcard,
) -> &mut Self { ) -> &mut Self {
self._like(field, value, position, "NOT LIKE", "AND") self._like(field, value, position, "NOT LIKE", "AND")
@ -194,7 +191,7 @@ impl QueryBuilder {
pub fn or_not_like( pub fn or_not_like(
&mut self, &mut self,
field: &str, field: &str,
value: Box<dyn Any>, value: (impl Any),
position: LikeWildcard, position: LikeWildcard,
) -> &mut Self { ) -> &mut Self {
self._like(field, value, position, "NOT LIKE", "OR") self._like(field, value, position, "NOT LIKE", "OR")
@ -215,12 +212,12 @@ impl QueryBuilder {
/// // Other operators can be used with a separating space /// // Other operators can be used with a separating space
/// qb.having("clues >=", vec![Box::new(4)]); /// qb.having("clues >=", vec![Box::new(4)]);
/// ``` /// ```
pub fn having(&mut self, key: &str, value: Vec<Box<dyn Any>>) -> &mut Self { pub fn having(&mut self, key: &str, value: Vec<(impl Any)>) -> &mut Self {
self._having(key, value, "AND") self._having(key, value, "AND")
} }
/// Add a `having` clause to the query, prefixed with an `or` /// Add a `having` clause to the query, prefixed with an `or`
pub fn or_having(&mut self, key: &str, value: Vec<Box<dyn Any>>) -> &mut Self { pub fn or_having(&mut self, key: &str, value: Vec<(impl Any)>) -> &mut Self {
self._having(key, value, "OR") self._having(key, value, "OR")
} }
@ -229,7 +226,7 @@ impl QueryBuilder {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
/// Alias method for `r#where`. /// Alias method for `r#where`.
pub fn filter(&mut self, key: &str, value: Box<dyn Any>) -> &mut Self { pub fn filter(&mut self, key: &str, value: (impl Any)) -> &mut Self {
self.r#where(key, value) self.r#where(key, value)
} }
@ -246,32 +243,32 @@ impl QueryBuilder {
/// // Other operators can be used with a separating space /// // Other operators can be used with a separating space
/// qb.r#where("key >", Box::new(4)); /// qb.r#where("key >", Box::new(4));
/// ``` /// ```
pub fn r#where(&mut self, key: &str, value: Box<dyn Any>) -> &mut Self { pub fn r#where(&mut self, key: &str, value: (impl Any)) -> &mut Self {
self._where_string(key, value, "AND") self._where_string(key, value, "AND")
} }
/// Specify a condition for the `where` clause of the query, prefixed with `or` /// Specify a condition for the `where` clause of the query, prefixed with `or`
pub fn or_where(&mut self, key: &str, value: Box<dyn Any>) -> &mut Self { pub fn or_where(&mut self, key: &str, value: (impl Any)) -> &mut Self {
self._where_string(key, value, "OR") self._where_string(key, value, "OR")
} }
/// Specify a `where in` clause for the query /// Specify a `where in` clause for the query
pub fn where_in(&mut self, key: &str, values: Vec<Box<dyn Any>>) -> &mut Self { pub fn where_in(&mut self, key: &str, values: Vec<(impl Any)>) -> &mut Self {
self._where_in(key, values, "IN", "AND") self._where_in(key, values, "IN", "AND")
} }
/// Specify a `where in` clause for the query, prefixed with `or` /// Specify a `where in` clause for the query, prefixed with `or`
pub fn or_where_in(&mut self, key: &str, values: Vec<Box<dyn Any>>) -> &mut Self { pub fn or_where_in(&mut self, key: &str, values: Vec<(impl Any)>) -> &mut Self {
self._where_in(key, values, "IN", "OR") self._where_in(key, values, "IN", "OR")
} }
/// Specify a `where not in` clause for the query /// Specify a `where not in` clause for the query
pub fn where_not_in(&mut self, key: &str, values: Vec<Box<dyn Any>>) -> &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") self._where_in(key, values, "NOT IN", "AND")
} }
/// Specify a `where not in` clause for the query, prefixed with `or` /// Specify a `where not in` clause for the query, prefixed with `or`
pub fn or_where_not_in(&mut self, key: &str, values: Vec<Box<dyn Any>>) -> &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") 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 /// Set a key and value for an insert or update query
pub fn set(&mut self, key: &str, value: Box<dyn Any>) -> &mut Self { pub fn set(&mut self, key: &str, value: (impl Any)) -> &mut Self {
// @TODO figure a way to make this easier to use // @TODO figure a way to make this easier to use
let key = self.driver.quote_identifier(key); 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 self
} }
/// Set a map of data for an insert or update query /// Set a map of data for an insert or update query
pub fn set_map(&mut self, data: HashMap<String, Box<dyn Any>>) -> &mut Self { pub fn set_map(&mut self, data: HashMap<String, (impl Any)>) -> &mut Self {
for (key, value) in data { for (key, value) in data {
self.set(&key, value); self.set(&key, value);
} }
@ -520,7 +517,7 @@ impl QueryBuilder {
fn _like( fn _like(
&mut self, &mut self,
field: &str, field: &str,
value: Box<dyn Any>, value: (impl Any),
position: LikeWildcard, position: LikeWildcard,
like: &str, like: &str,
conj: &str, conj: &str,
@ -529,13 +526,14 @@ impl QueryBuilder {
let like = format!("{} {} ?", field, like); let like = format!("{} {} ?", field, like);
let string_val = value.downcast::<String>().unwrap(); let value: Box<dyn Any + 'static> = Box::new(value);
let string_val = value.downcast_ref::<String>().unwrap();
// @TODO Properly parse types of `value` for string formatting // @TODO Properly parse types of `value` for string formatting
let value = match position { let value = match position {
LikeWildcard::Before => format!("%{}", *string_val), LikeWildcard::Before => format!("%{}", string_val),
LikeWildcard::After => format!("{}%s", *string_val), LikeWildcard::After => format!("{}%s", string_val),
LikeWildcard::Both => format!("%{}%", *string_val), LikeWildcard::Both => format!("%{}%", string_val),
}; };
self.state self.state
@ -545,7 +543,7 @@ impl QueryBuilder {
self self
} }
fn _having(&mut self, key: &str, values: Vec<Box<dyn Any>>, conj: &str) -> &mut Self { fn _having(&mut self, key: &str, values: Vec<(impl Any)>, conj: &str) -> &mut Self {
let keys = self._where(key, values); let keys = self._where(key, values);
for k in keys { for k in keys {
@ -573,9 +571,9 @@ impl QueryBuilder {
self self
} }
fn _where(&mut self, key: &str, values: Vec<Box<dyn Any>>) -> Vec<String> { fn _where(&mut self, key: &str, values: Vec<(impl Any)>) -> Vec<String> {
for x in values { for x in values {
self.state.append_where_values(x); self.state.append_where_values(Box::new(x));
} }
vec![String::from(key)] vec![String::from(key)]
@ -584,7 +582,7 @@ impl QueryBuilder {
fn _where_in( fn _where_in(
&mut self, &mut self,
key: &str, key: &str,
values: Vec<Box<dyn Any>>, values: Vec<(impl Any)>,
in_str: &str, in_str: &str,
conj: &str, conj: &str,
) -> &mut Self { ) -> &mut Self {
@ -592,7 +590,7 @@ impl QueryBuilder {
let placeholders = vec!["?"; values.len()]; let placeholders = vec!["?"; values.len()];
for value in values { 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(",")); let str = format!("{} {} ({}) ", key, in_str, placeholders.join(","));
@ -603,7 +601,7 @@ impl QueryBuilder {
self self
} }
fn _where_string(&mut self, key: &str, value: Box<dyn Any>, 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]); let keys = self._where(key, vec![value]);
for k in keys { 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<String>,
// Order by clause
order_map: HashMap<String, String>,
// Group by clause
group_array: Vec<String>,
// Values to apply to prepared statements
values: Vec<Box<Any>>,
// Values to apply to where clauses in prepared statements
where_values: Vec<Box<Any>>,
pub limit: Option<usize>,
pub offset: Option<usize>,
// Query components for complex selects
query_map: Vec<QueryClause>,
// Query components for having clauses
having_map: Vec<QueryClause>,
}
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<Any>) -> &mut Self {
self.values.push(val);
self
}
pub fn append_where_values(&mut self, val: Box<Any>) -> &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<String> {
&self.group_array
}
pub fn get_group_string(&self) -> &str {
&self.group_string
}
pub fn get_having_map(&self) -> &Vec<QueryClause> {
&self.having_map
}
pub fn get_query_map(&self) -> &Vec<QueryClause> {
&self.query_map
}
pub fn get_select_string(&self) -> &str {
&self.select_string
}
pub fn get_set_array_keys(&self) -> &Vec<String> {
&self.set_array_keys
}
pub fn get_set_string(&self) -> &str {
&self.set_string
}
pub fn get_order_map(&self) -> &HashMap<String, String> {
&self.order_map
}
pub fn get_order_string(&self) -> &str {
&self.order_string
}
pub fn get_values(&self) -> &Vec<Box<Any>> {
&self.values
}
pub fn get_where_values(&self) -> &Vec<Box<Any>> {
&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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -712,7 +937,7 @@ mod tests {
fn set_key_value() { fn set_key_value() {
let mut qb = QueryBuilder::default(); 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_eq!(qb.state.get_set_array_keys()[0], "\"foo\"");
assert!(qb.state.get_values()[0].is::<&str>()); assert!(qb.state.get_values()[0].is::<&str>());
@ -728,16 +953,16 @@ mod tests {
fn set_hashmap() { fn set_hashmap() {
let mut qb = QueryBuilder::default(); let mut qb = QueryBuilder::default();
let mut authors: HashMap<String, Box<dyn Any>> = HashMap::new(); let mut authors = HashMap::new();
authors.insert( authors.insert(
String::from("Chinua Achebe"), String::from("Chinua Achebe"),
Box::new(String::from("Nigeria")), String::from("Nigeria"),
); );
authors.insert( authors.insert(
String::from("Rabindranath Tagore"), 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); qb.set_map(authors);
@ -753,12 +978,12 @@ mod tests {
qb.from("test").where_in( qb.from("test").where_in(
"foo", "foo",
vec![ vec![
Box::new(0), 0,
Box::new(1), 1,
Box::new(2), 2,
Box::new(3), 3,
Box::new(4), 4,
Box::new(5), 5,
], ],
); );

View File

@ -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<String>,
// Order by clause
order_map: HashMap<String, String>,
// Group by clause
group_array: Vec<String>,
// Values to apply to prepared statements
values: Vec<Box<Any>>,
// Values to apply to where clauses in prepared statements
where_values: Vec<Box<Any>>,
pub limit: Option<usize>,
pub offset: Option<usize>,
// Query components for complex selects
query_map: Vec<QueryClause>,
// Query components for having clauses
having_map: Vec<QueryClause>,
}
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<Any>) -> &mut Self {
self.values.push(val);
self
}
pub fn append_where_values(&mut self, val: Box<Any>) -> &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<String> {
&self.group_array
}
pub fn get_group_string(&self) -> &str {
&self.group_string
}
pub fn get_having_map(&self) -> &Vec<QueryClause> {
&self.having_map
}
pub fn get_query_map(&self) -> &Vec<QueryClause> {
&self.query_map
}
pub fn get_select_string(&self) -> &str {
&self.select_string
}
pub fn get_set_array_keys(&self) -> &Vec<String> {
&self.set_array_keys
}
pub fn get_set_string(&self) -> &str {
&self.set_string
}
pub fn get_order_map(&self) -> &HashMap<String, String> {
&self.order_map
}
pub fn get_order_string(&self) -> &str {
&self.order_string
}
pub fn get_values(&self) -> &Vec<Box<Any>> {
&self.values
}
pub fn get_where_values(&self) -> &Vec<Box<Any>> {
&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
}
}