vespertide_core/schema/table/
validation.rs1use crate::schema::names::{ColumnName, TableName};
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub enum TableValidationError {
5 DuplicateColumnName {
6 table: TableName,
7 column: ColumnName,
8 },
9 DuplicateIndexColumn {
10 index_name: String,
11 column_name: String,
12 },
13 InvalidForeignKeyFormat {
14 column_name: String,
15 value: String,
16 },
17 InvariantViolation {
19 context: String,
20 },
21}
22
23impl std::fmt::Display for TableValidationError {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 match self {
26 TableValidationError::DuplicateColumnName { table, column } => {
27 write!(f, "table '{table}' has duplicate column name '{column}'")
28 }
29 TableValidationError::DuplicateIndexColumn {
30 index_name,
31 column_name,
32 } => {
33 write!(
34 f,
35 "Duplicate index '{index_name}' on column '{column_name}': the same index name cannot be applied to the same column multiple times"
36 )
37 }
38 TableValidationError::InvalidForeignKeyFormat { column_name, value } => {
39 write!(
40 f,
41 "Invalid foreign key format '{value}' on column '{column_name}': expected 'table.column' format"
42 )
43 }
44 TableValidationError::InvariantViolation { context } => {
45 write!(
46 f,
47 "internal table normalization invariant violated: {context}"
48 )
49 }
50 }
51 }
52}
53
54impl std::error::Error for TableValidationError {}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
62 use rstest::rstest;
63
64 #[rstest]
65 #[case::duplicate_index(
66 TableValidationError::DuplicateIndexColumn {
67 index_name: "ix_user__email".into(),
68 column_name: "email".into(),
69 },
70 "Duplicate index 'ix_user__email' on column 'email'",
71 )]
72 #[case::invalid_fk(
73 TableValidationError::InvalidForeignKeyFormat {
74 column_name: "author_id".into(),
75 value: "broken".into(),
76 },
77 "Invalid foreign key format 'broken' on column 'author_id'",
78 )]
79 fn display_emits_descriptive_messages(#[case] err: TableValidationError, #[case] needle: &str) {
80 let rendered = err.to_string();
81 assert!(
82 rendered.contains(needle),
83 "expected `{needle}` to appear in `{rendered}`"
84 );
85 }
86
87 #[test]
88 fn display_duplicate_column_name() {
89 let err = TableValidationError::DuplicateColumnName {
92 table: "user".into(),
93 column: "email".into(),
94 };
95 assert_eq!(
96 err.to_string(),
97 "table 'user' has duplicate column name 'email'",
98 );
99 }
100
101 #[test]
102 fn display_invariant_violation() {
103 let err = TableValidationError::InvariantViolation {
104 context: "normalize() collapsed two unique constraints".into(),
105 };
106 assert!(err.to_string().contains("normalize()"));
107 }
108}