use crate::errors::MCPError;
const MAX_IDENTIFIER_LEN: usize = 255;
pub fn validate_identifier(name: &str, label: &str) -> Result<(), MCPError> {
if name.is_empty() {
return Err(MCPError::InvalidParams(format!(
"'{label}' must not be empty"
)));
}
if name.len() > MAX_IDENTIFIER_LEN {
return Err(MCPError::InvalidParams(format!(
"'{label}' exceeds maximum length of {MAX_IDENTIFIER_LEN} characters (got {})",
name.len()
)));
}
for ch in name.chars() {
if !ch.is_alphanumeric() && ch != '_' {
return Err(MCPError::InvalidParams(format!(
"'{label}' contains invalid character '{ch}' — only alphanumeric and underscore allowed"
)));
}
}
if name.starts_with(|c: char| c.is_ascii_digit()) {
return Err(MCPError::InvalidParams(format!(
"'{label}' must not start with a digit"
)));
}
Ok(())
}
pub fn quote_identifier(name: &str) -> String {
quote_ident(name)
}
pub fn quote_ident(name: &str) -> String {
let mut out = String::with_capacity(name.len() + 2);
out.push('"');
for ch in name.chars() {
if ch == '"' {
out.push_str("\"\"");
} else {
out.push(ch);
}
}
out.push('"');
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_identifier() {
assert!(validate_identifier("users", "table").is_ok());
assert!(validate_identifier("user_orders_2024", "table").is_ok());
}
#[test]
fn test_empty_identifier() {
let err = validate_identifier("", "table").unwrap_err();
assert!(err.to_string().contains("must not be empty"));
}
#[test]
fn test_too_long_identifier() {
let long = "a".repeat(256);
let err = validate_identifier(&long, "table").unwrap_err();
assert!(err.to_string().contains("exceeds maximum length"));
}
#[test]
fn test_invalid_char_identifier() {
let err = validate_identifier("users; DROP TABLE", "table").unwrap_err();
assert!(err.to_string().contains("invalid character"));
}
#[test]
fn test_digit_start_identifier() {
let err = validate_identifier("1users", "table").unwrap_err();
assert!(err.to_string().contains("must not start with a digit"));
}
#[test]
fn test_quote_identifier() {
assert_eq!(quote_identifier("users"), "\"users\"");
assert_eq!(quote_identifier("order_items"), "\"order_items\"");
}
}