Skip to main content

use_db_key/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4//! Key metadata primitives for `RustUse`.
5
6use use_db_name::{ColumnName, TableName};
7
8/// Broad database key kind.
9#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
10pub enum KeyKind {
11    /// Primary key metadata.
12    #[default]
13    Primary,
14    /// Foreign key metadata.
15    Foreign,
16    /// Unique key metadata.
17    Unique,
18    /// Candidate key metadata.
19    Candidate,
20    /// Composite key metadata.
21    Composite,
22}
23
24/// A key column reference.
25#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
26pub struct KeyColumn {
27    table: Option<TableName>,
28    column: ColumnName,
29}
30
31impl KeyColumn {
32    /// Creates an unqualified key column.
33    #[must_use]
34    pub const fn new(column: ColumnName) -> Self {
35        Self {
36            table: None,
37            column,
38        }
39    }
40
41    /// Adds a table qualifier.
42    #[must_use]
43    pub fn with_table(mut self, table: TableName) -> Self {
44        self.table = Some(table);
45        self
46    }
47
48    /// Returns the optional table qualifier.
49    #[must_use]
50    pub const fn table(&self) -> Option<&TableName> {
51        self.table.as_ref()
52    }
53
54    /// Returns the column name.
55    #[must_use]
56    pub const fn column(&self) -> &ColumnName {
57        &self.column
58    }
59}
60
61/// Primary key metadata.
62#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
63pub struct PrimaryKey {
64    table: TableName,
65    column: ColumnName,
66}
67
68impl PrimaryKey {
69    /// Creates single-column primary key metadata.
70    #[must_use]
71    pub const fn new(table: TableName, column: ColumnName) -> Self {
72        Self { table, column }
73    }
74
75    /// Returns the table name.
76    #[must_use]
77    pub const fn table(&self) -> &TableName {
78        &self.table
79    }
80
81    /// Returns the column name.
82    #[must_use]
83    pub const fn column(&self) -> &ColumnName {
84        &self.column
85    }
86}
87
88/// Foreign key metadata.
89#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
90pub struct ForeignKey {
91    source: KeyColumn,
92    target: KeyColumn,
93}
94
95impl ForeignKey {
96    /// Creates foreign key metadata.
97    #[must_use]
98    pub const fn new(source: KeyColumn, target: KeyColumn) -> Self {
99        Self { source, target }
100    }
101
102    /// Creates foreign key metadata from table and column names.
103    #[must_use]
104    pub fn from_columns(
105        table: TableName,
106        column: ColumnName,
107        referenced_table: TableName,
108        referenced_column: ColumnName,
109    ) -> Self {
110        Self::new(
111            KeyColumn::new(column).with_table(table),
112            KeyColumn::new(referenced_column).with_table(referenced_table),
113        )
114    }
115
116    /// Returns the source column.
117    #[must_use]
118    pub const fn source(&self) -> &KeyColumn {
119        &self.source
120    }
121
122    /// Returns the target column.
123    #[must_use]
124    pub const fn target(&self) -> &KeyColumn {
125        &self.target
126    }
127}
128
129/// Unique key metadata.
130#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
131pub struct UniqueKey {
132    columns: Vec<KeyColumn>,
133}
134
135impl UniqueKey {
136    /// Creates unique key metadata when at least one column is supplied.
137    #[must_use]
138    pub fn new(columns: Vec<KeyColumn>) -> Option<Self> {
139        (!columns.is_empty()).then_some(Self { columns })
140    }
141
142    /// Returns the key columns.
143    #[must_use]
144    pub fn columns(&self) -> &[KeyColumn] {
145        &self.columns
146    }
147}
148
149/// Composite key metadata.
150pub type CompositeKey = UniqueKey;
151
152/// Candidate key metadata.
153pub type CandidateKey = UniqueKey;
154
155#[cfg(test)]
156mod tests {
157    use super::{ForeignKey, KeyColumn, PrimaryKey, UniqueKey};
158    use use_db_name::{ColumnName, TableName};
159
160    #[test]
161    fn stores_key_metadata() -> Result<(), Box<dyn std::error::Error>> {
162        let table = TableName::new("users")?;
163        let column = ColumnName::new("id")?;
164        let primary_key = PrimaryKey::new(table.clone(), column.clone());
165        let foreign_key = ForeignKey::from_columns(
166            TableName::new("posts")?,
167            ColumnName::new("user_id")?,
168            table.clone(),
169            column.clone(),
170        );
171        let unique = UniqueKey::new(vec![
172            KeyColumn::new(column.clone()).with_table(table.clone()),
173        ])
174        .expect("non-empty key");
175
176        assert_eq!(primary_key.table(), &table);
177        assert_eq!(primary_key.column(), &column);
178        assert_eq!(foreign_key.target().table().expect("target table"), &table);
179        assert_eq!(unique.columns()[0].column(), &column);
180        assert_eq!(UniqueKey::new(Vec::new()), None);
181        Ok(())
182    }
183}