//! Database Drivers //! //! Drivers represent a connection to a specific type of database engine use crate::fns::split_map_join; use regex::Regex; use std::any::Any; use std::error::Error; #[cfg(feature = "postgres")] pub mod postgres; #[cfg(feature = "sqlite")] pub mod sqlite; #[cfg(feature = "mysql")] pub mod mysql; #[cfg(feature = "mssql")] pub mod mssql; #[derive(Debug)] struct Connection; /// Result for a db query #[derive(Debug)] struct QueryResult; /// Empty Driver implementation /// /// Good for general testing #[derive(Debug)] pub struct DefaultDriver; impl DefaultDriver { /// Create a `DefaultDriver` pub fn new() -> Self { DefaultDriver {} } } impl DatabaseDriver for DefaultDriver { fn explain(&self, sql: &str) -> String { format!("EXPLAIN {}", sql) } fn random(&self) -> String { String::from(" RANDOM") } } /// Database Driver Trait /// /// Interface between the database connection library and the query builder pub trait DatabaseDriver { /// Get which characters are used to delimit identifiers /// such as tables, and columns fn _quotes(&self) -> (char, char) { ('"', '"') } /// Vector version of `quote_identifier` fn quote_identifiers(&self, identifiers: Vec) -> Vec { let mut output: Vec = vec![]; for identifier in identifiers { output.push(self.quote_identifier(&identifier).to_string()); } output } /// Quote the identifiers passed, so the database does not /// normalize the identifiers (eg, table, column, etc.) fn quote_identifier(&self, identifier: &str) -> String { let mut identifier = &mut String::from(identifier); // If the identifier is actually a comma-separated list, // recurse to quote each identifier in the list if identifier.contains(",") { // This was the only way I could figure to get // around mutable string reference scope hell let func = |part: &str| self.quote_identifier(part.trim()); identifier.replace_range(.., &split_map_join(identifier, ",", func)); } let (open_char, close_char) = self._quotes(); let trimmed_tiers = split_map_join(identifier, ".", |tier| { let tier = tier.trim(); if tier.starts_with(open_char) && tier.ends_with(close_char) { return tier.to_string(); } // Here where the quoting actually happens. Everything // else is breaking down the identifier list for this. format!("{}{}{}", &open_char, tier, &close_char) }); trimmed_tiers // @TODO Fix functional calls in 'select' queries } // Runs a basic sql query on the database fn query(&self, sql: &str) -> Result, Box> { Ok(Box::new(String::from(sql))) } /// Prepares an sql statement for the database fn prepare(&self, sql: &str) -> Result<(), ()> { Ok(()) } // Runs a prepared statement on the database // fn execute(&self, sql: &str, ?) -> Result; // Prepares and executes an sql query // fn prepare_execute(&self, sql: &str, params: &[?]) -> Result; // ------------------------------------------------------------------------ // ! Driver-specific SQL methods // ------------------------------------------------------------------------ /// Take an existing sql query and add a limit and/or offset fn limit(&self, sql: &str, limit: Option, offset: Option) -> String { let mut sql = sql.to_string(); if limit.is_some() { sql += &format!("\nLIMIT {}", limit.unwrap()); } if limit.is_some() && offset.is_some() { sql += &format!(" OFFSET {}", offset.unwrap()); } sql } /// Get the query plan for the existing sql fn explain(&self, sql: &str) -> String; /// Get the database's keyword for sorting randomly fn random(&self) -> String; } #[cfg(test)] mod tests { use super::*; #[test] fn test_quote_identifier() { let driver = DefaultDriver::new(); assert_eq!( driver.quote_identifier("foo, bar, baz"), r#""foo","bar","baz""# ); assert_eq!( driver.quote_identifier("foo.bar, baz, fizz"), r#""foo"."bar","baz","fizz""# ); } #[test] fn test_quote_identifiers() { let driver = DefaultDriver::new(); assert_eq!( driver.quote_identifiers(vec![ "\tfoo. bar".to_string(), "baz".to_string(), "fizz.\n\tbuzz.baz".to_string(), ]), vec![ r#""foo"."bar""#.to_string(), r#""baz""#.to_string(), r#""fizz"."buzz"."baz""#.to_string(), ] ); } }