1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use tokio_postgres::types::{FromSqlOwned, ToSql};
use crate::{Error, Sql};
/// A trait used to which Rust type a Postgres type is read into.
///
/// # Example
///
/// This can be useful to implement manually when e.g. reading a Postgres string column into an
/// enum.
///
/// ```
/// #[derive(Debug, Default, Clone, Copy)]
/// pub enum Role {
/// #[default]
/// User,
/// Admin,
/// }
///
/// impl sqlm_postgres::SqlType for Role {
/// type Type = String;
/// }
///
/// impl Role {
/// pub fn as_str(&self) -> &'static str {
/// match self {
/// Self::User => "user",
/// Self::Admin => "admin",
/// }
/// }
/// }
///
/// impl<'a> sqlm_postgres::FromSql<'a> for Role {
/// fn from_sql(
/// ty: &postgres_types::Type,
/// raw: &'a [u8],
/// ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
/// use std::str::FromStr;
/// Ok(Role::from_str(<&str>::from_sql(ty, raw)?)?)
/// }
///
/// fn accepts(ty: &postgres_types::Type) -> bool {
/// <&str as sqlm_postgres::FromSql<'_>>::accepts(ty)
/// }
/// }
///
/// impl sqlm_postgres::ToSql for Role {
/// fn to_sql(
/// &self,
/// ty: &postgres_types::Type,
/// out: &mut bytes::BytesMut,
/// ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>>
/// where
/// Self: Sized,
/// {
/// <&str as sqlm_postgres::ToSql>::to_sql(&self.as_str(), ty, out)
/// }
///
/// fn accepts(ty: &postgres_types::Type) -> bool
/// where
/// Self: Sized,
/// {
/// <&str as sqlm_postgres::ToSql>::accepts(ty)
/// }
///
/// fn to_sql_checked(
/// &self,
/// ty: &postgres_types::Type,
/// out: &mut bytes::BytesMut,
/// ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
/// <&str as sqlm_postgres::ToSql>::to_sql_checked(&self.as_str(), ty, out)
/// }
/// }
///
/// impl std::str::FromStr for Role {
/// type Err = UnknownRole;
///
/// fn from_str(s: &str) -> Result<Self, Self::Err> {
/// match s {
/// "user" => Ok(Role::User),
/// "admin" => Ok(Role::Admin),
/// s => Err(UnknownRole(s.to_string())),
/// }
/// }
/// }
///
/// #[derive(Debug)]
/// pub struct UnknownRole(String);
///
/// impl std::fmt::Display for UnknownRole {
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
/// write!(f, "Unknown bevel lhs `{}`", self.0)
/// }
/// }
///
/// impl std::error::Error for UnknownRole {}
/// ```
pub trait SqlType {
type Type;
fn query_literal<'a>(
sql: &'a Sql<'a, Primitive<Self::Type>, Self>,
conn: impl super::Connection + 'a,
) -> Pin<Box<dyn Future<Output = Result<Self, Error>> + Send + 'a>>
where
Self: FromSqlOwned + ToSql + Send + Sync,
Self::Type: Send + Sync,
{
Box::pin(async move {
let row = conn.query_one(sql.query, sql.parameters).await?;
Ok(row.try_get(0)?)
})
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Bytea(pub Vec<u8>);
#[cfg(not(nightly_column_names))]
pub struct StructColumn<T, const NAME: usize>(PhantomData<T>);
#[cfg(nightly_column_names)]
pub struct StructColumn<T, const NAME: &'static str>(PhantomData<T>);
pub struct Struct<T>(PhantomData<T>);
pub struct Primitive<T>(PhantomData<T>);
pub struct Array<T>(PhantomData<T>);
pub struct Enum<T>(PhantomData<T>);
#[cfg(not(nightly_column_names))]
pub struct EnumVariant<const NAME: usize>(());
#[cfg(nightly_column_names)]
pub struct EnumVariant<const NAME: &'static str>(());
macro_rules! impl_type {
($ty:path) => {
impl SqlType for $ty {
type Type = Self;
}
};
}
impl_type!(i32);
impl_type!(i64);
impl_type!(f32);
impl_type!(f64);
impl_type!(bool);
impl_type!(String);
#[cfg(feature = "json")]
impl_type!(serde_json::Value);
#[cfg(feature = "time")]
impl_type!(time::OffsetDateTime);
#[cfg(feature = "time")]
impl_type!(time::Date);
#[cfg(feature = "uuid")]
impl_type!(uuid::Uuid);
#[cfg(feature = "pgvector")]
impl_type!(pgvector::Vector);
impl<'a> SqlType for &'a str {
type Type = String;
}