cornucopia_client_core/
type_traits.rs

1use std::borrow::Cow;
2
3use crate::domain::escape_domain_to_sql;
4use postgres_protocol::types::{self, ArrayDimension};
5use postgres_types::{private::BytesMut, to_sql_checked, IsNull, Kind, ToSql, Type};
6
7pub trait StringSql: std::fmt::Debug + ToSql + Sync {}
8impl<T: StringSql> StringSql for &T {}
9impl StringSql for String {}
10impl StringSql for &str {}
11impl StringSql for Cow<'_, str> {}
12impl StringSql for Box<str> {}
13
14pub trait BytesSql: std::fmt::Debug + ToSql + Send + Sync {}
15impl<T: BytesSql> BytesSql for &T {}
16impl BytesSql for Vec<u8> {}
17impl BytesSql for &[u8] {}
18
19#[cfg(feature = "with-serde_json-1")]
20pub trait JsonSql: std::fmt::Debug + ToSql + Sync + Send {}
21#[cfg(feature = "with-serde_json-1")]
22impl<T: JsonSql> JsonSql for &T {}
23#[cfg(feature = "with-serde_json-1")]
24impl JsonSql for serde_json_1::value::Value {}
25#[cfg(feature = "with-serde_json-1")]
26impl<T: serde_1::ser::Serialize + std::fmt::Debug + Sync + Send> JsonSql
27    for postgres_types::Json<T>
28{
29}
30
31pub trait ArraySql: std::fmt::Debug + ToSql + Send + Sync {
32    type Item;
33    fn escape_domain_to_sql(
34        &self,
35        ty: &Type,
36        w: &mut BytesMut,
37    ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>>;
38}
39impl<T: std::fmt::Debug + ToSql + Sync, A: ArraySql<Item = T>> ArraySql for &A {
40    type Item = T;
41
42    fn escape_domain_to_sql(
43        &self,
44        ty: &Type,
45        w: &mut BytesMut,
46    ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>> {
47        A::escape_domain_to_sql(self, ty, w)
48    }
49}
50impl<T: std::fmt::Debug + ToSql + Send + Sync> ArraySql for Vec<T> {
51    type Item = T;
52
53    fn escape_domain_to_sql(
54        &self,
55        ty: &Type,
56        w: &mut BytesMut,
57    ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>> {
58        escape_domain_to_sql(ty, w, self.iter())
59    }
60}
61
62impl<T: std::fmt::Debug + ToSql + Sync> ArraySql for &[T] {
63    type Item = T;
64
65    fn escape_domain_to_sql(
66        &self,
67        ty: &Type,
68        w: &mut BytesMut,
69    ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>> {
70        escape_domain_to_sql(ty, w, self.iter())
71    }
72}
73
74impl<
75        T: std::fmt::Debug + ToSql + Send + Sync,
76        I: Iterator<Item = T> + ExactSizeIterator,
77        F: Fn() -> I + Send + Sync,
78    > ArraySql for IterSql<T, I, F>
79{
80    type Item = T;
81
82    fn escape_domain_to_sql(
83        &self,
84        ty: &Type,
85        w: &mut BytesMut,
86    ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>> {
87        escape_domain_to_sql(ty, w, (self.0)())
88    }
89}
90
91pub struct IterSql<T: ToSql, I: Iterator<Item = T> + ExactSizeIterator, F: Fn() -> I + Sync>(pub F);
92
93impl<T: ToSql, I: Iterator<Item = T> + ExactSizeIterator, F: Fn() -> I + Sync> std::fmt::Debug
94    for IterSql<T, I, F>
95{
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        f.debug_tuple("ArrayFn").finish()
98    }
99}
100
101// Taken from `postgres`
102impl<T: ToSql, I: Iterator<Item = T> + ExactSizeIterator, F: Fn() -> I + Sync> ToSql
103    for IterSql<T, I, F>
104{
105    fn to_sql(
106        &self,
107        ty: &Type,
108        w: &mut BytesMut,
109    ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>> {
110        let member_type = match *ty.kind() {
111            Kind::Array(ref member) => member,
112            _ => panic!("expected array type"),
113        };
114
115        let iter = (self.0)();
116
117        let dimension = ArrayDimension {
118            len: downcast(iter.len())?,
119            lower_bound: 1,
120        };
121
122        types::array_to_sql(
123            Some(dimension),
124            member_type.oid(),
125            iter,
126            |e, w| match e.to_sql(member_type, w)? {
127                IsNull::No => Ok(postgres_protocol::IsNull::No),
128                IsNull::Yes => Ok(postgres_protocol::IsNull::Yes),
129            },
130            w,
131        )?;
132        Ok(IsNull::No)
133    }
134
135    fn accepts(ty: &Type) -> bool {
136        match *ty.kind() {
137            Kind::Array(ref member) => T::accepts(member),
138            _ => false,
139        }
140    }
141
142    to_sql_checked!();
143}
144
145// https://github.com/sfackler/rust-postgres/blob/765395f288861209a644c621bf72172acd482515/postgres-types/src/lib.rs
146fn downcast(len: usize) -> Result<i32, Box<dyn std::error::Error + Sync + Send>> {
147    if len > i32::max_value() as usize {
148        Err("value too large to transmit".into())
149    } else {
150        Ok(len as i32)
151    }
152}