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
//!
//! 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

View File

@ -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<dyn DatabaseDriver>,
}
@ -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<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")
}
@ -174,7 +171,7 @@ impl QueryBuilder {
pub fn or_like(
&mut self,
field: &str,
value: Box<dyn Any>,
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<dyn Any>,
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<dyn Any>,
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<Box<dyn Any>>) -> &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<Box<dyn Any>>) -> &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<dyn Any>) -> &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<dyn Any>) -> &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<dyn Any>) -> &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<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")
}
/// 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")
}
/// 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")
}
/// 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")
}
@ -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<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
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<String, Box<dyn Any>>) -> &mut Self {
pub fn set_map(&mut self, data: HashMap<String, (impl Any)>) -> &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<dyn Any>,
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::<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
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<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);
for k in keys {
@ -573,9 +571,9 @@ impl QueryBuilder {
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 {
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<Box<dyn Any>>,
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<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]);
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)]
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<String, Box<dyn Any>> = 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,
],
);

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
}
}