Skip to content

Commit

Permalink
Merge branch 'type_enum' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
billy1624 committed Feb 20, 2021
2 parents b18f27e + 4f6bcf3 commit cbb0bb5
Show file tree
Hide file tree
Showing 9 changed files with 371 additions and 12 deletions.
5 changes: 3 additions & 2 deletions src/backend/postgres/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
pub(crate) mod foreign_key;
pub(crate) mod index;
pub(crate) mod query;
pub(crate) mod table;
pub(crate) mod index;
pub(crate) mod foreign_key;
pub(crate) mod types;

use super::*;

Expand Down
16 changes: 8 additions & 8 deletions src/backend/postgres/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,14 @@ impl TableBuilder for PostgresQueryBuilder {
}

fn prepare_column_spec(&self, column_spec: &ColumnSpec, sql: &mut SqlWriter) {
write!(sql, "{}", match column_spec {
ColumnSpec::Null => "NULL".into(),
ColumnSpec::NotNull => "NOT NULL".into(),
ColumnSpec::Default(value) => format!("DEFAULT {}", pg_value_to_string(value)),
ColumnSpec::AutoIncrement => "".into(),
ColumnSpec::UniqueKey => "UNIQUE".into(),
ColumnSpec::PrimaryKey => "PRIMARY KEY".into(),
}).unwrap()
match column_spec {
ColumnSpec::Null => write!(sql, "NULL"),
ColumnSpec::NotNull => write!(sql, "NOT NULL"),
ColumnSpec::Default(value) => write!(sql, "DEFAULT {}", pg_value_to_string(value)),
ColumnSpec::AutoIncrement => write!(sql, ""),
ColumnSpec::UniqueKey => write!(sql, "UNIQUE"),
ColumnSpec::PrimaryKey => write!(sql, "PRIMARY KEY"),
}.unwrap()
}

fn prepare_table_opt(&self, table_opt: &TableOpt, sql: &mut SqlWriter) {
Expand Down
62 changes: 62 additions & 0 deletions src/backend/postgres/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use super::*;
use crate::extension::postgres::types::*;

impl TypeBuilder for PostgresQueryBuilder {
fn prepare_type_create_statement(&self, create: &TypeCreateStatement, sql: &mut SqlWriter, collector: &mut dyn FnMut(Value)) {
write!(sql, "CREATE TYPE ").unwrap();

if let Some(name) = &create.name {
name.prepare(sql, '"');
}

if let Some(as_type) = &create.as_type {
write!(sql, " AS ").unwrap();
self.prepare_create_as_type(&as_type, sql);
}

if !create.values.is_empty() {
write!(sql, " (").unwrap();

for (count, val) in create.values.iter().enumerate() {
if count > 0 {
write!(sql, ", ").unwrap();
}
self.prepare_value(&val.to_string().into(), sql, collector);
}

write!(sql, ")").unwrap();
}
}

fn prepare_type_drop_statement(&self, drop: &TypeDropStatement, sql: &mut SqlWriter, _collector: &mut dyn FnMut(Value)) {
write!(sql, "DROP TYPE ").unwrap();

if drop.if_exists {
write!(sql, "IF EXISTS ").unwrap();
}

for name in drop.names.iter() {
name.prepare(sql, '"');
}

if let Some(option) = &drop.option {
write!(sql, " ").unwrap();
self.prepare_drop_type_opt(&option, sql);
}
}
}

impl PostgresQueryBuilder {
fn prepare_create_as_type(&self, as_type: &TypeAs, sql: &mut SqlWriter) {
write!(sql, "{}", match as_type {
TypeAs::Enum => "ENUM",
}).unwrap()
}

fn prepare_drop_type_opt(&self, opt: &TypeDropOpt, sql: &mut SqlWriter) {
write!(sql, "{}", match opt {
TypeDropOpt::Cascade => "CASCADE",
TypeDropOpt::Restrict => "RESTRICT",
}).unwrap()
}
}
3 changes: 3 additions & 0 deletions src/extension/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// mod mysql;
// mod sqlite;
pub mod postgres;
3 changes: 3 additions & 0 deletions src/extension/postgres/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub(crate) mod types;

pub use types::*;
236 changes: 236 additions & 0 deletions src/extension/postgres/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
use std::rc::Rc;
use crate::{backend::QueryBuilder, prepare::*, types::*, value::*};

/// Helper for constructing any type statement
pub struct Type;

#[derive(Clone, Default)]
pub struct TypeCreateStatement {
pub(crate) name: Option<Rc<dyn Iden>>,
pub(crate) as_type: Option<TypeAs>,
pub(crate) values: Vec<Rc<dyn Iden>>,
}

#[derive(Clone)]
pub enum TypeAs {
// Composite,
Enum,
// Range,
// Base,
// Array,
}

#[derive(Clone, Default)]
pub struct TypeDropStatement {
pub(crate) names: Vec<Rc<dyn Iden>>,
pub(crate) option: Option<TypeDropOpt>,
pub(crate) if_exists: bool,
}

#[derive(Clone)]
pub enum TypeDropOpt {
Cascade,
Restrict,
}

pub trait TypeBuilder {
/// Translate [`TypeCreateStatement`] into database specific SQL statement.
fn prepare_type_create_statement(&self, create: &TypeCreateStatement, sql: &mut SqlWriter, collector: &mut dyn FnMut(Value));

/// Translate [`TypeDropStatement`] into database specific SQL statement.
fn prepare_type_drop_statement(&self, create: &TypeDropStatement, sql: &mut SqlWriter, collector: &mut dyn FnMut(Value));
}

impl Type {
/// Construct type [`TypeCreateStatement`]
pub fn create() -> TypeCreateStatement {
TypeCreateStatement::new()
}

/// Construct type [`TypeDropStatement`]
pub fn drop() -> TypeDropStatement {
TypeDropStatement::new()
}
}

impl TypeCreateStatement {

pub fn new() -> Self {
Self::default()
}

/// Create enum as custom type
///
/// ```
/// use sea_query::{*, extension::postgres::Type};
///
/// enum FontFamily {
/// Type,
/// Serif,
/// Sans,
/// Monospace,
/// }
///
/// impl Iden for FontFamily {
/// fn unquoted(&self, s: &mut dyn std::fmt::Write) {
/// write!(s, "{}", match self {
/// Self::Type => "font_family",
/// Self::Serif => "serif",
/// Self::Sans => "sans",
/// Self::Monospace => "monospace",
/// }).unwrap();
/// }
/// }
///
/// assert_eq!(
/// Type::create()
/// .as_enum(FontFamily::Type)
/// .values(vec![FontFamily::Serif, FontFamily::Sans, FontFamily::Monospace])
/// .to_string(PostgresQueryBuilder),
/// r#"CREATE TYPE "font_family" AS ENUM ('serif', 'sans', 'monospace')"#
/// );
/// ```
pub fn as_enum<T: 'static>(&mut self, name: T) -> &mut Self
where T: Iden {
self.name = Some(Rc::new(name));
self.as_type = Some(TypeAs::Enum);
self
}

pub fn values<T: 'static>(&mut self, values: Vec<T>) -> &mut Self
where T: Iden {
self.values_dyn(values.into_iter().map(|c| Rc::new(c) as Rc<dyn Iden>).collect())
}

pub fn values_dyn(&mut self, mut values: Vec<Rc<dyn Iden>>) -> &mut Self {
self.values.append(&mut values);
self
}

// below are boiler plates

pub fn build<T: TypeBuilder>(&self, type_builder: T) -> (String, Vec<Value>) {
self.build_ref(&type_builder)
}

pub fn build_ref<T: TypeBuilder>(&self, type_builder: &T) -> (String, Vec<Value>) {
let mut params = Vec::new();
let mut collector = |v| params.push(v);
let sql = self.build_collect_ref(type_builder, &mut collector);
(sql, params)
}

pub fn build_collect<T: TypeBuilder>(&self, type_builder: T, collector: &mut dyn FnMut(Value)) -> String {
self.build_collect_ref(&type_builder, collector)
}

pub fn build_collect_ref<T: TypeBuilder>(&self, type_builder: &T, collector: &mut dyn FnMut(Value)) -> String {
let mut sql = SqlWriter::new();
type_builder.prepare_type_create_statement(self, &mut sql, collector);
sql.result()
}

/// Build corresponding SQL statement and return SQL string
pub fn to_string<T>(&self, type_builder: T) -> String
where T: TypeBuilder + QueryBuilder {
let (sql, values) = self.build_ref(&type_builder);
inject_parameters(&sql, values, &type_builder)
}
}

impl TypeDropStatement {

pub fn new() -> Self {
Self::default()
}

/// Drop a type
///
/// ```
/// use sea_query::{*, extension::postgres::Type};
///
/// struct FontFamily;
///
/// impl Iden for FontFamily {
/// fn unquoted(&self, s: &mut dyn std::fmt::Write) {
/// write!(s, "{}", "font_family").unwrap();
/// }
/// }
///
/// assert_eq!(
/// Type::drop()
/// .if_exists()
/// .name(FontFamily)
/// .restrict()
/// .to_string(PostgresQueryBuilder),
/// r#"DROP TYPE IF EXISTS "font_family" RESTRICT"#
/// );
/// ```
pub fn name<T: 'static>(&mut self, name: T) -> &mut Self
where T: Iden {
self.name_dyn(Rc::new(name))
}

pub fn name_dyn(&mut self, name: Rc<dyn Iden>) -> &mut Self {
self.names.push(name);
self
}

pub fn names<T: 'static>(&mut self, names: Vec<T>) -> &mut Self
where T: Iden {
self.names_dyn(names.into_iter().map(|c| Rc::new(c) as Rc<dyn Iden>).collect())
}

pub fn names_dyn(&mut self, mut names: Vec<Rc<dyn Iden>>) -> &mut Self {
self.names.append(&mut names);
self
}

/// Set `IF EXISTS`
pub fn if_exists(&mut self) -> &mut Self {
self.if_exists = true;
self
}

/// Set `CASCADE`
pub fn cascade(&mut self) -> &mut Self {
self.option = Some(TypeDropOpt::Cascade);
self
}

/// Set `RESTRICT`
pub fn restrict(&mut self) -> &mut Self {
self.option = Some(TypeDropOpt::Restrict);
self
}

// below are boiler plates

pub fn build<T: TypeBuilder>(&self, type_builder: T) -> (String, Vec<Value>) {
self.build_ref(&type_builder)
}

pub fn build_ref<T: TypeBuilder>(&self, type_builder: &T) -> (String, Vec<Value>) {
let mut params = Vec::new();
let mut collector = |v| params.push(v);
let sql = self.build_collect_ref(type_builder, &mut collector);
(sql, params)
}

pub fn build_collect<T: TypeBuilder>(&self, type_builder: T, collector: &mut dyn FnMut(Value)) -> String {
self.build_collect_ref(&type_builder, collector)
}

pub fn build_collect_ref<T: TypeBuilder>(&self, type_builder: &T, collector: &mut dyn FnMut(Value)) -> String {
let mut sql = SqlWriter::new();
type_builder.prepare_type_drop_statement(self, &mut sql, collector);
sql.result()
}

/// Build corresponding SQL statement and return SQL string
pub fn to_string<T>(&self, type_builder: T) -> String
where T: TypeBuilder + QueryBuilder {
let (sql, values) = self.build_ref(&type_builder);
inject_parameters(&sql, values, &type_builder)
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,7 @@
pub mod backend;
pub mod driver;
pub mod extension;
pub mod foreign_key;
pub mod index;
pub mod query;
Expand All @@ -559,6 +560,7 @@ pub mod value;

pub use backend::*;
//pub use driver::*;
//pub use extension::*;
pub use foreign_key::*;
pub use index::*;
pub use query::*;
Expand Down
3 changes: 1 addition & 2 deletions src/table/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ pub use drop::*;
pub use rename::*;
pub use truncate::*;

/// Shorthand for constructing any table statement
#[derive(Clone)]
/// Helper for constructing any table statement
pub struct Table;

/// All available types of table statement
Expand Down
Loading

0 comments on commit cbb0bb5

Please sign in to comment.