Skip to main content

elefant_client/types/
mod.rs

1mod enum_type;
2mod from_sql_row;
3mod oid;
4mod standard_types;
5
6// Refactored type implementations
7mod binary;
8mod bool;
9mod char;
10mod collections;
11#[cfg(feature = "time")]
12mod datetime;
13#[cfg(feature = "json")]
14mod json_type;
15mod nullable;
16mod numbers;
17mod text;
18#[cfg(feature = "uuid")]
19mod uuid_type;
20#[cfg(feature = "json")]
21pub use json_type::{Json, Jsonb};
22#[cfg(feature = "decimal")]
23mod numeric_type;
24mod point_type;
25pub use point_type::Point;
26mod network_type;
27pub use enum_type::*;
28pub use network_type::{Cidr, Inet};
29
30use crate::protocol::FieldDescription;
31use crate::ElefantClientError;
32pub use from_sql_row::*;
33pub use oid::*;
34use std::error::Error;
35
36/// Base trait providing shared functionality for both binary and text format trait implementations
37pub trait FromSqlBase<'a>: Sized {
38    fn accepts(field: &FieldDescription) -> bool {
39        Self::accepts_postgres_type(field.data_type_oid)
40    }
41
42    fn accepts_postgres_type(oid: i32) -> bool;
43
44    /// Extended accepts check with access to the enum type registry.
45    /// Override for enum types and collections of enums.
46    /// Default delegates to the static `accepts` check.
47    fn accepts_with_registry(field: &FieldDescription, _registry: &EnumTypeRegistry) -> bool {
48        Self::accepts(field)
49    }
50
51    fn from_null(field: &FieldDescription) -> Result<Self, ElefantClientError> {
52        Err(ElefantClientError::UnexpectedNullValue {
53            postgres_field: field.clone(),
54        })
55    }
56}
57
58/// Trait for types that can be deserialized from PostgreSQL binary format data
59pub trait FromSqlBinary<'a>: FromSqlBase<'a> {
60    fn from_sql_binary(
61        raw: &'a [u8],
62        field: &FieldDescription,
63    ) -> Result<Self, Box<dyn Error + Sync + Send>>;
64}
65
66/// Trait for types that can be deserialized from PostgreSQL text format data
67pub trait FromSqlText<'a>: FromSqlBase<'a> {
68    fn from_sql_text(
69        raw: &'a str,
70        field: &FieldDescription,
71    ) -> Result<Self, Box<dyn Error + Sync + Send>>;
72}
73
74/// Compatibility trait that provides both binary and text format support
75/// This maintains backward compatibility with existing code
76pub trait FromSql<'a>: FromSqlBinary<'a> + FromSqlText<'a> {}
77
78/// Automatic implementation of FromSql for types that implement both binary and text variants
79impl<'a, T> FromSql<'a> for T where T: FromSqlBinary<'a> + FromSqlText<'a> {}
80
81/// A trait for types which can be created from a Postgres value without borrowing any data.
82/// This is primarily useful for trait bounds on functions.
83pub trait FromSqlOwned: for<'owned> FromSql<'owned> {}
84
85impl<T> FromSqlOwned for T where T: for<'a> FromSql<'a> {}
86
87/// A trait for types which can be created from binary Postgres values without borrowing any data.
88pub trait FromSqlBinaryOwned: for<'owned> FromSqlBinary<'owned> {}
89
90impl<T> FromSqlBinaryOwned for T where T: for<'a> FromSqlBinary<'a> {}
91
92/// A trait for types which can be created from text Postgres values without borrowing any data.
93pub trait FromSqlTextOwned: for<'owned> FromSqlText<'owned> {}
94
95impl<T> FromSqlTextOwned for T where T: for<'a> FromSqlText<'a> {}
96
97pub trait ToSql {
98    fn to_sql_binary(
99        &self,
100        target_buffer: &mut Vec<u8>,
101    ) -> Result<(), Box<dyn Error + Sync + Send>>;
102    fn is_null(&self) -> bool {
103        false
104    }
105}
106
107pub trait PostgresNamedType {
108    const PG_NAME: &'static str;
109}
110
111pub trait DomainType {
112    type Inner: for<'owned> FromSql<'owned>;
113
114    fn from_inner(inner: Self::Inner) -> Self;
115
116    fn accepts(field: &FieldDescription) -> bool {
117        Self::accepts_postgres_type(field.data_type_oid)
118    }
119
120    fn accepts_postgres_type(oid: i32) -> bool;
121}
122
123#[macro_export]
124macro_rules! impl_from_sql_for_domain_type {
125    ($typ: ty) => {
126        impl<'a> FromSqlBase<'a> for $typ {
127            fn accepts(field: &FieldDescription) -> bool {
128                <Self as DomainType>::accepts(field)
129            }
130
131            fn accepts_postgres_type(oid: i32) -> bool {
132                <Self as DomainType>::accepts_postgres_type(oid)
133            }
134        }
135
136        impl<'a> FromSqlBinary<'a> for $typ {
137            fn from_sql_binary(
138                raw: &'a [u8],
139                field: &FieldDescription,
140            ) -> Result<Self, Box<dyn Error + Sync + Send>> {
141                let inner = <Self as DomainType>::Inner::from_sql_binary(raw, field)?;
142                Ok(Self::from_inner(inner))
143            }
144        }
145
146        impl<'a> FromSqlText<'a> for $typ {
147            fn from_sql_text(
148                raw: &'a str,
149                field: &FieldDescription,
150            ) -> Result<Self, Box<dyn Error + Sync + Send>> {
151                let inner = <Self as DomainType>::Inner::from_sql_text(raw, field)?;
152                Ok(Self::from_inner(inner))
153            }
154        }
155    };
156}
157
158pub struct PostgresType {
159    pub(crate) oid: i32,
160    name: &'static str,
161    /// The underlying type
162    element: Option<&'static PostgresType>,
163    /// True if this is an array type
164    is_array: bool,
165
166    array_delimiter: char,
167}
168
169impl PostgresType {
170    fn inner_most(&self) -> &PostgresType {
171        match self.element {
172            Some(element) => element.inner_most(),
173            None => self,
174        }
175    }
176}