Skip to main content

spg_sqlx/types/
text.rs

1//! v7.16.0 — `Type` / `Encode` / `Decode` for `String` / `&str`.
2
3use sqlx_core::decode::Decode;
4use sqlx_core::encode::{Encode, IsNull};
5use sqlx_core::error::BoxDynError;
6use sqlx_core::types::Type;
7
8use spg_embedded::Value as EngineValue;
9
10use crate::arguments::SpgArgumentValue;
11use crate::database::Spg;
12use crate::type_info::{Kind, SpgTypeInfo};
13use crate::value::SpgValueRef;
14
15impl Type<Spg> for str {
16    fn type_info() -> SpgTypeInfo {
17        SpgTypeInfo::of(Kind::Text)
18    }
19
20    fn compatible(ty: &SpgTypeInfo) -> bool {
21        // v7.17.0 Phase 3.P0-67 / P0-68 / P0-69 — exact-decimal,
22        // pgvector, tsvector, and UUID cells all decode to their
23        // canonical PG text form, so `String` / `&str` accept
24        // them as compatible. Keeps
25        // `query_as::<_, (String,)>("SELECT vec_col …")` shapes
26        // working without enabling type-specific features.
27        matches!(
28            ty.kind(),
29            Kind::Text | Kind::Numeric | Kind::Vector | Kind::TsVector | Kind::Uuid
30        )
31    }
32}
33
34impl Type<Spg> for String {
35    fn type_info() -> SpgTypeInfo {
36        <str as Type<Spg>>::type_info()
37    }
38
39    fn compatible(ty: &SpgTypeInfo) -> bool {
40        <str as Type<Spg>>::compatible(ty)
41    }
42}
43
44impl<'q> Encode<'q, Spg> for &'q str {
45    fn encode_by_ref(&self, buf: &mut Vec<SpgArgumentValue<'q>>) -> Result<IsNull, BoxDynError> {
46        buf.push(SpgArgumentValue {
47            value: EngineValue::Text((*self).to_string()),
48            type_info: Some(<str as Type<Spg>>::type_info()),
49            _phantom: core::marker::PhantomData,
50        });
51        Ok(IsNull::No)
52    }
53}
54
55impl<'q> Encode<'q, Spg> for String {
56    fn encode_by_ref(&self, buf: &mut Vec<SpgArgumentValue<'q>>) -> Result<IsNull, BoxDynError> {
57        buf.push(SpgArgumentValue {
58            value: EngineValue::Text(self.clone()),
59            type_info: Some(<String as Type<Spg>>::type_info()),
60            _phantom: core::marker::PhantomData,
61        });
62        Ok(IsNull::No)
63    }
64}
65
66impl<'r> Decode<'r, Spg> for String {
67    fn decode(value: SpgValueRef<'r>) -> Result<Self, BoxDynError> {
68        // v7.17.0 Phase 3.P0-67 / P0-68 / P0-69 — NUMERIC /
69        // VECTOR / TSVECTOR / UUID cells all decode to their
70        // canonical PG text form so the dominant
71        // `query_as::<_, (String,)>` pattern works without
72        // enabling any type-specific feature.
73        if let Some(s) = crate::types::decimal::try_numeric_as_string(value.engine()) {
74            return Ok(s);
75        }
76        if let Some(s) = crate::types::vector::try_vector_as_string(value.engine()) {
77            return Ok(s);
78        }
79        if let Some(s) = crate::types::vector::try_tsvector_as_string(value.engine()) {
80            return Ok(s);
81        }
82        if let Some(s) = crate::types::uuid::try_uuid_as_string(value.engine()) {
83            return Ok(s);
84        }
85        match value.engine() {
86            EngineValue::Text(s) | EngineValue::Json(s) => Ok(s.clone()),
87            other => Err(format!("cannot decode {other:?} as String / TEXT").into()),
88        }
89    }
90}
91
92impl<'r> Decode<'r, Spg> for &'r str {
93    fn decode(value: SpgValueRef<'r>) -> Result<Self, BoxDynError> {
94        match value.engine() {
95            EngineValue::Text(s) | EngineValue::Json(s) => Ok(s.as_str()),
96            other => Err(format!("cannot decode {other:?} as &str / TEXT").into()),
97        }
98    }
99}