1use crate::{pg_sys, FromDatum, IntoDatum, PgMemoryContexts};
10use core::fmt::Write;
11use pgx_sql_entity_graph::metadata::{
12 ArgumentError, Returns, ReturnsError, SqlMapping, SqlTranslatable,
13};
14use std::ops::{Deref, DerefMut};
15
16const UUID_BYTES_LEN: usize = 16;
17pub type UuidBytes = [u8; UUID_BYTES_LEN];
18
19#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd, Debug)]
21#[repr(transparent)]
22pub struct Uuid(UuidBytes);
23
24impl IntoDatum for Uuid {
25 #[inline]
26 fn into_datum(self) -> Option<pg_sys::Datum> {
27 let ptr = unsafe {
28 PgMemoryContexts::CurrentMemoryContext.palloc_slice::<u8>(UUID_BYTES_LEN)
30 };
31 ptr.clone_from_slice(&self.0);
32
33 Some(ptr.as_ptr().into())
34 }
35
36 #[inline]
37 fn type_oid() -> pg_sys::Oid {
38 pg_sys::UUIDOID
39 }
40}
41
42impl FromDatum for Uuid {
43 #[inline]
44 unsafe fn from_polymorphic_datum(
45 datum: pg_sys::Datum,
46 is_null: bool,
47 _typoid: pg_sys::Oid,
48 ) -> Option<Uuid> {
49 if is_null {
50 None
51 } else {
52 let bytes =
53 std::slice::from_raw_parts(datum.cast_mut_ptr::<u8>() as *const u8, UUID_BYTES_LEN);
54 if let Ok(uuid) = Uuid::from_slice(bytes) {
55 Some(uuid)
56 } else {
57 None
58 }
59 }
60 }
61}
62
63enum UuidFormatCase {
64 Lowercase,
65 Uppercase,
66}
67
68impl Uuid {
69 pub fn from_bytes(b: UuidBytes) -> Self {
70 Uuid(b)
71 }
72
73 pub const fn as_bytes(&self) -> &UuidBytes {
74 &self.0
75 }
76
77 pub fn from_slice(b: &[u8]) -> Result<Uuid, String> {
78 let len = b.len();
79
80 if len != UUID_BYTES_LEN {
81 Err(format!("Expected UUID to be {} bytes, got {}", UUID_BYTES_LEN, len))?;
82 }
83
84 let mut bytes = [0; UUID_BYTES_LEN];
85 bytes.copy_from_slice(b);
86 Ok(Uuid::from_bytes(bytes))
87 }
88
89 fn format(&self, f: &mut std::fmt::Formatter<'_>, case: UuidFormatCase) -> std::fmt::Result {
90 let hyphenated = f.sign_minus();
91 for (i, b) in self.0.iter().enumerate() {
92 if hyphenated && (i == 4 || i == 6 || i == 8 || i == 10) {
93 f.write_char('-')?;
94 }
95 match case {
96 UuidFormatCase::Lowercase => write!(f, "{:02x}", b)?,
97 UuidFormatCase::Uppercase => write!(f, "{:02X}", b)?,
98 };
99 }
100 Ok(())
101 }
102}
103
104impl Deref for Uuid {
105 type Target = UuidBytes;
106
107 fn deref(&self) -> &Self::Target {
108 &self.0
109 }
110}
111
112impl DerefMut for Uuid {
113 fn deref_mut(&mut self) -> &mut Self::Target {
114 &mut self.0
115 }
116}
117
118impl std::fmt::Display for Uuid {
119 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120 write!(f, "{:-x}", self)
121 }
122}
123
124impl<'a> std::fmt::LowerHex for Uuid {
125 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
126 self.format(f, UuidFormatCase::Lowercase)
127 }
128}
129
130impl<'a> std::fmt::UpperHex for Uuid {
131 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
132 self.format(f, UuidFormatCase::Uppercase)
133 }
134}
135
136unsafe impl SqlTranslatable for crate::datum::Uuid {
137 fn argument_sql() -> Result<SqlMapping, ArgumentError> {
138 Ok(SqlMapping::literal("uuid"))
139 }
140 fn return_sql() -> Result<Returns, ReturnsError> {
141 Ok(Returns::One(SqlMapping::literal("uuid")))
142 }
143}