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}