stringqb/src/query_builder.rs

417 lines
11 KiB
Rust

//! QueryBuilder
//!
//! The QueryBuilder creates sql queries from chained methods
use std::any::Any;
use std::collections::HashMap;
use crate::drivers::DatabaseDriver;
/// The position of the wildcard(s)
/// for a `like` clause
#[derive(Debug)]
pub enum LikeWildcard {
/// Wildcard before search term
/// eg. `%foo`
Before,
/// Wildcard after the search term
/// eg. `foo%`
After,
/// Wildcards surrounding the search term
/// eg. `%foo%`
Both,
}
/// The type of SQL join
#[derive(Debug)]
pub enum JoinType {
Inner,
Outer,
Left,
Right,
LeftOuter,
RightOuter,
}
/// The sort direction
#[derive(Debug)]
pub enum OrderDirection {
/// Sort Ascending
Asc,
/// Sort Descending
Desc,
}
#[derive(Debug)]
enum QueryClauseType {
AndGroupStart,
GroupEnd,
GroupStart,
Like,
OrGroupStart,
Where,
WhereIn,
}
#[derive(Debug)]
struct QueryClause {
clause_type: QueryClauseType,
conjunction: String,
string: String,
}
#[derive(Debug)]
struct QueryState {
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_array: HashMap<String, String>,
// Group by clause
group_array: HashMap<String, String>,
// Values to apply to prepared statements
values: Vec<Box<dyn Any>>,
// Values to apply to where clauses in prepared statements
where_values: Vec<Box<dyn Any>>,
limit: Option<u32>,
offset: Option<u32>,
// 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 {
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_array: HashMap::new(),
group_array: HashMap::new(),
values: vec![],
where_values: vec![],
limit: None,
offset: None,
query_map: vec![],
having_map: vec![],
}
}
}
impl QueryState {
pub fn new() -> QueryState {
QueryState::default()
}
}
/// The struct representing a query builder
#[derive(Default, Debug)]
pub struct QueryBuilder {
state: QueryState,
driver: Option<Box<dyn DatabaseDriver>>,
}
impl QueryBuilder {
/// Creates a new QueryBuilder instance
pub fn new() -> Self {
QueryBuilder {
state: QueryState::new(),
driver: None,
}
}
// --------------------------------------------------------------------------
// ! Select Queries
// --------------------------------------------------------------------------
/// Set the fields to select from the database
pub fn select(mut self, fields: &str) -> Self {
unimplemented!();
}
/// Adds the `distinct` keyword to a query
pub fn distinct(mut self) -> Self {
unimplemented!();
}
/// Specify the database table to select from
pub fn from(mut self, table_name: &str) -> Self {
// @TODO properly escape the table name
self.state.from_string = table_name.to_string();
self
}
// --------------------------------------------------------------------------
// ! 'Like' methods
// --------------------------------------------------------------------------
/// Creates a `like` clause in the sql statement
pub fn like(mut self, field: &str, value: Box<dyn Any>, position: LikeWildcard) -> Self {
unimplemented!();
}
/// Generates an OR Like clause
pub fn or_like(mut self, field: &str, value: Box<dyn Any>, position: LikeWildcard) -> Self {
unimplemented!();
}
/// Generates a NOI Like clause
pub fn not_like(mut self, field: &str, value: Box<dyn Any>, position: LikeWildcard) -> Self {
unimplemented!();
}
/// Generates an OR NOT Like clause
pub fn or_not_like(mut self, field: &str, value: Box<dyn Any>, position: LikeWildcard) -> Self {
unimplemented!();
}
// --------------------------------------------------------------------------
// ! Having methods
// --------------------------------------------------------------------------
/// Add a `having` clause to the query
pub fn having(mut self, key:&str, value: Box<dyn Any>) -> Self {
unimplemented!();
}
/// Add a `having` clause to the query, prefixed with an `or`
pub fn or_having(mut self, key:&str, value: Box<dyn Any>) -> Self {
unimplemented!();
}
// --------------------------------------------------------------------------
// ! 'Where' methods
// --------------------------------------------------------------------------
/// Specify a condition for the `where` clause of the query
pub fn r#where(mut self, key: &str, value: Box<dyn Any>) -> Self {
unimplemented!();
}
/// 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>) -> Self {
unimplemented!();
}
/// Specify a `where in` clause for the query
pub fn where_in(mut self, key: &str, value: Vec<Box<dyn Any>>) -> Self {
unimplemented!();
}
/// Specify a `where in` clause for the query, prefixed with `or`
pub fn or_where_in(mut self, key: &str, value: Vec<Box<dyn Any>>) -> Self {
unimplemented!();
}
/// Specify a `where not in` clause for the query
pub fn where_not_in(mut self, key: &str, value: Vec<Box<dyn Any>>) -> Self {
unimplemented!();
}
/// Specify a `where not in` clause for the query, prefixed with `or`
pub fn or_where_not_in(mut self, key: &str, value: Vec<Box<dyn Any>>) -> Self {
unimplemented!();
}
// --------------------------------------------------------------------------
// ! Other Query Modifier methods
// --------------------------------------------------------------------------
/// Set a key and value for an insert or update query
pub fn set(mut self, key: &str, value: Box<dyn Any>) -> Self {
// @TODO figure a way to make this easier to use
self.state.set_array_keys.push(key.to_string());
self.state.values.push(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>>) -> Self {
for (key, value) in data {
self = self.set(&key, value);
}
self
}
/// Add a table join to the query
pub fn join(mut self, table: &str, condition: &str, join_type: JoinType) -> Self {
unimplemented!();
}
/// Add a group by clause to the query
pub fn group_by(mut self, field: &str) -> Self {
unimplemented!();
}
/// Add an order by clause to the query
pub fn order_by(mut self, field: &str, direction: OrderDirection) -> Self {
unimplemented!();
}
/// Add a limit to the query
pub fn limit(mut self, limit: u32) -> Self {
self.state.limit = Some(limit);
self
}
/// Add an offset to the query
pub fn offset(mut self, offset: u32) -> Self {
self.state.offset = Some(offset);
self
}
// --------------------------------------------------------------------------
// ! Query Grouping Methods
// --------------------------------------------------------------------------
/// Start a logical grouping in the current query
pub fn group_start(mut self) -> Self {
unimplemented!();
}
/// Start a logical grouping, prefixed with `not`
pub fn not_group_start(mut self) -> Self {
unimplemented!();
}
/// Start a logical grouping, prefixed with `or`
pub fn or_group_start(mut self) -> Self {
unimplemented!();
}
/// Start a logical grouping, prefixed with `or not`
pub fn or_not_group_start(mut self) -> Self {
unimplemented!();
}
/// End the current logical grouping
pub fn group_end(mut self) -> Self {
unimplemented!();
}
// --------------------------------------------------------------------------
// ! Query execution methods
// --------------------------------------------------------------------------
/// Execute the built query
pub fn get(self) -> Box<dyn Any> {
unimplemented!();
}
/// Count all the rows in the specified database table
pub fn count_all(self, table: &str) -> u32 {
unimplemented!();
}
/// Execute the generated insert query
pub fn insert(mut self, table: &str) {
// @TODO determine query result type
unimplemented!();
}
/// Execute the generated update query
pub fn update(mut self, table: &str) {
// @TODO determine query result type
unimplemented!();
}
/// Execute the generated delete query
pub fn delete(mut self, table: &str) {
unimplemented!();
}
// --------------------------------------------------------------------------
// ! SQL Returning Methods
// --------------------------------------------------------------------------
/// Get the generated SQL for a select query
pub fn get_compiled_select(self) -> String {
unimplemented!();
}
/// Get the generated SQL for an insert query
pub fn get_compiled_insert(self) -> String {
unimplemented!();
}
/// Get the generated SQL for an update query
pub fn get_compiled_update(self) -> String {
unimplemented!();
}
/// Get the generated SQL for a delete query
pub fn get_compiled_delete(self) -> String {
unimplemented!();
}
// --------------------------------------------------------------------------
// ! Miscellaneous Methods
// --------------------------------------------------------------------------
/// Get a new instance of the query builder
pub fn reset_query(mut self) -> Self {
QueryBuilder::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn set_key_value() {
let builder = QueryBuilder::new()
.set("foo", Box::new("bar"));
assert!(builder.state.values[0].is::<&str>());
// @TODO find a way to make this kind of operation much more ergonomic
assert_eq!(*builder.state.values[0].downcast_ref::<&str>().unwrap(), "bar");
}
#[test]
fn set_hashmap() {
let qb = QueryBuilder::new();
let mut authors: HashMap<String, Box<dyn Any>> = HashMap::new();
authors.insert(
String::from("Chinua Achebe"),
Box::new(String::from("Nigeria")));
authors.insert(
String::from("Rabindranath Tagore"),
Box::new(String::from("India")));
authors.insert(
String::from("Anita Nair"),
Box::new(String::from("India")));
let qb = qb.set_map(authors);
assert_eq!(qb.state.values.len(), 3);
}
}