stringqb/src/drivers.rs

137 lines
3.5 KiB
Rust

//! Drivers
//!
//! Drivers represent a connection to a specific type of database engine
use crate::split_join_map;
use std::fmt;
#[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 {
pub fn new() -> Self {
DefaultDriver {}
}
}
impl DatabaseDriver for DefaultDriver {}
/// Database Driver Trait
///
/// Interface between the database connection library and the query builder
pub trait DatabaseDriver: fmt::Debug {
/// 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<&str>) -> Vec<String> {
let mut output: Vec<String> = 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(",") {
let mut quoted_parts: Vec<String> = vec![];
for part in identifier.split(",") {
let new_part = part.trim();
let new_part = &self.quote_identifier(new_part);
quoted_parts.push(new_part.to_owned());
}
// This was the only way I could figure to get
// around mutable string reference scope hell
identifier.replace_range(.., &mut quoted_parts.join(","));
}
let (open_char, close_char) = self._quotes();
let trimmed_tiers = split_join_map(identifier, ".", |tier| {
let tier = tier.trim();
// Here where the quoting actually happens. Everything
// else is breaking down the identifier list for this.
if tier.starts_with(open_char) && tier.ends_with(close_char) {
return tier.to_string();
}
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, query: &str) -> Result<(), ()>;
}
#[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", "baz", "fizz.\n\tbuzz.baz",]),
vec![
r#""foo"."bar""#.to_string(),
r#""baz""#.to_string(),
r#""fizz"."buzz"."baz""#.to_string(),
]
);
}
}