Skip to main content

systemprompt_database/admin/
identifier.rs

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