database_mcp/db/
identifier.rs1use crate::error::AppError;
4
5pub fn validate_identifier(name: &str) -> Result<(), AppError> {
12 if name.is_empty() || name.chars().all(char::is_whitespace) {
13 return Err(AppError::InvalidIdentifier(name.to_string()));
14 }
15 if name.chars().any(char::is_control) {
16 return Err(AppError::InvalidIdentifier(name.to_string()));
17 }
18 Ok(())
19}
20
21#[cfg(test)]
22mod tests {
23 use super::*;
24
25 #[test]
26 fn accepts_standard_names() {
27 assert!(validate_identifier("users").is_ok());
28 assert!(validate_identifier("my_table").is_ok());
29 assert!(validate_identifier("DB_123").is_ok());
30 }
31
32 #[test]
33 fn accepts_hyphenated_names() {
34 assert!(validate_identifier("eu-docker").is_ok());
35 assert!(validate_identifier("access-logs").is_ok());
36 }
37
38 #[test]
39 fn accepts_special_chars() {
40 assert!(validate_identifier("my.db").is_ok());
41 assert!(validate_identifier("123db").is_ok());
42 assert!(validate_identifier("café").is_ok());
43 assert!(validate_identifier("a b").is_ok());
44 }
45
46 #[test]
47 fn rejects_empty() {
48 assert!(validate_identifier("").is_err());
49 }
50
51 #[test]
52 fn rejects_whitespace_only() {
53 assert!(validate_identifier(" ").is_err());
54 assert!(validate_identifier("\t").is_err());
55 }
56
57 #[test]
58 fn rejects_control_chars() {
59 assert!(validate_identifier("test\x00db").is_err());
60 assert!(validate_identifier("test\ndb").is_err());
61 assert!(validate_identifier("test\x1Fdb").is_err());
62 }
63}