use super::ColumnType;
const ENUM_PREFIX: &str = "enum#";
const DOMAIN_PREFIX: &str = "domain#";
const COMPOSITE_PREFIX: &str = "composite#";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UserTypeKind {
Enum,
Domain,
Composite,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UserTypeIdentity {
pub kind: UserTypeKind,
pub oid: u32,
pub dimensions: usize,
}
impl UserTypeIdentity {
#[must_use]
pub fn parse(name: &str) -> Option<Self> {
let mut element = name.trim();
let mut dimensions = 0usize;
while let Some(inner) = element.strip_suffix("[]") {
element = inner;
dimensions += 1;
}
let (kind, oid) = if let Some(oid) = element.strip_prefix(ENUM_PREFIX) {
(UserTypeKind::Enum, oid)
} else if let Some(oid) = element.strip_prefix(COMPOSITE_PREFIX) {
(UserTypeKind::Composite, oid)
} else {
(UserTypeKind::Domain, element.strip_prefix(DOMAIN_PREFIX)?)
};
if oid.is_empty() || !oid.bytes().all(|byte| byte.is_ascii_digit()) {
return None;
}
Some(Self {
kind,
oid: oid.parse().ok()?,
dimensions,
})
}
}
impl ColumnType {
#[must_use]
pub fn user_type_identity(&self) -> Option<String> {
match self {
ColumnType::Enum(reference) => Some(format!("{ENUM_PREFIX}{}", reference.oid)),
ColumnType::Composite(reference) => {
Some(format!("{COMPOSITE_PREFIX}{}", reference.oid))
}
ColumnType::Domain { oid, .. } => Some(format!("{DOMAIN_PREFIX}{oid}")),
ColumnType::Array(element) => element
.user_type_identity()
.map(|element| format!("{element}[]")),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::{UserTypeIdentity, UserTypeKind};
use crate::ast::{ColumnType, EnumTypeReference};
#[test]
fn identities_round_trip_through_arrays() {
let mood = ColumnType::Enum(EnumTypeReference {
schema: "public".into(),
name: "mood".into(),
oid: 20_000,
array_oid: 20_001,
});
let array = ColumnType::Array(Box::new(ColumnType::Array(Box::new(mood))));
let identity = array.user_type_identity().unwrap();
assert_eq!(identity, "enum#20000[][]");
assert_eq!(
UserTypeIdentity::parse(&identity),
Some(UserTypeIdentity {
kind: UserTypeKind::Enum,
oid: 20_000,
dimensions: 2,
})
);
assert_eq!(
UserTypeIdentity::parse("composite#9[]"),
Some(UserTypeIdentity {
kind: UserTypeKind::Composite,
oid: 9,
dimensions: 1,
})
);
assert_eq!(
UserTypeIdentity::parse("domain#7"),
Some(UserTypeIdentity {
kind: UserTypeKind::Domain,
oid: 7,
dimensions: 0,
})
);
for name in ["\"enum#1\"", "enum#", "enum#x", "domain#-1", "integer"] {
assert_eq!(UserTypeIdentity::parse(name), None, "{name}");
}
assert_eq!(ColumnType::Integer.user_type_identity(), None);
}
}