cornucopia_client_core/
type_traits.rs1use 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
101impl<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
145fn 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}