#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
use use_db_name::{ColumnName, TableName};
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum KeyKind {
#[default]
Primary,
Foreign,
Unique,
Candidate,
Composite,
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct KeyColumn {
table: Option<TableName>,
column: ColumnName,
}
impl KeyColumn {
#[must_use]
pub const fn new(column: ColumnName) -> Self {
Self {
table: None,
column,
}
}
#[must_use]
pub fn with_table(mut self, table: TableName) -> Self {
self.table = Some(table);
self
}
#[must_use]
pub const fn table(&self) -> Option<&TableName> {
self.table.as_ref()
}
#[must_use]
pub const fn column(&self) -> &ColumnName {
&self.column
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct PrimaryKey {
table: TableName,
column: ColumnName,
}
impl PrimaryKey {
#[must_use]
pub const fn new(table: TableName, column: ColumnName) -> Self {
Self { table, column }
}
#[must_use]
pub const fn table(&self) -> &TableName {
&self.table
}
#[must_use]
pub const fn column(&self) -> &ColumnName {
&self.column
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ForeignKey {
source: KeyColumn,
target: KeyColumn,
}
impl ForeignKey {
#[must_use]
pub const fn new(source: KeyColumn, target: KeyColumn) -> Self {
Self { source, target }
}
#[must_use]
pub fn from_columns(
table: TableName,
column: ColumnName,
referenced_table: TableName,
referenced_column: ColumnName,
) -> Self {
Self::new(
KeyColumn::new(column).with_table(table),
KeyColumn::new(referenced_column).with_table(referenced_table),
)
}
#[must_use]
pub const fn source(&self) -> &KeyColumn {
&self.source
}
#[must_use]
pub const fn target(&self) -> &KeyColumn {
&self.target
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct UniqueKey {
columns: Vec<KeyColumn>,
}
impl UniqueKey {
#[must_use]
pub fn new(columns: Vec<KeyColumn>) -> Option<Self> {
(!columns.is_empty()).then_some(Self { columns })
}
#[must_use]
pub fn columns(&self) -> &[KeyColumn] {
&self.columns
}
}
pub type CompositeKey = UniqueKey;
pub type CandidateKey = UniqueKey;
#[cfg(test)]
mod tests {
use super::{ForeignKey, KeyColumn, PrimaryKey, UniqueKey};
use use_db_name::{ColumnName, TableName};
#[test]
fn stores_key_metadata() -> Result<(), Box<dyn std::error::Error>> {
let table = TableName::new("users")?;
let column = ColumnName::new("id")?;
let primary_key = PrimaryKey::new(table.clone(), column.clone());
let foreign_key = ForeignKey::from_columns(
TableName::new("posts")?,
ColumnName::new("user_id")?,
table.clone(),
column.clone(),
);
let unique = UniqueKey::new(vec![
KeyColumn::new(column.clone()).with_table(table.clone()),
])
.expect("non-empty key");
assert_eq!(primary_key.table(), &table);
assert_eq!(primary_key.column(), &column);
assert_eq!(foreign_key.target().table().expect("target table"), &table);
assert_eq!(unique.columns()[0].column(), &column);
assert_eq!(UniqueKey::new(Vec::new()), None);
Ok(())
}
}