My personal project and infrastructure archive
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
nomicon/development/libs/barrel/src/table.rs

177 lines
5.4 KiB

//! A module that represents tables and columns
//!
//! A table is a collection of columns and some metadata. Creating
//! a table gives you access to the metadata fields that can only
//! be set when creating the table.
//!
//! You can also change existing tables with a closure that can
//! then access individual columns in that table.
use super::backend::SqlGenerator;
use super::{IndexChange, TableChange};
use crate::types::Type;
use std::fmt::{Debug, Formatter, Result as FmtResult};
impl Debug for TableChange {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
f.write_str("TableChange")
}
}
impl Debug for IndexChange {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
f.write_str("IndexChange")
}
}
#[derive(Debug, Clone)]
pub struct Table {
pub meta: TableMeta,
columns: Vec<TableChange>,
indices: Vec<IndexChange>,
}
impl Table {
pub fn new<S: Into<String>>(name: S) -> Self {
Self {
meta: TableMeta::new(name.into()),
columns: vec![],
indices: vec![],
}
}
/// Add a new column to a table
///
/// ```rust
/// # use barrel::{types, Migration};
/// # let mut m = Migration::new();
/// # m.create_table("users", |table| {
/// table.add_column("id", types::primary());
/// table.add_column("name", types::varchar(64));
/// # });
/// ```
pub fn add_column<S: Into<String>>(&mut self, name: S, _type: Type) -> &mut Type {
self.columns
.push(TableChange::AddColumn(name.into(), _type));
match self.columns.last_mut().unwrap() {
&mut TableChange::AddColumn(_, ref mut c) => c,
_ => unreachable!(),
}
}
pub fn drop_column<S: Into<String>>(&mut self, name: S) {
self.columns.push(TableChange::DropColumn(name.into()));
}
pub fn rename_column<S: Into<String>>(&mut self, old: S, new: S) {
self.columns
.push(TableChange::RenameColumn(old.into(), new.into()));
}
/// Inject a line of custom SQL into the table block
///
/// This is a bypass to the barrel typesystem, in case there is
/// something your database supports that barrel doesn't, or if
/// there is an issue with the way that barrel represents types.
/// It does however mean that the SQL provided needs to be
/// specific for one database, meaning that future migrations
/// might become cumbersome.
pub fn inject_custom<S: Into<String>>(&mut self, sql: S) {
self.columns.push(TableChange::CustomLine(sql.into()));
}
/// Add a new index to a table, spanning over multiple columns
pub fn add_index<S: Into<String>>(&mut self, name: S, columns: Type) {
match columns.inner {
crate::types::BaseType::Index(_) => {}
_ => panic!("Calling `add_index` with a non-`Index` type is not allowed!"),
}
self.indices.push(IndexChange::AddIndex {
table: self.meta.name.clone(),
index: name.into(),
columns,
});
}
/// Drop an index on this table
pub fn drop_index<S: Into<String>>(&mut self, name: S) {
self.indices.push(IndexChange::RemoveIndex(
self.meta.name.clone(),
name.into(),
));
}
/// Generate Sql for this table, returned as two vectors
///
/// The first vector (`.0`) represents all column changes done to the table,
/// the second vector (`.1`) contains all index and suffix changes.
///
/// It is very well possible for either of them to be empty,
/// although both being empty *might* signify an error.
pub fn make<T: SqlGenerator>(
&mut self,
ex: bool,
schema: Option<&str>,
) -> (Vec<String>, Vec<String>) {
use IndexChange as IC;
use TableChange as TC;
let columns = self
.columns
.iter_mut()
.map(|change| match change {
&mut TC::AddColumn(ref name, ref col) => T::add_column(ex, schema, name, &col),
&mut TC::DropColumn(ref name) => T::drop_column(name),
&mut TC::RenameColumn(ref old, ref new) => T::rename_column(old, new),
&mut TC::ChangeColumn(ref mut name, _, _) => T::alter_table(name, schema),
&mut TC::CustomLine(ref sql) => sql.clone(),
})
.collect();
let indeces = self
.indices
.iter()
.map(|change| match change {
IC::AddIndex {
index,
table,
columns,
} => T::create_index(table, schema, index, columns),
IC::RemoveIndex(_, index) => T::drop_index(index),
})
.collect();
(columns, indeces)
}
}
/// Some metadata about a table that was just created
#[derive(Debug, Clone)]
pub struct TableMeta {
pub name: String,
pub encoding: String,
}
impl TableMeta {
/// Create a new tablemeta with default values
pub fn new(name: String) -> Self {
Self {
name,
encoding: "utf-8".to_owned(),
}
}
/// Get a clone of the table name
pub fn name(&self) -> String {
self.name.clone()
}
/// Specify an encoding for this table which might vary from the main encoding
/// of your database
pub fn encoding<S: Into<String>>(&mut self, enc: S) -> &mut TableMeta {
self.encoding = enc.into();
self
}
}