postro/
encode.rs

1//! Query parameter encoding.
2use bytes::{Buf, Bytes};
3
4use crate::{
5    ext::BindParams,
6    postgres::{Oid, PgType},
7    value::ValueRef,
8};
9
10/// Value that can be encoded to be bound to sql parameter.
11pub trait Encode<'q> {
12    /// Encode the value.
13    fn encode(self) -> Encoded<'q>;
14}
15
16/// Postgres encoded value.
17pub struct Encoded<'q> {
18    value: ValueRef<'q>,
19    is_null: bool,
20    oid: Oid,
21}
22
23impl<'q> Encoded<'q> {
24    /// Create [`Encoded`] from borrowed slice.
25    pub fn from_slice(slice: &'q [u8], oid: Oid) -> Encoded<'q> {
26        Encoded {
27            value: ValueRef::Slice(slice),
28            is_null: false,
29            oid,
30        }
31    }
32
33    /// Create [`Encoded`] by copying given slice.
34    pub fn copy_from_slice(slice: &[u8], oid: Oid) -> Encoded<'static> {
35        Encoded {
36            value: ValueRef::Bytes(Bytes::copy_from_slice(slice)),
37            is_null: false,
38            oid,
39        }
40    }
41
42    /// Create [`Encoded`] from owned bytes.
43    pub fn owned(value: impl Into<Bytes>, oid: Oid) -> Encoded<'static> {
44        Encoded {
45            value: ValueRef::Bytes(value.into()),
46            is_null: false,
47            oid,
48        }
49    }
50
51    /// Create [`Encoded`] `NULL`.
52    pub fn null() -> Encoded<'static> {
53        Encoded {
54            value: ValueRef::Slice(&[]),
55            is_null: true,
56            oid: 0,
57        }
58    }
59
60    /// Returns [`Oid`], or `0` if its `NULL`.
61    pub fn oid(&self) -> Oid {
62        match self.is_null {
63            true => 0,
64            false => self.oid,
65        }
66    }
67
68    pub(crate) fn value(&self) -> &ValueRef<'q> {
69        &self.value
70    }
71}
72
73impl Buf for Encoded<'_> {
74    fn remaining(&self) -> usize {
75        self.value.remaining()
76    }
77
78    fn chunk(&self) -> &[u8] {
79        self.value.chunk()
80    }
81
82    fn advance(&mut self, cnt: usize) {
83        self.value.advance(cnt);
84    }
85}
86
87impl BindParams for Encoded<'_> {
88    fn size(&self) -> i32 {
89        match self.is_null {
90            true => -1,
91            false => self.remaining().try_into().unwrap(),
92        }
93    }
94}
95
96macro_rules! encode {
97    (<$lf:tt,$ty:ty>$pat:tt => $body:expr) => {
98        impl<$lf> Encode<$lf> for &$lf $ty {
99            fn encode($pat) -> Encoded<$lf> {
100                Encoded {
101                    value: $body,
102                    oid: <$ty>::OID,
103                    is_null: false,
104                }
105            }
106        }
107    };
108    (<$ty:ty>$pat:tt => $body:expr) => {
109        impl Encode<'static> for $ty {
110            fn encode($pat) -> Encoded<'static> {
111                Encoded {
112                    value: $body,
113                    oid: <$ty>::OID,
114                    is_null: false,
115                }
116            }
117        }
118    };
119}
120
121encode!(<bool>self => ValueRef::inline(&(self as u8).to_be_bytes()));
122encode!(<i32>self => ValueRef::inline(&self.to_be_bytes()));
123encode!(<'a,str>self => ValueRef::Slice(self.as_bytes()));
124encode!(<'a,String>self => ValueRef::Slice(self.as_bytes()));
125
126impl std::fmt::Debug for Encoded<'_> {
127    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
128        f.debug_tuple("Encoded")
129            .field(if self.is_null { &"NULL" } else { &self.value })
130            .field(&self.oid)
131            .finish()
132    }
133}
134