uqa_sql/ast/types/
identity.rs1use super::ColumnType;
10
11const ENUM_PREFIX: &str = "enum#";
12const DOMAIN_PREFIX: &str = "domain#";
13const COMPOSITE_PREFIX: &str = "composite#";
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum UserTypeKind {
17 Enum,
18 Domain,
19 Composite,
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct UserTypeIdentity {
25 pub kind: UserTypeKind,
26 pub oid: u32,
27 pub dimensions: usize,
28}
29
30impl UserTypeIdentity {
31 #[must_use]
33 pub fn parse(name: &str) -> Option<Self> {
34 let mut element = name.trim();
35 let mut dimensions = 0usize;
36 while let Some(inner) = element.strip_suffix("[]") {
37 element = inner;
38 dimensions += 1;
39 }
40 let (kind, oid) = if let Some(oid) = element.strip_prefix(ENUM_PREFIX) {
41 (UserTypeKind::Enum, oid)
42 } else if let Some(oid) = element.strip_prefix(COMPOSITE_PREFIX) {
43 (UserTypeKind::Composite, oid)
44 } else {
45 (UserTypeKind::Domain, element.strip_prefix(DOMAIN_PREFIX)?)
46 };
47 if oid.is_empty() || !oid.bytes().all(|byte| byte.is_ascii_digit()) {
48 return None;
49 }
50 Some(Self {
51 kind,
52 oid: oid.parse().ok()?,
53 dimensions,
54 })
55 }
56}
57
58impl ColumnType {
59 #[must_use]
61 pub fn user_type_identity(&self) -> Option<String> {
62 match self {
63 ColumnType::Enum(reference) => Some(format!("{ENUM_PREFIX}{}", reference.oid)),
64 ColumnType::Composite(reference) => {
65 Some(format!("{COMPOSITE_PREFIX}{}", reference.oid))
66 }
67 ColumnType::Domain { oid, .. } => Some(format!("{DOMAIN_PREFIX}{oid}")),
68 ColumnType::Array(element) => element
69 .user_type_identity()
70 .map(|element| format!("{element}[]")),
71 _ => None,
72 }
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::{UserTypeIdentity, UserTypeKind};
79 use crate::ast::{ColumnType, EnumTypeReference};
80
81 #[test]
82 fn identities_round_trip_through_arrays() {
83 let mood = ColumnType::Enum(EnumTypeReference {
84 schema: "public".into(),
85 name: "mood".into(),
86 oid: 20_000,
87 array_oid: 20_001,
88 });
89 let array = ColumnType::Array(Box::new(ColumnType::Array(Box::new(mood))));
90 let identity = array.user_type_identity().unwrap();
91 assert_eq!(identity, "enum#20000[][]");
92 assert_eq!(
93 UserTypeIdentity::parse(&identity),
94 Some(UserTypeIdentity {
95 kind: UserTypeKind::Enum,
96 oid: 20_000,
97 dimensions: 2,
98 })
99 );
100 assert_eq!(
101 UserTypeIdentity::parse("composite#9[]"),
102 Some(UserTypeIdentity {
103 kind: UserTypeKind::Composite,
104 oid: 9,
105 dimensions: 1,
106 })
107 );
108 assert_eq!(
109 UserTypeIdentity::parse("domain#7"),
110 Some(UserTypeIdentity {
111 kind: UserTypeKind::Domain,
112 oid: 7,
113 dimensions: 0,
114 })
115 );
116 for name in ["\"enum#1\"", "enum#", "enum#x", "domain#-1", "integer"] {
117 assert_eq!(UserTypeIdentity::parse(name), None, "{name}");
118 }
119 assert_eq!(ColumnType::Integer.user_type_identity(), None);
120 }
121}