clementine_tx_sender/db/
wrapper.rs

1//! Minimal SQLx wrapper types for tx-sender.
2
3use bitcoin::hashes::Hash as _;
4use bitcoin::{OutPoint, Txid};
5use sqlx::error::BoxDynError;
6use sqlx::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueRef};
7use sqlx::{Decode, Encode, Postgres, Type};
8use std::str::FromStr;
9
10/// Store `bitcoin::Txid` as `BYTEA` in Postgres.
11#[derive(sqlx::FromRow, Debug, Clone)]
12pub struct TxidDB(pub Txid);
13
14impl Type<Postgres> for TxidDB {
15    fn type_info() -> PgTypeInfo {
16        PgTypeInfo::with_name("BYTEA")
17    }
18}
19
20impl Encode<'_, Postgres> for TxidDB {
21    fn encode_by_ref(
22        &self,
23        buf: &mut PgArgumentBuffer,
24    ) -> Result<sqlx::encode::IsNull, BoxDynError> {
25        let bytes = self.0.to_byte_array();
26        <&[u8] as Encode<Postgres>>::encode_by_ref(&bytes.as_ref(), buf)
27    }
28}
29
30impl<'r> Decode<'r, Postgres> for TxidDB {
31    fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
32        let bytes = <Vec<u8> as Decode<Postgres>>::decode(value)?;
33        Ok(Self(Txid::from_slice(&bytes)?))
34    }
35}
36
37// Enable binding Vec<TxidDB> as bytea[] in queries (e.g., WHERE txid = ANY($1)).
38impl sqlx::postgres::PgHasArrayType for TxidDB {
39    fn array_type_info() -> PgTypeInfo {
40        PgTypeInfo::with_name("_bytea")
41    }
42}
43
44/// Store `bitcoin::OutPoint` as `TEXT` in Postgres.
45#[derive(sqlx::FromRow, Debug, Clone, PartialEq)]
46pub struct OutPointDB(pub OutPoint);
47
48impl Type<Postgres> for OutPointDB {
49    fn type_info() -> PgTypeInfo {
50        PgTypeInfo::with_name("TEXT")
51    }
52}
53
54impl Encode<'_, Postgres> for OutPointDB {
55    fn encode_by_ref(
56        &self,
57        buf: &mut PgArgumentBuffer,
58    ) -> Result<sqlx::encode::IsNull, BoxDynError> {
59        let s = self.0.to_string();
60        <&str as Encode<Postgres>>::encode_by_ref(&s.as_str(), buf)
61    }
62}
63
64impl<'r> Decode<'r, Postgres> for OutPointDB {
65    fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
66        let s = <&str as Decode<Postgres>>::decode(value)?;
67        Ok(Self(OutPoint::from_str(s)?))
68    }
69}