Ugly progress commit
This commit is contained in:
parent
2434f2cba8
commit
4cc6f079c3
@ -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
27
src/fns.rs
Normal 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 {}
|
44
src/lib.rs
44
src/lib.rs
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)]
|
||||
|
Loading…
Reference in New Issue
Block a user