spg_sqlx/type_info.rs
1//! v7.16.0 — `sqlx::TypeInfo` for SPG column types.
2
3use std::fmt;
4
5use sqlx_core::type_info::TypeInfo;
6
7/// SPG column type info. Stores the concrete [`Kind`] so the
8/// adapter can drive PG-shape column metadata that
9/// `#[derive(FromRow)]` expects.
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct SpgTypeInfo {
12 kind: Kind,
13}
14
15/// Identity tag for each column type the adapter currently
16/// understands. Matches the subset of `spg_storage::DataType`
17/// the adapter Encode/Decode coverage extends to.
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19#[non_exhaustive]
20pub enum Kind {
21 /// `INT` / 4-byte signed integer.
22 Int,
23 /// `BIGINT` / 8-byte signed integer.
24 BigInt,
25 /// `SMALLINT` / 2-byte signed integer.
26 SmallInt,
27 /// `BOOLEAN`.
28 Bool,
29 /// `TEXT` / `VARCHAR` (text body — encoding agnostic).
30 Text,
31 /// `BYTEA` (raw bytes).
32 Bytes,
33 /// `FLOAT` (IEEE-754 double).
34 Float,
35 /// `DATE`.
36 Date,
37 /// `TIMESTAMP`.
38 Timestamp,
39 /// `TIMESTAMPTZ`.
40 Timestamptz,
41 /// `JSON` / `JSONB` (text-backed JSON).
42 Json,
43 /// Unknown / type-erased — used for parameters that the
44 /// adapter binds without a fixed column-side type yet (e.g.
45 /// the first bind of a fresh parameter index).
46 Null,
47}
48
49impl SpgTypeInfo {
50 /// Construct a TypeInfo for a known kind.
51 #[must_use]
52 pub const fn of(kind: Kind) -> Self {
53 Self { kind }
54 }
55
56 /// The concrete kind tag.
57 #[must_use]
58 pub const fn kind(&self) -> Kind {
59 self.kind
60 }
61
62 /// v7.16.0 — translate from the engine's [`DataType`] to
63 /// the adapter's typed `Kind`. Used by the fetch path to
64 /// build column metadata for `SpgRow`. Any DataType the
65 /// adapter hasn't bridged yet maps to `Kind::Null` —
66 /// downstream Decode calls then fail with a clear
67 /// "cannot decode" message identifying the column type.
68 #[must_use]
69 pub fn from_data_type(ty: spg_embedded::DataType) -> Self {
70 use spg_embedded::DataType;
71 let kind = match ty {
72 DataType::Int => Kind::Int,
73 DataType::BigInt => Kind::BigInt,
74 DataType::SmallInt => Kind::SmallInt,
75 DataType::Bool => Kind::Bool,
76 DataType::Text => Kind::Text,
77 DataType::Bytes => Kind::Bytes,
78 DataType::Float => Kind::Float,
79 DataType::Date => Kind::Date,
80 DataType::Timestamp => Kind::Timestamp,
81 DataType::Timestamptz => Kind::Timestamptz,
82 DataType::Json => Kind::Json,
83 // v7.16.0 — DataType is #[non_exhaustive]; any
84 // variant we haven't bridged yet decodes to Null
85 // (so Decode impls see "compatible? no" instead of
86 // a panic).
87 _ => Kind::Null,
88 };
89 Self { kind }
90 }
91}
92
93impl TypeInfo for SpgTypeInfo {
94 fn is_null(&self) -> bool {
95 matches!(self.kind, Kind::Null)
96 }
97
98 fn name(&self) -> &str {
99 match self.kind {
100 Kind::Int => "INT",
101 Kind::BigInt => "BIGINT",
102 Kind::SmallInt => "SMALLINT",
103 Kind::Bool => "BOOLEAN",
104 Kind::Text => "TEXT",
105 Kind::Bytes => "BYTEA",
106 Kind::Float => "FLOAT",
107 Kind::Date => "DATE",
108 Kind::Timestamp => "TIMESTAMP",
109 Kind::Timestamptz => "TIMESTAMPTZ",
110 Kind::Json => "JSON",
111 Kind::Null => "NULL",
112 }
113 }
114}
115
116impl fmt::Display for SpgTypeInfo {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 f.write_str(self.name())
119 }
120}