1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use use_db_name::{ColumnName, TableName};
7
8#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
10pub enum KeyKind {
11 #[default]
13 Primary,
14 Foreign,
16 Unique,
18 Candidate,
20 Composite,
22}
23
24#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
26pub struct KeyColumn {
27 table: Option<TableName>,
28 column: ColumnName,
29}
30
31impl KeyColumn {
32 #[must_use]
34 pub const fn new(column: ColumnName) -> Self {
35 Self {
36 table: None,
37 column,
38 }
39 }
40
41 #[must_use]
43 pub fn with_table(mut self, table: TableName) -> Self {
44 self.table = Some(table);
45 self
46 }
47
48 #[must_use]
50 pub const fn table(&self) -> Option<&TableName> {
51 self.table.as_ref()
52 }
53
54 #[must_use]
56 pub const fn column(&self) -> &ColumnName {
57 &self.column
58 }
59}
60
61#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
63pub struct PrimaryKey {
64 table: TableName,
65 column: ColumnName,
66}
67
68impl PrimaryKey {
69 #[must_use]
71 pub const fn new(table: TableName, column: ColumnName) -> Self {
72 Self { table, column }
73 }
74
75 #[must_use]
77 pub const fn table(&self) -> &TableName {
78 &self.table
79 }
80
81 #[must_use]
83 pub const fn column(&self) -> &ColumnName {
84 &self.column
85 }
86}
87
88#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
90pub struct ForeignKey {
91 source: KeyColumn,
92 target: KeyColumn,
93}
94
95impl ForeignKey {
96 #[must_use]
98 pub const fn new(source: KeyColumn, target: KeyColumn) -> Self {
99 Self { source, target }
100 }
101
102 #[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 #[must_use]
118 pub const fn source(&self) -> &KeyColumn {
119 &self.source
120 }
121
122 #[must_use]
124 pub const fn target(&self) -> &KeyColumn {
125 &self.target
126 }
127}
128
129#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
131pub struct UniqueKey {
132 columns: Vec<KeyColumn>,
133}
134
135impl UniqueKey {
136 #[must_use]
138 pub fn new(columns: Vec<KeyColumn>) -> Option<Self> {
139 (!columns.is_empty()).then_some(Self { columns })
140 }
141
142 #[must_use]
144 pub fn columns(&self) -> &[KeyColumn] {
145 &self.columns
146 }
147}
148
149pub type CompositeKey = UniqueKey;
151
152pub 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}