Ugly progress commit
This commit is contained in:
parent
2434f2cba8
commit
4cc6f079c3
@ -1,7 +1,8 @@
|
|||||||
//! Database Drivers
|
//! Database Drivers
|
||||||
//!
|
//!
|
||||||
//! Drivers represent a connection to a specific type of database engine
|
//! 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;
|
use std::fmt;
|
||||||
|
|
||||||
#[cfg(feature = "postgres")]
|
#[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 drivers;
|
||||||
pub mod enums;
|
pub mod enums;
|
||||||
|
pub mod fns;
|
||||||
pub mod query_builder;
|
pub mod query_builder;
|
||||||
pub mod types;
|
|
||||||
|
|
||||||
/// Split a string, apply a closure to each substring,
|
pub mod prelude {
|
||||||
/// then join the string back together
|
//! Re-exports important traits and types.
|
||||||
///
|
pub use crate::enums::*;
|
||||||
/// For example:
|
pub use crate::drivers::{DatabaseDriver, DefaultDriver};
|
||||||
/// ```
|
pub use crate::query_builder::QueryBuilder;
|
||||||
/// 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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,12 @@
|
|||||||
//! The QueryBuilder creates sql queries from chained methods
|
//! The QueryBuilder creates sql queries from chained methods
|
||||||
mod query_state;
|
mod query_state;
|
||||||
|
|
||||||
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::drivers::{DatabaseDriver, DefaultDriver};
|
use crate::drivers::{DatabaseDriver, DefaultDriver};
|
||||||
use crate::enums::*;
|
use crate::enums::*;
|
||||||
use crate::split_map_join;
|
use crate::fns::split_map_join;
|
||||||
use crate::types::Wild;
|
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use query_state::QueryState;
|
use query_state::QueryState;
|
||||||
@ -31,6 +31,8 @@ pub struct QueryBuilder {
|
|||||||
|
|
||||||
impl Default for QueryBuilder {
|
impl Default for QueryBuilder {
|
||||||
/// Creates a new QueryBuilder instance with default driver
|
/// Creates a new QueryBuilder instance with default driver
|
||||||
|
///
|
||||||
|
/// This is **not** useful for a real database.
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
QueryBuilder {
|
QueryBuilder {
|
||||||
state: QueryState::new(),
|
state: QueryState::new(),
|
||||||
@ -41,6 +43,17 @@ impl Default for QueryBuilder {
|
|||||||
|
|
||||||
impl QueryBuilder {
|
impl QueryBuilder {
|
||||||
/// Create a new QueryBuilder instance with a driver
|
/// 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 {
|
pub fn new(driver: impl DatabaseDriver + 'static) -> Self {
|
||||||
QueryBuilder {
|
QueryBuilder {
|
||||||
state: QueryState::new(),
|
state: QueryState::new(),
|
||||||
@ -125,22 +138,35 @@ impl QueryBuilder {
|
|||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Creates a `like` clause in the sql statement
|
/// 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")
|
self._like(field, value, position, "LIKE", "AND")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates an OR Like clause
|
/// Generates an OR Like clause
|
||||||
pub fn or_like(&mut self, field: &str, value: Wild, position: LikeWildcard) -> &mut Self {
|
pub fn or_like(&mut self, field: &str, value: Box<dyn Any>, position: LikeWildcard) -> &mut Self {
|
||||||
self._like(field, value, position, "LIKE", "OR")
|
self._like(field, Box::new(value), position, "LIKE", "OR")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a NOI Like clause
|
/// 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")
|
self._like(field, value, position, "NOT LIKE", "AND")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates an OR NOT Like clause
|
/// 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")
|
self._like(field, value, position, "NOT LIKE", "OR")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,12 +175,12 @@ impl QueryBuilder {
|
|||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Add a `having` clause to the query
|
/// 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!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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: Wild) -> &mut Self {
|
pub fn or_having(&mut self, key: &str, value: Box<dyn Any>) -> &mut Self {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +189,7 @@ impl QueryBuilder {
|
|||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Specify a condition for the `where` clause of the query
|
/// 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
|
// @TODO actually implement setting the keys for the where
|
||||||
self.state.append_where_values(value);
|
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
|
/// 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)
|
self.r#where(key, "=", value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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: Wild) -> &mut Self {
|
pub fn or_where(&mut self, key: &str, value: Box<dyn Any>) -> &mut Self {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specify a `where in` clause for the query
|
/// Specify a `where in` clause for the query
|
||||||
pub fn where_in(&mut self, key: &str, value: Vec<Wild>) -> &mut Self {
|
pub fn where_in(&mut self, key: &str, values: Vec<Box<dyn Any>>) -> &mut Self {
|
||||||
unimplemented!();
|
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, value: Vec<Wild>) -> &mut Self {
|
pub fn or_where_in(&mut self, key: &str, values: Vec<Box<dyn Any>>) -> &mut Self {
|
||||||
unimplemented!();
|
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, value: Vec<Wild>) -> &mut Self {
|
pub fn where_not_in(&mut self, key: &str, values: Vec<Box<dyn Any>>) -> &mut Self {
|
||||||
unimplemented!();
|
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, value: Vec<Wild>) -> &mut Self {
|
pub fn or_where_not_in(&mut self, key: &str, values: Vec<Box<dyn Any>>) -> &mut Self {
|
||||||
unimplemented!();
|
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
|
/// 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
|
// @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(value);
|
||||||
@ -214,7 +240,7 @@ impl QueryBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 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, Wild>) -> &mut Self {
|
pub fn set_map(&mut self, data: HashMap<String, Box<dyn Any>>) -> &mut Self {
|
||||||
for (key, value) in data {
|
for (key, value) in data {
|
||||||
self.set(&key, value);
|
self.set(&key, value);
|
||||||
}
|
}
|
||||||
@ -454,7 +480,7 @@ impl QueryBuilder {
|
|||||||
fn _like(
|
fn _like(
|
||||||
&mut self,
|
&mut self,
|
||||||
field: &str,
|
field: &str,
|
||||||
value: Wild,
|
value: Box<dyn Any>,
|
||||||
position: LikeWildcard,
|
position: LikeWildcard,
|
||||||
like: &str,
|
like: &str,
|
||||||
conj: &str,
|
conj: &str,
|
||||||
@ -485,19 +511,34 @@ impl QueryBuilder {
|
|||||||
self
|
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!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _where_in(&mut self, key: &str, values: Vec<Wild>) -> &mut Self {
|
fn _where_in(&mut self, key: &str, values: Vec<Box<dyn Any>>, in_str: &str, conj: &str) -> &mut Self {
|
||||||
unimplemented!();
|
let key = self.driver.quote_identifier(key);
|
||||||
|
let placeholders = vec!["?"; values.len()];
|
||||||
|
|
||||||
|
for value in values {
|
||||||
|
self.state.append_where_values(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _where_in_string(&mut self, key: &str, values: Vec<Wild>) -> &mut Self {
|
let conj = if self.state.query_map_empty() {
|
||||||
unimplemented!();
|
" WHERE "
|
||||||
|
} else {
|
||||||
|
conj
|
||||||
|
};
|
||||||
|
|
||||||
|
let str = format!("{} {} ({}) ", key, in_str, placeholders.join(","));
|
||||||
|
|
||||||
|
self.state.append_query_map(QueryClauseType::WhereIn, conj, &str);
|
||||||
|
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
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!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -585,7 +626,7 @@ mod tests {
|
|||||||
fn set_hashmap() {
|
fn set_hashmap() {
|
||||||
let mut qb = QueryBuilder::default();
|
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(
|
authors.insert(
|
||||||
String::from("Chinua Achebe"),
|
String::from("Chinua Achebe"),
|
||||||
Box::new(String::from("Nigeria")),
|
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_set_array_keys().len(), 3);
|
||||||
assert_eq!(qb.state.get_values().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>,
|
group_array: Vec<String>,
|
||||||
|
|
||||||
// Values to apply to prepared statements
|
// Values to apply to prepared statements
|
||||||
values: Vec<Wild>,
|
values: Vec<Box<Any>>,
|
||||||
|
|
||||||
// Values to apply to where clauses in prepared statements
|
// Values to apply to where clauses in prepared statements
|
||||||
where_values: Vec<Wild>,
|
where_values: Vec<Box<Any>>,
|
||||||
|
|
||||||
pub limit: Option<usize>,
|
pub limit: Option<usize>,
|
||||||
|
|
||||||
@ -125,13 +125,13 @@ impl QueryState {
|
|||||||
self
|
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.values.push(val);
|
||||||
|
|
||||||
self
|
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.where_values.push(val);
|
||||||
|
|
||||||
self
|
self
|
||||||
@ -188,11 +188,11 @@ impl QueryState {
|
|||||||
&self.order_string
|
&self.order_string
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_values(&self) -> &Vec<Wild> {
|
pub fn get_values(&self) -> &Vec<Box<Any>> {
|
||||||
&self.values
|
&self.values
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_where_values(&self) -> &Vec<Wild> {
|
pub fn get_where_values(&self) -> &Vec<Box<Any>> {
|
||||||
&self.where_values
|
&self.where_values
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
//! Shared Types for different Database drivers
|
//! Shared Types for different Database drivers
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
/// The Wild type is any type, until examined
|
|
||||||
pub type Wild = Box<dyn Any>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Type(pub Wild);
|
struct Type(pub Box<dyn Any>);
|
||||||
|
|
||||||
/// Enum struct for mapping between database and Rust types
|
/// Enum struct for mapping between database and Rust types
|
||||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
|
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
|
||||||
|
Loading…
Reference in New Issue
Block a user