systemprompt_database/admin/
identifier.rs1use std::fmt;
8
9use thiserror::Error;
10
11const MAX_IDENTIFIER_LEN: usize = 63;
12
13#[derive(Debug, Clone, Copy, Error)]
14pub enum IdentifierError {
15 #[error("identifier is empty")]
16 Empty,
17 #[error("identifier length {0} exceeds `PostgreSQL` limit of {MAX_IDENTIFIER_LEN}")]
18 TooLong(usize),
19 #[error("identifier must start with an ASCII letter or underscore")]
20 BadLead,
21 #[error("identifier contains invalid character {0:?}")]
22 InvalidChar(char),
23}
24
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct SafeIdentifier(String);
27
28impl SafeIdentifier {
29 pub fn parse(raw: &str) -> Result<Self, IdentifierError> {
30 if raw.is_empty() {
31 return Err(IdentifierError::Empty);
32 }
33 if raw.len() > MAX_IDENTIFIER_LEN {
34 return Err(IdentifierError::TooLong(raw.len()));
35 }
36 let mut chars = raw.chars();
37 let first = chars.next().ok_or(IdentifierError::Empty)?;
38 if !(first.is_ascii_alphabetic() || first == '_') {
39 return Err(IdentifierError::BadLead);
40 }
41 for c in chars {
42 if !(c.is_ascii_alphanumeric() || c == '_') {
43 return Err(IdentifierError::InvalidChar(c));
44 }
45 }
46 Ok(Self(raw.to_owned()))
47 }
48
49 pub fn as_str(&self) -> &str {
50 &self.0
51 }
52}
53
54impl fmt::Display for SafeIdentifier {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 f.write_str(&self.0)
57 }
58}