Ugly progress commit

This commit is contained in:
Timothy Warren 2019-04-12 15:25:59 -04:00
parent 2434f2cba8
commit 4cc6f079c3
6 changed files with 135 additions and 81 deletions

View File

@ -1,7 +1,8 @@
//! Database Drivers
//!
//! Drivers represent a connection to a specific type of database engine
use crate::split_map_join;
use crate::fns::split_map_join;
use regex::Regex;
use std::fmt;
#[cfg(feature = "postgres")]

27
src/fns.rs Normal file
View File

@ -0,0 +1,27 @@
//! Utility / Helper functions that don't really belong anywhere else
/// Split a string, apply a closure to each substring,
/// then join the string back together
///
/// For example:
/// ```
/// use stringqb::fns::split_map_join;
///
/// let result = split_map_join("a\n,b, c\t,d", ",", |s| s.trim().to_string());
/// assert_eq!("a,b,c,d", result);
/// ```
pub fn split_map_join<'a>(
string: &'a str,
split_join_by: &str,
map_fn: impl (FnMut(&'a str) -> String),
) -> String {
string
.split(split_join_by)
.into_iter()
.map(map_fn)
.collect::<Vec<String>>()
.join(split_join_by)
}
#[cfg(test)]
mod tests {}

View File

@ -12,44 +12,12 @@ extern crate lazy_static;
pub mod drivers;
pub mod enums;
pub mod fns;
pub mod query_builder;
pub mod types;
/// Split a string, apply a closure to each substring,
/// then join the string back together
///
/// For example:
/// ```
/// use stringqb::split_map_join;
///
/// let result = split_map_join("a\n,b, c\t,d", ",", |s| s.trim().to_string());
/// assert_eq!("a,b,c,d", result);
/// ```
pub fn split_map_join<'a>(
string: &'a str,
split_join_by: &str,
map_fn: impl (FnMut(&'a str) -> String),
) -> String {
string
.split(split_join_by)
.into_iter()
.map(map_fn)
.collect::<Vec<String>>()
.join(split_join_by)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_split_map_join() {
let start = "a\t,b ,c\n,d";
let expected = "a,b,c,d";
assert_eq!(
split_map_join(start, ",", |s| s.trim().to_string()),
expected
);
}
pub mod prelude {
//! Re-exports important traits and types.
pub use crate::enums::*;
pub use crate::drivers::{DatabaseDriver, DefaultDriver};
pub use crate::query_builder::QueryBuilder;
}

View File

@ -3,12 +3,12 @@
//! The QueryBuilder creates sql queries from chained methods
mod query_state;
use std::any::Any;
use std::collections::HashMap;
use crate::drivers::{DatabaseDriver, DefaultDriver};
use crate::enums::*;
use crate::split_map_join;
use crate::types::Wild;
use crate::fns::split_map_join;
use regex::Regex;
use query_state::QueryState;
@ -31,6 +31,8 @@ pub struct QueryBuilder {
impl Default for QueryBuilder {
/// Creates a new QueryBuilder instance with default driver
///
/// This is **not** useful for a real database.
fn default() -> Self {
QueryBuilder {
state: QueryState::new(),
@ -41,6 +43,17 @@ impl Default for QueryBuilder {
impl QueryBuilder {
/// Create a new QueryBuilder instance with a driver
///
/// ```no_run
/// use stringqb::prelude::*;
///
/// // You probably do not want to use the default driver, as it
/// // is basically a mock for testing
/// use stringqb::drivers::DefaultDriver;
///
/// // The query builder must be mutable to be useful
/// let mut qb = QueryBuilder::new(DefaultDriver::new());
/// ```
pub fn new(driver: impl DatabaseDriver + 'static) -> Self {
QueryBuilder {
state: QueryState::new(),
@ -125,22 +138,35 @@ impl QueryBuilder {
// --------------------------------------------------------------------------
/// Creates a `like` clause in the sql statement
pub fn like(&mut self, field: &str, value: Wild, position: LikeWildcard) -> &mut Self {
///
/// ```no_run
/// # 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);
///
/// // Search for a value that starts with "foo"
/// qb.like("field", Box::new("foo"), LikeWildcard::After);
///
/// // Search for a value that has "foo" in it
/// qb.like("field", Box::new("foo"), LikeWildcard::Both);
/// ```
pub fn like(&mut self, field: &str, value: Box<dyn Any>, position: LikeWildcard) -> &mut Self {
self._like(field, value, position, "LIKE", "AND")
}
/// Generates an OR Like clause
pub fn or_like(&mut self, field: &str, value: Wild, position: LikeWildcard) -> &mut Self {
self._like(field, value, position, "LIKE", "OR")
pub fn or_like(&mut self, field: &str, value: Box<dyn Any>, position: LikeWildcard) -> &mut Self {
self._like(field, Box::new(value), position, "LIKE", "OR")
}
/// Generates a NOI Like clause
pub fn not_like(&mut self, field: &str, value: Wild, position: LikeWildcard) -> &mut Self {
pub fn not_like(&mut self, field: &str, value: Box<dyn Any>, position: LikeWildcard) -> &mut Self {
self._like(field, value, position, "NOT LIKE", "AND")
}
/// Generates an OR NOT Like clause
pub fn or_not_like(&mut self, field: &str, value: Wild, position: LikeWildcard) -> &mut Self {
pub fn or_not_like(&mut self, field: &str, value: Box<dyn Any>, position: LikeWildcard) -> &mut Self {
self._like(field, value, position, "NOT LIKE", "OR")
}
@ -149,12 +175,12 @@ impl QueryBuilder {
// --------------------------------------------------------------------------
/// Add a `having` clause to the query
pub fn having(&mut self, key: &str, value: Wild) -> &mut Self {
pub fn having(&mut self, key: &str, value: Box<dyn Any>) -> &mut Self {
unimplemented!();
}
/// Add a `having` clause to the query, prefixed with an `or`
pub fn or_having(&mut self, key: &str, value: Wild) -> &mut Self {
pub fn or_having(&mut self, key: &str, value: Box<dyn Any>) -> &mut Self {
unimplemented!();
}
@ -163,7 +189,7 @@ impl QueryBuilder {
// --------------------------------------------------------------------------
/// Specify a condition for the `where` clause of the query
pub fn r#where(&mut self, key: &str, op: &str, value: Wild) -> &mut Self {
pub fn r#where(&mut self, key: &str, op: &str, value: Box<dyn Any>) -> &mut Self {
// @TODO actually implement setting the keys for the where
self.state.append_where_values(value);
@ -171,33 +197,33 @@ impl QueryBuilder {
}
/// Specify a condition for a `where` clause where a column has a value
pub fn where_eq(&mut self, key: &str, value: Wild) -> &mut Self {
pub fn where_eq(&mut self, key: &str, value: Box<dyn Any>) -> &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: Wild) -> &mut Self {
pub fn or_where(&mut self, key: &str, value: Box<dyn Any>) -> &mut Self {
unimplemented!();
}
/// Specify a `where in` clause for the query
pub fn where_in(&mut self, key: &str, value: Vec<Wild>) -> &mut Self {
unimplemented!();
pub fn where_in(&mut self, key: &str, values: Vec<Box<dyn 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, value: Vec<Wild>) -> &mut Self {
unimplemented!();
pub fn or_where_in(&mut self, key: &str, values: Vec<Box<dyn 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, value: Vec<Wild>) -> &mut Self {
unimplemented!();
pub fn where_not_in(&mut self, key: &str, values: Vec<Box<dyn 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, value: Vec<Wild>) -> &mut Self {
unimplemented!();
pub fn or_where_not_in(&mut self, key: &str, values: Vec<Box<dyn Any>>) -> &mut Self {
self._where_in(key, values, "NOT IN", "OR")
}
// --------------------------------------------------------------------------
@ -205,7 +231,7 @@ impl QueryBuilder {
// --------------------------------------------------------------------------
/// Set a key and value for an insert or update query
pub fn set(&mut self, key: &str, value: Wild) -> &mut Self {
pub fn set(&mut self, key: &str, value: Box<dyn 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);
@ -214,7 +240,7 @@ impl QueryBuilder {
}
/// Set a map of data for an insert or update query
pub fn set_map(&mut self, data: HashMap<String, Wild>) -> &mut Self {
pub fn set_map(&mut self, data: HashMap<String, Box<dyn Any>>) -> &mut Self {
for (key, value) in data {
self.set(&key, value);
}
@ -454,7 +480,7 @@ impl QueryBuilder {
fn _like(
&mut self,
field: &str,
value: Wild,
value: Box<dyn Any>,
position: LikeWildcard,
like: &str,
conj: &str,
@ -485,19 +511,34 @@ impl QueryBuilder {
self
}
fn _where(key: &str, values: Vec<Wild>) -> HashMap<String, Wild> {
fn _where(&mut self, key: &str, values: Vec<Box<dyn Any>>) -> HashMap<String, Box<dyn Any>> {
let mut map: HashMap<String, Box<dyn Any>> = HashMap::new();
unimplemented!();
}
fn _where_in(&mut self, key: &str, values: Vec<Wild>) -> &mut Self {
unimplemented!();
fn _where_in(&mut self, key: &str, values: Vec<Box<dyn Any>>, in_str: &str, conj: &str) -> &mut Self {
let key = self.driver.quote_identifier(key);
let placeholders = vec!["?"; values.len()];
for value in values {
self.state.append_where_values(value);
}
let conj = if self.state.query_map_empty() {
" WHERE "
} else {
conj
};
let str = format!("{} {} ({}) ", key, in_str, placeholders.join(","));
self.state.append_query_map(QueryClauseType::WhereIn, conj, &str);
self
}
fn _where_in_string(&mut self, key: &str, values: Vec<Wild>) -> &mut Self {
unimplemented!();
}
fn _where_string(&mut self, key: &str, value: Wild) -> &mut Self {
fn _where_string(&mut self, key: &str, value: Box<dyn Any>) -> &mut Self {
unimplemented!();
}
@ -585,7 +626,7 @@ mod tests {
fn set_hashmap() {
let mut qb = QueryBuilder::default();
let mut authors: HashMap<String, Wild> = HashMap::new();
let mut authors: HashMap<String, Box<dyn Any>> = HashMap::new();
authors.insert(
String::from("Chinua Achebe"),
Box::new(String::from("Nigeria")),
@ -602,4 +643,24 @@ mod tests {
assert_eq!(qb.state.get_set_array_keys().len(), 3);
assert_eq!(qb.state.get_values().len(), 3);
}
#[test]
fn set_where_in() {
let mut qb = QueryBuilder::default();
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)
]);
let sql = qb.get_compiled_select();
let expected = "SELECT *\nFROM \"test\" WHERE \"foo\" IN (?,?,?,?,?,?) ";
assert_eq!(sql, expected);
}
}

View File

@ -39,10 +39,10 @@ pub struct QueryState {
group_array: Vec<String>,
// Values to apply to prepared statements
values: Vec<Wild>,
values: Vec<Box<Any>>,
// Values to apply to where clauses in prepared statements
where_values: Vec<Wild>,
where_values: Vec<Box<Any>>,
pub limit: Option<usize>,
@ -125,13 +125,13 @@ impl QueryState {
self
}
pub fn append_values(&mut self, val: Wild) -> &mut 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: Wild) -> &mut Self {
pub fn append_where_values(&mut self, val: Box<Any>) -> &mut Self {
self.where_values.push(val);
self
@ -188,11 +188,11 @@ impl QueryState {
&self.order_string
}
pub fn get_values(&self) -> &Vec<Wild> {
pub fn get_values(&self) -> &Vec<Box<Any>> {
&self.values
}
pub fn get_where_values(&self) -> &Vec<Wild> {
pub fn get_where_values(&self) -> &Vec<Box<Any>> {
&self.where_values
}

View File

@ -1,11 +1,8 @@
//! Shared Types for different Database drivers
use std::any::Any;
/// The Wild type is any type, until examined
pub type Wild = Box<dyn Any>;
#[derive(Debug)]
struct Type(pub Wild);
struct Type(pub Box<dyn Any>);
/// Enum struct for mapping between database and Rust types
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]