From 4a751cb4384c6c374ccd45a82fab960d8f898483 Mon Sep 17 00:00:00 2001 From: gammelalf Date: Fri, 21 Feb 2025 16:14:15 +0100 Subject: [PATCH] Simplified the CREATE TABLE and CREATE INDEX builder --- src/create_index.rs | 174 +++++++++++++------------------------------- src/create_table.rs | 150 ++++++++++++-------------------------- src/lib.rs | 57 +++++---------- 3 files changed, 112 insertions(+), 269 deletions(-) diff --git a/src/create_index.rs b/src/create_index.rs index 41aae87..ae64fc5 100644 --- a/src/create_index.rs +++ b/src/create_index.rs @@ -1,195 +1,119 @@ use crate::error::Error; +use crate::DBImpl; -/** -Representation of a CREATE INDEX builder. -*/ -pub trait CreateIndex<'until_build> { - /** - Creates a unique index. - - Null values are considered different from all other null values. - */ - fn unique(self) -> Self; - - /** - Creates the index only if it doesn't exist yet. - */ - fn if_not_exists(self) -> Self; - - /** - Adds a column to the index. - - **Parameter**: - - `column`: String representing the column to index. - */ - fn add_column(self, column: &'until_build str) -> Self; - - /** - Sets the condition to apply. This will build a partial index. - - **Parameter**: - - `condition`: String representing condition to apply the index to - */ - fn set_condition(self, condition: String) -> Self; - - /** - This method is used to build the create index operation - */ - fn build(self) -> Result; -} - -/** -Representation of a create index operation -*/ -pub struct CreateIndexData<'until_build> { +/// The builder for a `CREATE INDEX` statement +pub struct CreateIndexBuilder<'until_build> { pub(crate) name: &'until_build str, pub(crate) table_name: &'until_build str, pub(crate) unique: bool, pub(crate) if_not_exists: bool, pub(crate) columns: Vec<&'until_build str>, pub(crate) condition: Option, + pub(crate) dialect: DBImpl, } -/** -Implementation of database specific implementations of the [CreateIndex] trait. - -Should only be constructed via [crate::DBImpl::create_index]. -*/ -pub enum CreateIndexImpl<'until_build> { - /** - SQLite representation of the CREATE INDEX operation. - */ - #[cfg(feature = "sqlite")] - Sqlite(CreateIndexData<'until_build>), - /** - MySQL representation of the CREATE INDEX operation. - */ - #[cfg(feature = "mysql")] - MySQL(CreateIndexData<'until_build>), - /** - Postgres representation of the CREATE INDEX operation. - */ - #[cfg(feature = "postgres")] - Postgres(CreateIndexData<'until_build>), -} - -impl<'until_build> CreateIndex<'until_build> for CreateIndexImpl<'until_build> { +impl<'until_build> CreateIndexBuilder<'until_build> { + /// Creates a unique index. + /// + /// Null values are considered different from all other null values. fn unique(mut self) -> Self { - match self { - #[cfg(feature = "sqlite")] - CreateIndexImpl::Sqlite(ref mut d) => d.unique = true, - #[cfg(feature = "mysql")] - CreateIndexImpl::MySQL(ref mut d) => d.unique = true, - #[cfg(feature = "postgres")] - CreateIndexImpl::Postgres(ref mut d) => d.unique = true, - }; + self.unique = true; self } + /// Creates the index only if it doesn't exist yet. fn if_not_exists(mut self) -> Self { - match self { - #[cfg(feature = "sqlite")] - CreateIndexImpl::Sqlite(ref mut d) => d.if_not_exists = true, - #[cfg(feature = "mysql")] - CreateIndexImpl::MySQL(ref mut d) => d.if_not_exists = true, - #[cfg(feature = "postgres")] - CreateIndexImpl::Postgres(ref mut d) => d.if_not_exists = true, - }; + self.if_not_exists = true; self } + /// Adds a column to the index. + /// + /// **Parameter**: + /// - `column`: String representing the column to index. fn add_column(mut self, column: &'until_build str) -> Self { - match self { - #[cfg(feature = "sqlite")] - CreateIndexImpl::Sqlite(ref mut d) => d.columns.push(column), - #[cfg(feature = "mysql")] - CreateIndexImpl::MySQL(ref mut d) => d.columns.push(column), - #[cfg(feature = "postgres")] - CreateIndexImpl::Postgres(ref mut d) => d.columns.push(column), - } + self.columns.push(column); self } + /// Sets the condition to apply. This will build a partial index. + /// + /// **Parameter**: + /// - `condition`: String representing condition to apply the index to fn set_condition(mut self, condition: String) -> Self { - match self { - #[cfg(feature = "sqlite")] - CreateIndexImpl::Sqlite(ref mut d) => d.condition = Some(condition), - #[cfg(feature = "mysql")] - CreateIndexImpl::MySQL(ref mut d) => d.condition = Some(condition), - #[cfg(feature = "postgres")] - CreateIndexImpl::Postgres(ref mut d) => d.condition = Some(condition), - } + self.condition = Some(condition); self } + /// This method is used to build the create index operation. fn build(self) -> Result { - match self { + match self.dialect { #[cfg(feature = "sqlite")] - CreateIndexImpl::Sqlite(d) => { - if d.columns.is_empty() { + DBImpl::SQLite => { + if self.columns.is_empty() { return Err(Error::SQLBuildError(format!( "Couldn't create index on {}: Missing column(s) to create the index on", - d.table_name + self.table_name ))); } Ok(format!( "CREATE {} INDEX{} {} ON {} ({}) {};", - if d.unique { "UNIQUE" } else { "" }, - if d.if_not_exists { + if self.unique { "UNIQUE" } else { "" }, + if self.if_not_exists { " IF NOT EXISTS" } else { "" }, - d.name, - d.table_name, - d.columns.join(", "), - d.condition.as_ref().map_or("", |x| x.as_str()), + self.name, + self.table_name, + self.columns.join(", "), + self.condition.as_ref().map_or("", |x| x.as_str()), )) } #[cfg(feature = "mysql")] - CreateIndexImpl::MySQL(d) => { - if d.columns.is_empty() { + DBImpl::MySQL => { + if self.columns.is_empty() { return Err(Error::SQLBuildError(format!( "Couldn't create index on {}: Missing column(s) to create the index on", - d.table_name + self.table_name ))); } Ok(format!( "CREATE {} INDEX{} {} ON {} ({});", - if d.unique { "UNIQUE" } else { "" }, - if d.if_not_exists { + if self.unique { "UNIQUE" } else { "" }, + if self.if_not_exists { " IF NOT EXISTS" } else { "" }, - d.name, - d.table_name, - d.columns.join(", "), + self.name, + self.table_name, + self.columns.join(", "), )) } #[cfg(feature = "postgres")] - CreateIndexImpl::Postgres(d) => { - if d.columns.is_empty() { + DBImpl::Postgres => { + if self.columns.is_empty() { return Err(Error::SQLBuildError(format!( "Couldn't create index on {}: Missing column(s) to create the index on", - d.table_name + self.table_name ))); } Ok(format!( "CREATE{} INDEX{} {} ON {} ({}){};", - if d.unique { " UNIQUE" } else { "" }, - if d.if_not_exists { + if self.unique { " UNIQUE" } else { "" }, + if self.if_not_exists { " IF NOT EXISTS" } else { "" }, - d.name, - d.table_name, - d.columns.join(", "), - match d.condition { + self.name, + self.table_name, + self.columns.join(", "), + match self.condition { None => String::from(""), Some(cond) => format!(" WHERE {}", cond.as_str()), } diff --git a/src/create_table.rs b/src/create_table.rs index 523b955..c49c7c2 100644 --- a/src/create_table.rs +++ b/src/create_table.rs @@ -2,117 +2,59 @@ use std::fmt::Write; use crate::create_column::{CreateColumn, CreateColumnImpl}; use crate::error::Error; -use crate::Value; - -/** -The trait representing a create table builder -*/ -pub trait CreateTable<'until_build, 'post_build> { - /** - Add a column to the table. - */ - fn add_column(self, column: CreateColumnImpl<'until_build, 'post_build>) -> Self; - - /** - Sets the IF NOT EXISTS trait on the table - */ - fn if_not_exists(self) -> Self; - - /** - This method is used to convert the current state for the given dialect in a - list of tuples. - - Each tuple consists of the query string and the corresponding bind parameters. - */ - fn build(self) -> Result>)>, Error>; -} +use crate::{DBImpl, Value}; -/** -The representation of an create table operation. -*/ -pub struct CreateTableData<'until_build, 'post_build> { +/// The builder for a `CREATE TABLE` statement +pub struct CreateTableBuilder<'until_build, 'post_build> { pub(crate) name: &'until_build str, pub(crate) columns: Vec>, pub(crate) if_not_exists: bool, pub(crate) lookup: Vec>, pub(crate) pre_statements: Vec<(String, Vec>)>, pub(crate) statements: Vec<(String, Vec>)>, + pub(crate) dialect: DBImpl, } -/** -The implementation of the [CreateTable] trait for different database dialects. - -This should only be constructed via [crate::DBImpl::create_table]. -*/ -pub enum CreateTableImpl<'until_build, 'post_build> { - /** - SQLite representation of the CREATE TABLE operation. - */ - #[cfg(feature = "sqlite")] - SQLite(CreateTableData<'until_build, 'post_build>), - /** - MySQL representation of the CREATE TABLE operation. - */ - #[cfg(feature = "mysql")] - MySQL(CreateTableData<'until_build, 'post_build>), - /** - Postgres representation of the CREATE TABLE operation. - */ - #[cfg(feature = "postgres")] - Postgres(CreateTableData<'until_build, 'post_build>), -} - -impl<'until_build, 'post_build> CreateTable<'until_build, 'post_build> - for CreateTableImpl<'until_build, 'post_build> -{ - fn add_column(mut self, column: CreateColumnImpl<'until_build, 'post_build>) -> Self { - match self { - #[cfg(feature = "sqlite")] - CreateTableImpl::SQLite(ref mut d) => d.columns.push(column), - #[cfg(feature = "mysql")] - CreateTableImpl::MySQL(ref mut d) => d.columns.push(column), - #[cfg(feature = "postgres")] - CreateTableImpl::Postgres(ref mut d) => d.columns.push(column), - } +impl<'until_build, 'post_build> CreateTableBuilder<'until_build, 'post_build> { + /// Add a column to the table + pub fn add_column(mut self, column: CreateColumnImpl<'until_build, 'post_build>) -> Self { + self.columns.push(column); self } - fn if_not_exists(mut self) -> Self { - match self { - #[cfg(feature = "sqlite")] - CreateTableImpl::SQLite(ref mut d) => d.if_not_exists = true, - #[cfg(feature = "mysql")] - CreateTableImpl::MySQL(ref mut d) => d.if_not_exists = true, - #[cfg(feature = "postgres")] - CreateTableImpl::Postgres(ref mut d) => d.if_not_exists = true, - } + /// Sets the IF NOT EXISTS trait on the table + pub fn if_not_exists(mut self) -> Self { + self.if_not_exists = true; self } - fn build(self) -> Result>)>, Error> { - match self { + /// This method is used to convert the current state for the given dialect in a list of tuples. + /// + /// Each tuple consists of a query string and the corresponding bind parameters. + pub fn build(mut self) -> Result>)>, Error> { + match self.dialect { #[cfg(feature = "sqlite")] - CreateTableImpl::SQLite(mut d) => { + DBImpl::SQLite => { let mut s = format!( "CREATE TABLE{} \"{}\" (", - if d.if_not_exists { + if self.if_not_exists { " IF NOT EXISTS" } else { "" }, - d.name + self.name ); - let columns_len = d.columns.len() - 1; - for (idx, mut x) in d.columns.into_iter().enumerate() { + let columns_len = self.columns.len() - 1; + for (idx, mut x) in self.columns.into_iter().enumerate() { #[cfg(any(feature = "mysql", feature = "postgres"))] if let CreateColumnImpl::SQLite(ref mut cci) = x { - cci.statements = Some(&mut d.statements) + cci.statements = Some(&mut self.statements) } #[cfg(not(any(feature = "mysql", feature = "postgres")))] { let CreateColumnImpl::SQLite(ref mut cci) = x; - cci.statements = Some(&mut d.statements); + cci.statements = Some(&mut self.statements); } x.build(&mut s)?; @@ -124,33 +66,33 @@ impl<'until_build, 'post_build> CreateTable<'until_build, 'post_build> write!(s, ") STRICT; ").unwrap(); - let mut statements = vec![(s, d.lookup)]; - statements.extend(d.statements); + let mut statements = vec![(s, self.lookup)]; + statements.extend(self.statements); Ok(statements) } #[cfg(feature = "mysql")] - CreateTableImpl::MySQL(mut d) => { + DBImpl::MySQL => { let mut s = format!( "CREATE TABLE{} `{}` (", - if d.if_not_exists { + if self.if_not_exists { " IF NOT EXISTS" } else { "" }, - d.name + self.name ); - let columns_len = d.columns.len() - 1; - for (idx, mut x) in d.columns.into_iter().enumerate() { + let columns_len = self.columns.len() - 1; + for (idx, mut x) in self.columns.into_iter().enumerate() { #[cfg(any(feature = "postgres", feature = "sqlite"))] if let CreateColumnImpl::MySQL(ref mut cci) = x { - cci.statements = Some(&mut d.statements); + cci.statements = Some(&mut self.statements); } #[cfg(not(any(feature = "postgres", feature = "sqlite")))] { let CreateColumnImpl::MySQL(ref mut cci) = x; - cci.statements = Some(&mut d.statements); + cci.statements = Some(&mut self.statements); } x.build(&mut s)?; @@ -162,35 +104,35 @@ impl<'until_build, 'post_build> CreateTable<'until_build, 'post_build> write!(s, "); ").unwrap(); - let mut statements = vec![(s, d.lookup)]; - statements.extend(d.statements); + let mut statements = vec![(s, self.lookup)]; + statements.extend(self.statements); Ok(statements) } #[cfg(feature = "postgres")] - CreateTableImpl::Postgres(mut d) => { + DBImpl::Postgres => { let mut s = format!( "CREATE TABLE{} \"{}\" (", - if d.if_not_exists { + if self.if_not_exists { " IF NOT EXISTS" } else { "" }, - d.name + self.name ); - let columns_len = d.columns.len() - 1; - for (idx, mut x) in d.columns.into_iter().enumerate() { + let columns_len = self.columns.len() - 1; + for (idx, mut x) in self.columns.into_iter().enumerate() { #[cfg(any(feature = "sqlite", feature = "mysql"))] if let CreateColumnImpl::Postgres(ref mut cci) = x { - cci.pre_statements = Some(&mut d.pre_statements); - cci.statements = Some(&mut d.statements); + cci.pre_statements = Some(&mut self.pre_statements); + cci.statements = Some(&mut self.statements); } #[cfg(not(any(feature = "sqlite", feature = "mysql")))] { let CreateColumnImpl::Postgres(ref mut cci) = x; - cci.pre_statements = Some(&mut d.pre_statements); - cci.statements = Some(&mut d.statements); + cci.pre_statements = Some(&mut self.pre_statements); + cci.statements = Some(&mut self.statements); } x.build(&mut s)?; @@ -202,9 +144,9 @@ impl<'until_build, 'post_build> CreateTable<'until_build, 'post_build> write!(s, "); ").unwrap(); - let mut statements = d.pre_statements; - statements.push((s, d.lookup)); - statements.extend(d.statements); + let mut statements = self.pre_statements; + statements.push((s, self.lookup)); + statements.extend(self.statements); Ok(statements) } diff --git a/src/lib.rs b/src/lib.rs index ff443a7..7968d25 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,8 +60,8 @@ use crate::create_column::CreateColumnPostgresData; #[cfg(feature = "sqlite")] use crate::create_column::CreateColumnSQLiteData; use crate::create_column::{CreateColumnImpl, SQLAnnotation}; -use crate::create_index::{CreateIndex, CreateIndexData, CreateIndexImpl}; -use crate::create_table::{CreateTable, CreateTableData, CreateTableImpl}; +use crate::create_index::CreateIndexBuilder; +use crate::create_table::CreateTableBuilder; use crate::create_trigger::{ SQLCreateTrigger, SQLCreateTriggerOperation, SQLCreateTriggerPointInTime, }; @@ -93,35 +93,22 @@ pub enum DBImpl { } impl DBImpl { - /** - The entry point to create a table. - - **Parameter**: - - `name`: Name of the table - */ + /// The entry point to create a table. + /// + /// **Parameter**: + /// - `name`: Name of the table pub fn create_table<'until_build, 'post_build>( &self, name: &'until_build str, - ) -> impl CreateTable<'until_build, 'post_build> - where - 'post_build: 'until_build, - { - let d = CreateTableData { + ) -> CreateTableBuilder<'until_build, 'post_build> { + CreateTableBuilder { name, columns: vec![], if_not_exists: false, lookup: vec![], pre_statements: vec![], statements: vec![], - }; - - match self { - #[cfg(feature = "sqlite")] - DBImpl::SQLite => CreateTableImpl::SQLite(d), - #[cfg(feature = "mysql")] - DBImpl::MySQL => CreateTableImpl::MySQL(d), - #[cfg(feature = "postgres")] - DBImpl::Postgres => CreateTableImpl::Postgres(d), + dialect: *self, } } @@ -152,34 +139,24 @@ impl DBImpl { } } - /** - The entry point to create an index. - - **Parameter**: - - `name`: Name of the index. - - `table_name`: Table to create the index on. - */ + /// The entry point to create an index. + /// + /// **Parameter**: + /// - `name`: Name of the index. + /// - `table_name`: Table to create the index on. pub fn create_index<'until_build>( &self, name: &'until_build str, table_name: &'until_build str, - ) -> impl CreateIndex<'until_build> { - let d = CreateIndexData { + ) -> CreateIndexBuilder<'until_build> { + CreateIndexBuilder { name, table_name, unique: false, if_not_exists: false, columns: vec![], condition: None, - }; - - match self { - #[cfg(feature = "sqlite")] - DBImpl::SQLite => CreateIndexImpl::Sqlite(d), - #[cfg(feature = "mysql")] - DBImpl::MySQL => CreateIndexImpl::MySQL(d), - #[cfg(feature = "postgres")] - DBImpl::Postgres => CreateIndexImpl::Postgres(d), + dialect: *self, } }