use super::BuildableStatement;
use crate::errors::SqlBuilderError;
#[derive(Debug)]
pub struct Select {
table: String,
distinct: bool,
condition: Option<String>,
columns: Option<String>,
group: Option<String>,
order: Option<String>,
limit: Option<u32>,
offset: Option<u32>,
join: Option<Vec<String>>,
}
impl Select {
pub fn new(table: &str) -> Self {
Self {
table: table.to_string(),
distinct: false,
columns: None,
condition: None,
group: None,
order: None,
limit: None,
offset: None,
join: None,
}
}
pub fn from(query: &str) -> Result<Select, SqlBuilderError> {
let mut parts = query.split_whitespace();
let select = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
if select.to_uppercase() != "SELECT" {
return Err(SqlBuilderError::InvalidQuery);
}
let _ = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
let from = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
if from.to_uppercase() != "FROM" {
return Err(SqlBuilderError::InvalidQuery);
}
let table = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
let mut select_builder = Select::new(table);
while let Some(part) = parts.next() {
match part.to_uppercase().as_str() {
"WHERE" => {
let condition = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
select_builder.condition(condition.to_string());
}
"GROUP" => {
let by = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
if by.to_uppercase() != "BY" {
return Err(SqlBuilderError::InvalidQuery);
}
let group = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
select_builder.group(group);
}
"ORDER" => {
let by = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
if by.to_uppercase() != "BY" {
return Err(SqlBuilderError::InvalidQuery);
}
let order = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
select_builder.order(order);
}
"LIMIT" => {
let limit = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
let limit = limit
.parse::<u32>()
.map_err(|_| SqlBuilderError::InvalidQuery)?;
select_builder.limit(limit);
}
"OFFSET" => {
let offset = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
let offset = offset
.parse::<u32>()
.map_err(|_| SqlBuilderError::InvalidQuery)?;
select_builder.offset(offset);
}
_ => return Err(SqlBuilderError::InvalidQuery),
}
}
Ok(select_builder)
}
pub fn distinct(&mut self) -> &mut Self {
self.distinct = true;
self
}
pub fn columns(&mut self, columns: &str) -> &mut Self {
self.columns = Some(columns.to_string());
self
}
pub fn group(&mut self, group: &str) -> &mut Self {
self.group = Some(group.to_string());
self
}
pub fn order(&mut self, order: &str) -> &mut Self {
self.order = Some(order.to_string());
self
}
pub fn condition(&mut self, condition: String) -> &mut Self {
self.condition = Some(condition);
self
}
pub fn limit(&mut self, limit: u32) -> &mut Self {
self.limit = Some(limit);
self
}
pub fn offset(&mut self, offset: u32) -> &mut Self {
self.offset = Some(offset);
self
}
pub fn join(&mut self, join: String) -> &mut Self {
match &mut self.join {
None => self.join = Some(vec![join]),
Some(j) => j.push(join),
}
self
}
pub fn build(&self) -> Result<String, SqlBuilderError> {
if self.table.is_empty() {
return Err(SqlBuilderError::EmptyTableName);
}
let mut statement = String::from("SELECT");
if self.distinct {
statement.push_str(" DISTINCT");
}
if let Some(columns) = &self.columns {
statement.push_str(&format!(" {}", columns));
} else {
statement.push_str(" *");
}
statement.push_str(&format!(" FROM {}", self.table));
if let Some(join) = &self.join {
for j in join {
statement.push_str(&format!(" {}", j))
}
}
if let Some(condition) = &self.condition {
statement.push_str(&format!(" WHERE {}", condition));
}
if let Some(group) = &self.group {
statement.push_str(&format!(" GROUP BY {}", group));
}
if let Some(order) = &self.order {
statement.push_str(&format!(" ORDER BY {}", order));
}
if let Some(limit) = &self.limit {
statement.push_str(&format!(" LIMIT {}", limit));
}
if let Some(offset) = &self.offset {
statement.push_str(&format!(" OFFSET {}", offset));
}
statement.push(';');
Ok(statement)
}
}
impl BuildableStatement for Select {
fn build(&self) -> String {
self.build().unwrap()
}
}