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