1use std::borrow::Cow;
15
16use core::fmt::{Display, Formatter};
17use core::str::FromStr;
18
19pub const MAX_LENGTH: usize = 63;
21
22const fn validate(input: &str) -> Option<ParseError> {
24 if input.is_empty() {
25 return Some(ParseError::Empty);
26 }
27
28 if input.len() > MAX_LENGTH {
29 return Some(ParseError::TooLong);
30 }
31
32 let bytes = input.as_bytes();
33 let mut index = 0;
34
35 while index < bytes.len() {
36 if bytes[index] == 0 {
37 return Some(ParseError::ContainsNul);
38 }
39 index += 1;
40 }
41
42 None
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
50#[serde(try_from = "String")]
51struct Identifier(Cow<'static, str>);
52
53impl TryFrom<String> for Identifier {
54 type Error = ParseError;
55
56 fn try_from(value: String) -> Result<Self, Self::Error> {
57 value.parse()
58 }
59}
60
61impl Identifier {
62 #[must_use]
68 const fn from_static_or_panic(input: &'static str) -> Self {
69 match validate(input) {
70 Some(error) => panic!("{}", error.message()),
71 None => Self(Cow::Borrowed(input)),
72 }
73 }
74
75 #[must_use]
77 fn as_str(&self) -> &str {
78 &self.0
79 }
80}
81
82impl Display for Identifier {
83 fn fmt(&self, formatter: &mut Formatter<'_>) -> core::fmt::Result {
84 write!(formatter, "{}", self.0)
85 }
86}
87
88impl AsRef<str> for Identifier {
89 fn as_ref(&self) -> &str {
90 &self.0
91 }
92}
93
94impl FromStr for Identifier {
95 type Err = ParseError;
96
97 fn from_str(input: &str) -> Result<Self, Self::Err> {
98 match validate(input) {
99 Some(error) => Err(error),
100 None => Ok(Self(Cow::Owned(input.to_owned()))),
101 }
102 }
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub enum ParseError {
108 Empty,
110
111 TooLong,
113
114 ContainsNul,
116}
117
118impl ParseError {
119 #[must_use]
121 pub const fn message(&self) -> &'static str {
122 match self {
123 Self::Empty => "identifier cannot be empty",
124 Self::TooLong => "identifier exceeds maximum length",
125 Self::ContainsNul => "identifier cannot contain NUL bytes",
126 }
127 }
128}
129
130impl Display for ParseError {
131 fn fmt(&self, formatter: &mut Formatter<'_>) -> core::fmt::Result {
132 write!(formatter, "{}", self.message())
133 }
134}
135
136impl std::error::Error for ParseError {}
137
138macro_rules! define_identifier_type {
140 ($(#[$meta:meta])* $name:ident, $test_mod:ident) => {
141 $(#[$meta])*
142 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
143 #[serde(try_from = "String")]
144 pub struct $name(Identifier);
145
146 impl TryFrom<String> for $name {
147 type Error = ParseError;
148
149 fn try_from(value: String) -> Result<Self, Self::Error> {
150 value.parse()
151 }
152 }
153
154 impl $name {
155 #[must_use]
161 pub const fn from_static_or_panic(input: &'static str) -> Self {
162 Self(Identifier::from_static_or_panic(input))
163 }
164
165 #[must_use]
167 pub fn as_str(&self) -> &str {
168 self.0.as_str()
169 }
170 }
171
172 impl Display for $name {
173 fn fmt(&self, formatter: &mut Formatter<'_>) -> core::fmt::Result {
174 write!(formatter, "{}", self.0)
175 }
176 }
177
178 impl AsRef<str> for $name {
179 fn as_ref(&self) -> &str {
180 self.0.as_ref()
181 }
182 }
183
184 impl FromStr for $name {
185 type Err = ParseError;
186
187 fn from_str(input: &str) -> Result<Self, Self::Err> {
188 Identifier::from_str(input).map(Self)
189 }
190 }
191
192 #[cfg(test)]
193 mod $test_mod {
194 use super::*;
195
196 #[test]
197 fn parse_valid() {
198 let value: $name = "test".parse().unwrap();
199 assert_eq!(value.to_string(), "test");
200 }
201
202 #[test]
203 fn parse_valid_with_space() {
204 let value: $name = "test value".parse().unwrap();
205 assert_eq!(value.to_string(), "test value");
206 }
207
208 #[test]
209 fn parse_empty_fails() {
210 let result: Result<$name, _> = "".parse();
211 assert!(matches!(result, Err(ParseError::Empty)));
212 }
213
214 #[test]
215 fn parse_contains_nul_fails() {
216 let result: Result<$name, _> = "test\0value".parse();
217 assert!(matches!(result, Err(ParseError::ContainsNul)));
218 }
219
220 #[test]
221 fn parse_too_long_fails() {
222 let input = "a".repeat(MAX_LENGTH + 1);
223 let result: Result<$name, _> = input.parse();
224 assert!(matches!(result, Err(ParseError::TooLong)));
225 }
226 }
227 };
228}
229
230define_identifier_type!(
231 Table,
233 table
234);
235
236define_identifier_type!(
237 Schema,
239 schema
240);
241
242impl Schema {
243 pub const PUBLIC: Self = Self::from_static_or_panic("public");
245}
246
247define_identifier_type!(
248 Column,
250 column
251);
252
253define_identifier_type!(
254 Index,
256 index
257);
258
259define_identifier_type!(
260 Constraint,
264 constraint
265);
266
267define_identifier_type!(
268 Extension,
270 extension
271);
272
273define_identifier_type!(
274 Sequence,
276 sequence
277);
278
279define_identifier_type!(
280 Function,
282 function
283);
284
285define_identifier_type!(
286 Trigger,
288 trigger
289);
290
291define_identifier_type!(
292 Domain,
294 domain
295);
296
297define_identifier_type!(
298 Type,
302 r#type
303);
304
305define_identifier_type!(
306 View,
308 view
309);
310
311define_identifier_type!(
312 Relation,
317 relation
318);
319
320impl From<Table> for Relation {
321 fn from(table: Table) -> Self {
322 Self(table.0)
323 }
324}
325
326impl From<View> for Relation {
327 fn from(view: View) -> Self {
328 Self(view.0)
329 }
330}
331
332define_identifier_type!(
333 MaterializedView,
335 materialized_view
336);
337
338impl From<MaterializedView> for Relation {
339 fn from(materialized_view: MaterializedView) -> Self {
340 Self(materialized_view.0)
341 }
342}
343
344define_identifier_type!(
345 Operator,
347 operator
348);
349
350define_identifier_type!(
351 Aggregate,
353 aggregate
354);
355
356define_identifier_type!(
357 Collation,
359 collation
360);
361
362define_identifier_type!(
363 Tablespace,
365 tablespace
366);
367
368define_identifier_type!(
369 Policy,
371 policy
372);
373
374define_identifier_type!(
375 Rule,
377 rule
378);
379
380define_identifier_type!(
381 Publication,
383 publication
384);
385
386define_identifier_type!(
387 Subscription,
389 subscription
390);
391
392define_identifier_type!(
393 ForeignServer,
395 foreign_server
396);
397
398define_identifier_type!(
399 ForeignDataWrapper,
401 foreign_data_wrapper
402);
403
404define_identifier_type!(
405 ForeignTable,
407 foreign_table
408);
409
410define_identifier_type!(
411 EventTrigger,
413 event_trigger
414);
415
416define_identifier_type!(
417 Language,
419 language
420);
421
422define_identifier_type!(
423 TextSearchConfiguration,
425 text_search_configuration
426);
427
428define_identifier_type!(
429 TextSearchDictionary,
431 text_search_dictionary
432);
433
434define_identifier_type!(
435 Conversion,
437 conversion
438);
439
440define_identifier_type!(
441 OperatorClass,
443 operator_class
444);
445
446define_identifier_type!(
447 OperatorFamily,
449 operator_family
450);
451
452define_identifier_type!(
453 AccessMethod,
455 access_method
456);
457
458define_identifier_type!(
459 StatisticsObject,
461 statistics_object
462);
463
464define_identifier_type!(
465 Database,
467 database
468);
469
470impl Database {
471 pub const POSTGRES: Self = Self::from_static_or_panic("postgres");
473}
474
475define_identifier_type!(
476 Role,
480 role
481);
482
483impl Role {
484 pub const POSTGRES: Self = Self::from_static_or_panic("postgres");
486}
487
488pub type User = Role;
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496
497 mod identifier {
498 use super::*;
499
500 #[test]
501 fn parse_valid_simple() {
502 let identifier: Identifier = "users".parse().unwrap();
503 assert_eq!(identifier.to_string(), "users");
504 }
505
506 #[test]
507 fn parse_valid_with_space() {
508 let identifier: Identifier = "my table".parse().unwrap();
509 assert_eq!(identifier.to_string(), "my table");
510 }
511
512 #[test]
513 fn parse_valid_with_special_chars() {
514 let identifier: Identifier = "my-table.name".parse().unwrap();
515 assert_eq!(identifier.to_string(), "my-table.name");
516 }
517
518 #[test]
519 fn parse_valid_starting_with_digit() {
520 let identifier: Identifier = "1table".parse().unwrap();
521 assert_eq!(identifier.to_string(), "1table");
522 }
523
524 #[test]
525 fn parse_valid_max_length() {
526 let input = "a".repeat(MAX_LENGTH);
527 let identifier: Identifier = input.parse().unwrap();
528 assert_eq!(identifier.to_string(), input);
529 }
530
531 #[test]
532 fn parse_empty_fails() {
533 let result: Result<Identifier, _> = "".parse();
534 assert_eq!(result, Err(ParseError::Empty));
535 }
536
537 #[test]
538 fn parse_too_long_fails() {
539 let input = "a".repeat(MAX_LENGTH + 1);
540 let result: Result<Identifier, _> = input.parse();
541 assert_eq!(result, Err(ParseError::TooLong));
542 }
543
544 #[test]
545 fn parse_contains_nul_fails() {
546 let result: Result<Identifier, _> = "my\0table".parse();
547 assert_eq!(result, Err(ParseError::ContainsNul));
548 }
549 }
550}