#![macro_use]
pub use paste::paste;
#[doc(hidden)]
#[macro_export]
macro_rules! table_next_id {
($table:ident: $type:ty) => {
$crate::paste! {
pub fn [<$table _next_id>](&self) -> $type {
self.$table
.keys()
.max()
.map(|key| *key + 1)
.unwrap_or_default()
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! table_insert {
($table:ident: $type:ty, $pk:ident, $errty:ty) => {
$crate::paste! {
pub fn [<$table _insert>](&mut self, data: $type) -> Result<(), $errty> {
self.[<$table _insert_check>](&data)?;
self.[<$table _insert_indices>](&data);
self.$table.insert(data.$pk.clone(), data);
Ok(())
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! table_insert_check {
($self:expr, foreign, $table:ident, $data:ident, $expr:expr, $err:expr) => {
if $self.$table.get(&$expr).is_none() {
return Err($err);
}
};
($self:expr, unique, $table:ident, $data:ident, $expr:expr, $err:expr) => {
if $self.$table.get(&$expr).is_some() {
return Err($err);
}
};
($self:expr, primary, $table:ident, $data:ident, $expr:expr, $err:expr) => {
if $self.$table.get(&$expr).is_some() {
return Err($err);
}
};
($self:expr, constraint, $table:ident, $data:ident, $expr:expr, $err:expr) => {
if let Err(error) = $self.$table($data) {
return Err(error);
}
};
($self:expr, $other:ident, $table:ident, $data:ident, $expr:expr, $err:expr) => {};
}
#[doc(hidden)]
#[macro_export]
macro_rules! table_insert_checks {
($table:ident: $type:ty, $errty:ty, $($itype:ident $name:ident $prop:tt => $err:expr),*) => {
$crate::paste! {
fn [<$table _insert_check>](&mut self, data: &$type) -> Result<(), $errty> {
$($crate::table_insert_check!(self, $itype, $name, data, $crate::table_prop!(data, $prop), $err);)*
Ok(())
}
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! table_delete {
($table:ident: $type:ty, $pk:ty, $errty:ty) => {
$crate::paste! {
pub fn [<$table _delete>](&mut self, id: $pk) -> Result<$type, $errty> {
let data = self.[<$table _delete_check>](id.clone())?;
self.[<$table _delete_indices>](&data);
self.$table.remove(&id);
Ok(data)
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! table_insert_index {
($self:expr, $pk:expr, unique, $name:ident, $prop:expr) => {
if $self.$name.insert($prop.clone(), $pk.clone()).is_some() {
panic!(concat!(stringify!($name), " index entry already existsted"));
}
};
($self:expr, $pk:expr, index, $name:ident, $prop:expr) => {
if !$self
.$name
.entry($prop.clone())
.or_default()
.insert($pk.clone())
{
panic!(concat!(stringify!($name), " index already had new user"));
}
};
($self:expr, $pk:expr, $other:ident, $name:ident, $prop:expr) => {};
}
#[doc(hidden)]
#[macro_export]
macro_rules! table_prop {
($data:expr, $prop:ident) => {
$data.$prop
};
($data:expr, ($($prop:ident),*)) => {
($($crate::table_prop!(@inner, $data, $prop)),*)
};
(@inner, $data:expr, $prop:ident) => { $data.$prop.clone() };
($data:expr, $prop:tt) => { $prop };
}
#[doc(hidden)]
#[macro_export]
macro_rules! table_insert_indices {
($table:ident: $type:ty, $pk:ident, $($itype:ident $name:ident $prop:tt => $err:expr),*) => {
$crate::paste! {
fn [<$table _insert_indices>](&mut self, data: &$type) {
$($crate::table_insert_index!(self, data.$pk, $itype, $name, $crate::table_prop!(data, $prop));)*
}
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! table_delete_index {
($self:expr, $pk:expr, unique, $name:ident, $prop:expr) => {
match $self.$name.remove(&$prop) {
None => panic!(concat!(stringify!($name), " unique index missing item")),
Some(value) if value != $pk => {
panic!(concat!(stringify!($name), " unique index had wrong key"))
}
_ => {}
}
};
($self:expr, $pk:expr, reverse, $name:ident, $prop:expr) => {
match $self.$name.remove(&$pk) {
Some(value) if !value.is_empty() => {
panic!(concat!(stringify!($name, " reverse index not empty")))
}
_ => {}
}
};
($self:expr, $pk:expr, index, $name:ident, $prop:expr) => {
let values = $self
.$name
.get_mut(&$prop)
.expect(concat!(stringify!($name), " index missing"));
if !values.remove(&$pk) {
panic!(concat!(stringify!($name), " index already had new user"));
}
if values.is_empty() {
$self.$name.remove(&$prop);
}
};
($self:expr, $pk:expr, $other:ident, $name:ident, $prop:expr) => {};
}
#[doc(hidden)]
#[macro_export]
macro_rules! table_delete_indices {
($table:ident: $type:ty, $pk:ident, $($itype:ident $name:ident $prop:tt => $err:expr),*) => {
$crate::paste! {
fn [<$table _delete_indices>](&mut self, data: &$type) {
$($crate::table_delete_index!(self, data.$pk, $itype, $name, $crate::table_prop!(data, $prop));)*
}
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! table_update_index {
($self:expr, $pk:expr, index, $name:ident, $old:expr, $new:expr) => {
if $old != $new {
$crate::table_delete_index!($self, $pk, index, $name, $old);
$crate::table_insert_index!($self, $pk, index, $name, $new);
}
};
($self:expr, $pk:expr, unique, $name:ident, $old:expr, $new:expr) => {
if $old != $new {
$crate::table_delete_index!($self, $pk, unique, $name, $old);
$crate::table_insert_index!($self, $pk, unique, $name, $new);
}
};
($self:expr, $pk:expr, reverse, $name:ident, $old:expr, $new:expr) => {
if $old != $new {
$crate::table_delete_index!($self, $pk, reverse, $name, $old);
$crate::table_insert_index!($self, $pk, reverse, $name, $new);
}
};
($self:expr, $pk:expr, $other:ident, $name:ident, $old:expr, $new:expr) => {};
}
#[doc(hidden)]
#[macro_export]
macro_rules! table_update_indices {
($table:ident: $type:ty, $pk:ident, $($itype:ident $name:ident $prop:tt => $err:expr),*) => {
$crate::paste! {
fn [<$table _update_indices>](&mut self, old: &$type, new: &$type) {
$($crate::table_update_index!(self, old.$pk, $itype, $name, $crate::table_prop!(old, $prop), $crate::table_prop!(new, $prop));)*
}
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! table_update_check {
($self:expr, $pk:expr, unique, $name:ident, $data:ident, $old:expr, $new:expr, $err:expr) => {
if $old != $new {
$crate::table_insert_check!($self, unique, $name, $data, $new, $err);
}
};
($self:expr, $pk:expr, foreign, $name:ident, $data:ident, $old:expr, $new:expr, $err:expr) => {
if $old != $new {
$crate::table_insert_check!($self, foreign, $name, $data, $new, $err);
}
};
($self:expr, $pk:expr, constraint, $name:ident, $data:ident, $old:expr, $new:expr, $err:expr) => {
$crate::table_insert_check!($self, constraint, $name, $data, $new, $err);
};
($self:expr, $pk:expr, $other:ident, $name:ident, $data:ident, $old:expr, $new:expr, $err:expr) => {};
}
#[doc(hidden)]
#[macro_export]
macro_rules! table_update_checks {
($table:ident: $type:ty, $pk:ident, $errty:ty, $($itype:ident $name:ident $prop:tt => $err:expr),*) => {
$crate::paste! {
fn [<$table _update_check>](&mut self, old: &$type, new: &$type) -> Result<(), $errty> {
$($crate::table_update_check!(self, old.$pk, $itype, $name, new, $crate::table_prop!(old, $prop), $crate::table_prop!(new, $prop), $err);)*
Ok(())
}
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! table_delete_check {
($self:expr, $pk:expr, reverse, $name:ident, $prop:expr, $err:expr) => {
match $self.$name.get(&$pk) {
None => {}
Some(items) if items.is_empty() => {
panic!(concat!(stringify!($name), " has empty index"))
}
Some(_items) => return Err($err),
}
};
($self:expr, $pk:expr, $other:ident, $name:ident, $prop:expr, $err:expr) => {};
}
#[doc(hidden)]
#[macro_export]
macro_rules! table_delete_checks {
($table:ident: $type:ty, $pk:ident: $pkty:ty, $errty:ty, $missing:expr, $($itype:ident $name:ident $prop:tt => $err:expr),*) => {
$crate::paste! {
fn [<$table _delete_check>](&mut self, id: $pkty) -> Result<$type, $errty> {
let row = match self.$table.get(&id) {
Some(row) => row.clone(),
None => return Err($missing),
};
$($crate::table_delete_check!(self, row.$pk, $itype, $name, $crate::table_prop!(row, $prop), $err);)*
Ok(row)
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! table_indices {
($table:ident: $type:ty, $pk:ident: $pkty:ty, $errty:ty, $error:expr, $($itype:ident $name:ident $prop:tt => $err:expr),*) => {
$crate::table_insert_checks!($table: $type, $errty, $($itype $name $prop => $err),*);
$crate::table_update_checks!($table: $type, $pk, $errty, $($itype $name $prop => $err),*);
$crate::table_delete_checks!($table: $type, $pk: $pkty, $errty, $error, $($itype $name $prop => $err),*);
$crate::table_insert_indices!($table: $type, $pk, $($itype $name $prop => $err),*);
$crate::table_delete_indices!($table: $type, $pk, $($itype $name $prop => $err),*);
$crate::table_update_indices!($table: $type, $pk, $($itype $name $prop => $err),*);
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! table_update {
($table:ident: $type:ty, $pk:ident => $err:expr, $errty:ty) => {
$crate::paste! {
pub fn [<$table _update>](&mut self, new: $type) -> Result<$type, $errty> {
let old = match self.$table.get(&new.$pk) {
Some(value) => value.clone(),
None => return Err($err),
};
self.[<$table _update_check>](&old, &new)?;
self.[<$table _update_indices>](&old, &new);
self.$table.insert(new.$pk.clone(), new);
Ok(old)
}
}
};
}
#[macro_export]
macro_rules! table {
($table:ident: $type:ty, $pk:ident: $pkty:ty, missing $errty:ty => $missing:expr, $($itype:ident $name:ident $prop:tt => $err:expr),*) => {
$crate::table_next_id!($table: $pkty);
$crate::table_indices!($table: $type, $pk: $pkty, $errty, $missing, $($itype $name $prop => $err),*);
$crate::table_delete!($table: $type, $pkty, $errty);
$crate::table_insert!($table: $type, $pk, $errty);
$crate::table_update!($table: $type, $pk => $missing, $errty);
};
($table:ident: $type:ty, $pk:ident: $pkty:ty, noautokey, missing $errty:ty => $missing:expr, $($itype:ident $name:ident $prop:tt => $err:expr),*) => {
$crate::table_indices!($table: $type, $pk: $pkty, $errty, $missing, $($itype $name $prop => $err),*);
$crate::table_delete!($table: $type, $pkty, $errty);
$crate::table_insert!($table: $type, $pk, $errty);
$crate::table_update!($table: $type, $pk => $missing, $errty);
}
}