Skip to main content

coil_data/
identifiers.rs

1use std::fmt;
2
3use crate::DataModelError;
4
5#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
6pub struct QueryField(String);
7
8impl QueryField {
9    pub fn new(value: impl Into<String>) -> Result<Self, DataModelError> {
10        Ok(Self(validate_token("query_field", value.into())?))
11    }
12
13    pub fn as_str(&self) -> &str {
14        &self.0
15    }
16}
17
18impl fmt::Display for QueryField {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        f.write_str(&self.0)
21    }
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub struct MigrationId(String);
26
27impl MigrationId {
28    pub fn new(value: impl Into<String>) -> Result<Self, DataModelError> {
29        Ok(Self(validate_token("migration_id", value.into())?))
30    }
31
32    pub fn as_str(&self) -> &str {
33        &self.0
34    }
35}
36
37impl fmt::Display for MigrationId {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        f.write_str(&self.0)
40    }
41}
42
43#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
44pub struct TableName(String);
45
46impl TableName {
47    pub fn new(value: impl Into<String>) -> Result<Self, DataModelError> {
48        Ok(Self(validate_token("table_name", value.into())?))
49    }
50
51    pub fn as_str(&self) -> &str {
52        &self.0
53    }
54}
55
56impl fmt::Display for TableName {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        f.write_str(&self.0)
59    }
60}
61
62pub(crate) fn validate_token(field: &'static str, value: String) -> Result<String, DataModelError> {
63    let trimmed = value.trim();
64    if trimmed.is_empty() {
65        return Err(DataModelError::EmptyField { field });
66    }
67
68    if trimmed
69        .chars()
70        .all(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '-' | '_' | '.' | ':'))
71    {
72        Ok(trimmed.to_string())
73    } else {
74        Err(DataModelError::InvalidToken {
75            field,
76            value: trimmed.to_string(),
77        })
78    }
79}
80
81pub(crate) fn require_non_empty(
82    field: &'static str,
83    value: String,
84) -> Result<String, DataModelError> {
85    let trimmed = value.trim();
86    if trimmed.is_empty() {
87        Err(DataModelError::EmptyField { field })
88    } else {
89        Ok(trimmed.to_string())
90    }
91}