hyperdb_compile_check/
diagnostic.rs1#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum ValidationError {
12 StructNotRegistered {
15 struct_name: String,
17 },
18
19 TablesNotRegistered {
21 tables: Vec<String>,
23 },
24
25 MissingColumns {
28 struct_name: String,
30 missing: Vec<String>,
32 },
33
34 UnknownColumn {
38 column: String,
40 },
41
42 SqlSyntaxError {
44 message: String,
46 },
47
48 HyperError {
50 message: String,
52 },
53}
54
55impl ValidationError {
56 pub fn to_diagnostic(&self) -> String {
59 match self {
60 Self::StructNotRegistered { struct_name } => format!(
61 "type `{struct_name}` must `#[derive(Table)]` with `#[hyperdb(register)]` \
62 to be used with `query_as!`"
63 ),
64 Self::TablesNotRegistered { tables } => {
65 if tables.len() == 1 {
66 format!(
67 "table {:?} is not registered; did you forget \
68 `#[derive(Table)] #[hyperdb(register)]` on the struct that maps to it?",
69 tables[0]
70 )
71 } else {
72 format!(
73 "tables {tables:?} are not registered; add \
74 `#[derive(Table)] #[hyperdb(register)]` to the structs that map to them"
75 )
76 }
77 }
78 Self::MissingColumns {
79 struct_name,
80 missing,
81 } => {
82 if missing.len() == 1 {
83 format!(
84 "`{struct_name}` requires column {:?} but the query does not project it; \
85 add it to the SELECT list or remove the field from `{struct_name}`",
86 missing[0]
87 )
88 } else {
89 format!(
90 "`{struct_name}` requires columns {missing:?} but the query does not \
91 project them; add them to the SELECT list or remove the fields from \
92 `{struct_name}`"
93 )
94 }
95 }
96 Self::UnknownColumn { column } => format!(
97 "column {column:?} does not exist on any table in the query; \
98 check for a typo or a renamed/dropped column"
99 ),
100 Self::SqlSyntaxError { message } => {
101 format!("SQL syntax error: {message}")
102 }
103 Self::HyperError { message } => {
104 format!("Hyper validation error: {message}")
105 }
106 }
107 }
108}
109
110impl std::fmt::Display for ValidationError {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 f.write_str(&self.to_diagnostic())
113 }
114}
115
116impl std::error::Error for ValidationError {}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn struct_not_registered_message() {
124 let e = ValidationError::StructNotRegistered {
125 struct_name: "User".into(),
126 };
127 assert!(e.to_diagnostic().contains("User"));
128 assert!(e.to_diagnostic().contains("derive(Table)"));
129 }
130
131 #[test]
132 fn single_missing_column_message() {
133 let e = ValidationError::MissingColumns {
134 struct_name: "User".into(),
135 missing: vec!["email".into()],
136 };
137 let msg = e.to_diagnostic();
138 assert!(msg.contains("email"), "message: {msg}");
139 assert!(msg.contains("User"), "message: {msg}");
140 }
141
142 #[test]
143 fn multi_missing_columns_message() {
144 let e = ValidationError::MissingColumns {
145 struct_name: "User".into(),
146 missing: vec!["email".into(), "name".into()],
147 };
148 let msg = e.to_diagnostic();
149 assert!(msg.contains("email"), "message: {msg}");
150 assert!(msg.contains("name"), "message: {msg}");
151 }
152
153 #[test]
154 fn single_table_not_registered_message() {
155 let e = ValidationError::TablesNotRegistered {
156 tables: vec!["ghosts".into()],
157 };
158 assert!(e.to_diagnostic().contains("ghosts"));
159 assert!(e.to_diagnostic().contains("derive(Table)"));
160 }
161
162 #[test]
163 fn unknown_column_message() {
164 let e = ValidationError::UnknownColumn { column: "d".into() };
165 let msg = e.to_diagnostic();
166 assert!(msg.contains("\"d\""), "message: {msg}");
167 assert!(msg.contains("does not exist"), "message: {msg}");
168 assert!(msg.contains("typo"), "message: {msg}");
169 }
170}