Skip to main content

spg_sqlx/types/
uuid.rs

1//! v7.17.0 Phase 3.P0-69 — sqlx bridge for `UUID`.
2//!
3//! Two surfaces land here:
4//!   * `try_uuid_as_string` — unconditional fallthrough used by
5//!     `types/text.rs::Decode<String>` so any sqlx caller can
6//!     read UUID columns as canonical hyphenated text without
7//!     enabling the `uuid` feature.
8//!   * `uuid::Uuid` Encode/Decode (gated by the `uuid` feature) —
9//!     full typed round-trip matching sqlx-postgres's UUID
10//!     surface, so mailrs and other sqlx apps can drop in
11//!     without code change.
12
13use spg_embedded::Value as EngineValue;
14
15#[cfg(feature = "uuid")]
16use sqlx_core::decode::Decode;
17#[cfg(feature = "uuid")]
18use sqlx_core::error::BoxDynError;
19#[cfg(feature = "uuid")]
20use sqlx_core::types::Type;
21
22#[cfg(feature = "uuid")]
23use crate::database::Spg;
24#[cfg(feature = "uuid")]
25use crate::type_info::{Kind, SpgTypeInfo};
26#[cfg(feature = "uuid")]
27use crate::value::SpgValueRef;
28
29/// Try to render a UUID cell as PG's canonical hyphenated
30/// lowercase text. Returns `None` for non-UUID cells so the
31/// `Decode<String>` falls through to the next attempt.
32pub(crate) fn try_uuid_as_string(value: &EngineValue) -> Option<String> {
33    match value {
34        EngineValue::Uuid(b) => Some(spg_storage::format_uuid(b)),
35        _ => None,
36    }
37}
38
39#[cfg(feature = "uuid")]
40mod u {
41    use super::*;
42    use sqlx_core::encode::{Encode, IsNull};
43    use uuid::Uuid;
44
45    use crate::arguments::SpgArgumentValue;
46
47    impl Type<Spg> for Uuid {
48        fn type_info() -> SpgTypeInfo {
49            SpgTypeInfo::of(Kind::Uuid)
50        }
51
52        fn compatible(ty: &SpgTypeInfo) -> bool {
53            matches!(ty.kind(), Kind::Uuid)
54        }
55    }
56
57    impl<'q> Encode<'q, Spg> for Uuid {
58        fn encode_by_ref(
59            &self,
60            buf: &mut Vec<SpgArgumentValue<'q>>,
61        ) -> Result<IsNull, BoxDynError> {
62            buf.push(SpgArgumentValue {
63                value: EngineValue::Uuid(*self.as_bytes()),
64                type_info: Some(SpgTypeInfo::of(Kind::Uuid)),
65                _phantom: core::marker::PhantomData,
66            });
67            Ok(IsNull::No)
68        }
69    }
70
71    impl<'r> Decode<'r, Spg> for Uuid {
72        fn decode(value: SpgValueRef<'r>) -> Result<Self, BoxDynError> {
73            match value.engine() {
74                EngineValue::Uuid(b) => Ok(Uuid::from_bytes(*b)),
75                // Generous decode from canonical text — covers
76                // the case where a `SELECT id::text FROM t` path
77                // landed at a UUID column site.
78                EngineValue::Text(s) => spg_storage::parse_uuid_str(s)
79                    .map(Uuid::from_bytes)
80                    .ok_or_else(|| format!("cannot parse text {s:?} as uuid::Uuid").into()),
81                other => Err(format!("cannot decode {other:?} as uuid::Uuid / UUID").into()),
82            }
83        }
84    }
85}