use crate::db::auth::{StAccess, StTableType};
use crate::{AlgebraicType, ProductType, SpacetimeType};
use derive_more::Display;
use spacetimedb_primitives::*;
use spacetimedb_sats::raw_identifier::RawIdentifier;
pub use crate::ModuleDefBuilder as RawModuleDefV8Builder;
pub use crate::RawModuleDefV8;
pub const SEQUENCE_ALLOCATION_STEP: i128 = 4096;
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, SpacetimeType)]
#[sats(crate = crate)]
pub struct RawSequenceDefV8 {
pub sequence_name: RawIdentifier,
pub col_pos: ColId,
pub increment: i128,
pub start: Option<i128>,
pub min_value: Option<i128>,
pub max_value: Option<i128>,
pub allocated: i128,
}
impl RawSequenceDefV8 {
pub fn for_column(table: &str, column_or_name: &str, col_pos: ColId) -> Self {
let seq_name = column_or_name.trim_start_matches(&format!("ct_{table}_"));
RawSequenceDefV8 {
sequence_name: RawIdentifier::new(format!("seq_{table}_{seq_name}")),
col_pos,
increment: 1,
start: None,
min_value: None,
max_value: None,
allocated: 0,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Display, SpacetimeType)]
#[sats(crate = crate)]
pub enum IndexType {
BTree = 0,
Hash = 1,
}
impl From<IndexType> for u8 {
fn from(value: IndexType) -> Self {
value as u8
}
}
impl TryFrom<u8> for IndexType {
type Error = ();
fn try_from(v: u8) -> Result<Self, Self::Error> {
match v {
0 => Ok(IndexType::BTree),
1 => Ok(IndexType::Hash),
_ => Err(()),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, SpacetimeType)]
#[sats(crate = crate)]
pub struct RawIndexDefV8 {
pub index_name: RawIdentifier,
pub is_unique: bool,
pub index_type: IndexType,
pub columns: ColList,
}
impl RawIndexDefV8 {
pub fn btree(index_name: RawIdentifier, columns: impl Into<ColList>, is_unique: bool) -> Self {
Self {
columns: columns.into(),
index_name,
is_unique,
index_type: IndexType::BTree,
}
}
pub fn for_column(table: &str, index_or_name: &str, columns: impl Into<ColList>, is_unique: bool) -> Self {
let unique = if is_unique { "unique" } else { "non_unique" };
let name = index_or_name.trim_start_matches(&format!("ct_{table}_"));
let name = if name.ends_with(&unique) {
format!("idx_{table}_{name}")
} else {
format!("idx_{table}_{name}_{unique}")
};
Self::btree(RawIdentifier::new(name), columns, is_unique)
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, SpacetimeType)]
#[sats(crate = crate)]
pub struct RawColumnDefV8 {
pub col_name: RawIdentifier,
pub col_type: AlgebraicType,
}
impl RawColumnDefV8 {
pub fn from_product_type(value: ProductType) -> Vec<RawColumnDefV8> {
Vec::from(value.elements)
.into_iter()
.enumerate()
.map(|(pos, col)| {
let col_name = if let Some(name) = col.name {
name
} else {
RawIdentifier::new(format!("col_{pos}"))
};
RawColumnDefV8 {
col_name,
col_type: col.algebraic_type,
}
})
.collect()
}
}
impl RawColumnDefV8 {
pub fn sys(field_name: &str, col_type: AlgebraicType) -> Self {
Self {
col_name: RawIdentifier::new(field_name),
col_type,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, SpacetimeType)]
#[sats(crate = crate)]
pub struct RawConstraintDefV8 {
pub constraint_name: RawIdentifier,
pub constraints: Constraints,
pub columns: ColList,
}
impl RawConstraintDefV8 {
pub fn new(constraint_name: RawIdentifier, constraints: Constraints, columns: impl Into<ColList>) -> Self {
Self {
constraint_name,
constraints,
columns: columns.into(),
}
}
pub fn for_column(
table: &str,
column_or_name: &str,
constraints: Constraints,
columns: impl Into<ColList>,
) -> Self {
let name = column_or_name.trim_start_matches(&format!("idx_{table}_"));
let kind_name = format!("{:?}", constraints.kind()).to_lowercase();
let name = if name.ends_with(&kind_name) {
format!("ct_{table}_{name}")
} else {
format!("ct_{table}_{name}_{kind_name}")
};
Self::new(RawIdentifier::new(name), constraints, columns)
}
}
pub fn generate_cols_name<'a>(columns: &ColList, col_name: impl Fn(ColId) -> Option<&'a str>) -> String {
let mut column_name = Vec::with_capacity(columns.len() as usize);
column_name.extend(columns.iter().filter_map(col_name));
column_name.join("_")
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, SpacetimeType)]
#[sats(crate = crate)]
pub struct RawTableDefV8 {
pub table_name: RawIdentifier,
pub columns: Vec<RawColumnDefV8>,
pub indexes: Vec<RawIndexDefV8>,
pub constraints: Vec<RawConstraintDefV8>,
pub sequences: Vec<RawSequenceDefV8>,
pub table_type: StTableType,
pub table_access: StAccess,
pub scheduled: Option<RawIdentifier>,
}
impl RawTableDefV8 {
pub fn new(table_name: RawIdentifier, columns: Vec<RawColumnDefV8>) -> Self {
Self {
table_name,
columns,
indexes: vec![],
constraints: vec![],
sequences: vec![],
table_type: StTableType::User,
table_access: StAccess::Public,
scheduled: None,
}
}
#[cfg(feature = "test")]
pub fn new_for_tests(table_name: impl Into<RawIdentifier>, columns: ProductType) -> Self {
Self::new(table_name.into(), RawColumnDefV8::from_product_type(columns))
}
pub fn with_type(self, table_type: StTableType) -> Self {
let mut x = self;
x.table_type = table_type;
x
}
pub fn with_access(self, table_access: StAccess) -> Self {
let mut x = self;
x.table_access = table_access;
x
}
pub fn with_constraints(self, constraints: Vec<RawConstraintDefV8>) -> Self {
let mut x = self;
x.constraints = constraints;
x
}
fn generate_cols_name(&self, columns: &ColList) -> String {
generate_cols_name(columns, |p| self.get_column(p.idx()).map(|c| &*c.col_name))
}
pub fn with_column_constraint(mut self, kind: Constraints, columns: impl Into<ColList>) -> Self {
self.constraints.push(self.gen_constraint_def(kind, columns));
self
}
pub fn gen_constraint_def(&self, kind: Constraints, columns: impl Into<ColList>) -> RawConstraintDefV8 {
let columns = columns.into();
RawConstraintDefV8::for_column(&self.table_name, &self.generate_cols_name(&columns), kind, columns)
}
pub fn with_indexes(self, indexes: Vec<RawIndexDefV8>) -> Self {
let mut x = self;
x.indexes = indexes;
x
}
pub fn with_column_index(self, columns: impl Into<ColList>, is_unique: bool) -> Self {
let mut x = self;
let columns = columns.into();
x.indexes.push(RawIndexDefV8::for_column(
&x.table_name,
&x.generate_cols_name(&columns),
columns,
is_unique,
));
x
}
pub fn with_sequences(self, sequences: Vec<RawSequenceDefV8>) -> Self {
let mut x = self;
x.sequences = sequences;
x
}
pub fn with_column_sequence(self, columns: ColId) -> Self {
let mut x = self;
x.sequences.push(RawSequenceDefV8::for_column(
&x.table_name,
&x.generate_cols_name(&ColList::new(columns)),
columns,
));
x
}
pub fn with_scheduled(mut self, scheduled: Option<RawIdentifier>) -> Self {
self.scheduled = scheduled;
self
}
pub fn from_product(table_name: RawIdentifier, row: ProductType) -> Self {
Self::new(
table_name,
Vec::from(row.elements)
.into_iter()
.enumerate()
.map(|(col_pos, e)| RawColumnDefV8 {
col_name: e.name.unwrap_or_else(|| RawIdentifier::new(format!("col_{col_pos}"))),
col_type: e.algebraic_type,
})
.collect::<Vec<_>>(),
)
}
pub fn get_column(&self, pos: usize) -> Option<&RawColumnDefV8> {
self.columns.get(pos)
}
pub fn get_column_by_name(&self, col_name: &str) -> Option<&RawColumnDefV8> {
self.columns.iter().find(|x| &*x.col_name == col_name)
}
}