butane_core/
custom.rs

1//! For supporting additional types with the Pg backend (or other
2//! future backends).
3//!
4//! For an example of usage, see `butane/tests/custom_pg.rs` in the
5//! source repository. Not supported for the Sqlite backend as Sqlite
6//! supports a very limited set of types to begin with.
7
8#![allow(missing_docs)]
9
10use std::fmt;
11
12use serde::{Deserialize, Serialize};
13
14#[cfg(feature = "pg")]
15use tokio_postgres as postgres;
16
17/// For use with [SqlType::Custom](crate::SqlType)
18#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
19pub enum SqlTypeCustom {
20    #[cfg(feature = "pg")]
21    Pg(#[serde(with = "pgtypeser")] tokio_postgres::types::Type),
22}
23
24/// For use with [SqlVal::Custom](crate::SqlVal)
25#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
26pub enum SqlValCustom {
27    #[cfg(feature = "pg")]
28    Pg {
29        #[serde(with = "pgtypeser")]
30        ty: postgres::types::Type,
31        data: Vec<u8>,
32    },
33}
34
35impl SqlValCustom {
36    pub fn as_valref(&self) -> SqlValRefCustom {
37        match self {
38            #[cfg(feature = "pg")]
39            SqlValCustom::Pg { ty, data } => SqlValRefCustom::PgBytes {
40                ty: ty.clone(),
41                data: data.as_ref(),
42            },
43            #[cfg(not(feature = "pg"))]
44            _ => panic!("SqlValCustom unsupported"),
45        }
46    }
47}
48
49impl fmt::Display for SqlValCustom {
50    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
51        match self {
52            #[cfg(feature = "pg")]
53            SqlValCustom::Pg { ty, .. } => f.write_str(&format!("<custom PG value of type {ty}>")),
54            #[cfg(not(feature = "pg"))]
55            _ => f.write_str("<unknown custom value>"),
56        }
57    }
58}
59
60#[cfg(feature = "pg")]
61impl postgres::types::ToSql for SqlValCustom {
62    fn to_sql(
63        &self,
64        wanted_ty: &postgres::types::Type,
65        out: &mut bytes::BytesMut,
66    ) -> std::result::Result<
67        postgres::types::IsNull,
68        Box<dyn std::error::Error + 'static + Sync + Send>,
69    > {
70        use bytes::BufMut;
71        match self {
72            SqlValCustom::Pg { ty, data } => {
73                if ty != wanted_ty {
74                    return Err(Box::new(crate::Error::Internal(format!(
75                        "postgres type mismatch. Wanted {wanted_ty} but have {ty}"
76                    ))));
77                }
78                out.put(data.as_ref())
79            }
80        }
81        Ok(postgres::types::IsNull::No)
82    }
83
84    fn accepts(_: &postgres::types::Type) -> bool {
85        // Unfortunately, this is a type method rather than an instance method,
86        // so we don't know what this specific instance accepts :(
87        true
88    }
89    postgres::types::to_sql_checked!();
90}
91
92/// For use with [SqlValRef::Custom](crate::SqlValRef)
93#[derive(Clone, Debug)]
94pub enum SqlValRefCustom<'a> {
95    /// Used with Postgres, but suitable only for input (e.g. input to
96    /// a query), not suitable for parsing in a [FromSql](crate::FromSql) implementation.
97    #[cfg(feature = "pg")]
98    PgToSql {
99        ty: postgres::types::Type,
100        tosql: &'a (dyn postgres::types::ToSql + Sync),
101    },
102    /// The Pg backend will return SqlValRef instances of this
103    /// type. May also be used by [ToSql](crate::ToSql)
104    /// implementations, but may be less convenient than `PgToSql` for that purpose.
105    #[cfg(feature = "pg")]
106    PgBytes {
107        ty: postgres::types::Type,
108        data: &'a [u8],
109    },
110    #[cfg(not(feature = "pg"))]
111    Phantom(std::marker::PhantomData<&'a ()>),
112}
113
114impl From<SqlValRefCustom<'_>> for SqlValCustom {
115    fn from(r: SqlValRefCustom) -> SqlValCustom {
116        match r {
117            #[cfg(feature = "pg")]
118            SqlValRefCustom::PgToSql { ty, tosql } => {
119                let mut b = bytes::BytesMut::new();
120                // TODO avoid unwrap
121                tosql.to_sql_checked(&ty, &mut b).unwrap();
122                SqlValCustom::Pg {
123                    ty,
124                    data: b.to_vec(),
125                }
126            }
127            #[cfg(feature = "pg")]
128            SqlValRefCustom::PgBytes { ty, data } => SqlValCustom::Pg {
129                ty,
130                data: data.into(),
131            },
132            #[cfg(not(feature = "pg"))]
133            SqlValRefCustom::Phantom(_) => {
134                panic!("phantom SqlValRefCustom should not be instantiated")
135            }
136        }
137    }
138}
139
140#[cfg(feature = "pg")]
141mod pgtypeser {
142    use serde::{Deserialize, Deserializer, Serialize, Serializer};
143    use tokio_postgres as postgres;
144
145    pub fn serialize<S>(ty: &postgres::types::Type, serializer: S) -> Result<S::Ok, S::Error>
146    where
147        S: Serializer,
148    {
149        let ty = SerializablePgType::from(ty.clone());
150        ty.serialize(serializer)
151    }
152
153    pub fn deserialize<'de, D>(deserializer: D) -> Result<postgres::types::Type, D::Error>
154    where
155        D: Deserializer<'de>,
156    {
157        Ok(SerializablePgType::deserialize(deserializer)?.into())
158    }
159
160    //Serializable version of postgres::types::Type
161    #[derive(Clone, Debug, Deserialize, Serialize)]
162    struct SerializablePgType {
163        name: String,
164        oid: u32,
165        kind: Box<SerializablePgKind>,
166        schema: String,
167    }
168    impl From<postgres::types::Type> for SerializablePgType {
169        fn from(ty: postgres::types::Type) -> Self {
170            Self {
171                name: ty.name().to_string(),
172                oid: ty.oid(),
173                kind: Box::new(ty.kind().clone().into()),
174                schema: ty.schema().to_string(),
175            }
176        }
177    }
178    impl From<SerializablePgType> for postgres::types::Type {
179        fn from(spt: SerializablePgType) -> postgres::types::Type {
180            postgres::types::Type::new(spt.name, spt.oid, (*spt.kind).into(), spt.schema)
181        }
182    }
183
184    #[derive(Clone, Debug, Deserialize, Serialize)]
185    enum SerializablePgKind {
186        Simple,
187        Enum(Vec<String>),
188        Pseudo,
189        Array(SerializablePgType),
190        Range(SerializablePgType),
191        Domain(SerializablePgType),
192        Composite(Vec<SerializablePgField>),
193    }
194    impl From<postgres::types::Kind> for SerializablePgKind {
195        fn from(k: postgres::types::Kind) -> Self {
196            use postgres::types::Kind::*;
197            match k {
198                Simple => Self::Simple,
199                Enum(v) => Self::Enum(v),
200                Pseudo => Self::Pseudo,
201                Array(ty) => Self::Array(ty.into()),
202                Range(ty) => Self::Range(ty.into()),
203                Domain(ty) => Self::Domain(ty.into()),
204                Composite(v) => Self::Composite(v.into_iter().map(|f| f.into()).collect()),
205                // TODO why is rustc requiring wildcard here
206                _ => panic!("Unhandled variant"),
207            }
208        }
209    }
210    impl From<SerializablePgKind> for postgres::types::Kind {
211        fn from(spk: SerializablePgKind) -> postgres::types::Kind {
212            use postgres::types::Kind::*;
213            match spk {
214                SerializablePgKind::Simple => Simple,
215                SerializablePgKind::Enum(v) => Enum(v),
216                SerializablePgKind::Pseudo => Pseudo,
217                SerializablePgKind::Array(ty) => Array(ty.into()),
218                SerializablePgKind::Range(ty) => Range(ty.into()),
219                SerializablePgKind::Domain(ty) => Domain(ty.into()),
220                SerializablePgKind::Composite(v) => {
221                    Composite(v.into_iter().map(|f| f.into()).collect())
222                }
223            }
224        }
225    }
226
227    #[derive(Clone, Debug, Deserialize, Serialize)]
228    struct SerializablePgField {
229        name: String,
230        ty: SerializablePgType,
231    }
232    impl From<postgres::types::Field> for SerializablePgField {
233        fn from(f: postgres::types::Field) -> Self {
234            Self {
235                name: f.name().to_string(),
236                ty: f.type_().clone().into(),
237            }
238        }
239    }
240    impl From<SerializablePgField> for postgres::types::Field {
241        fn from(spf: SerializablePgField) -> postgres::types::Field {
242            postgres::types::Field::new(spf.name, spf.ty.into())
243        }
244    }
245}