Skip to main content

drizzle_postgres/
traits.rs

1mod column;
2mod table;
3mod value;
4
5#[cfg(not(feature = "std"))]
6use crate::prelude::*;
7pub use column::*;
8use core::any::Any;
9use drizzle_core::error::DrizzleError;
10pub use table::*;
11pub use value::*;
12
13use crate::values::{OwnedPostgresValue, PostgresValue};
14
15/// Trait for `PostgreSQL` native enum types that can be used as dyn objects
16#[allow(clippy::wrong_self_convention)]
17pub trait PostgresEnum: Send + Sync + Any {
18    /// Get the enum type name for `PostgreSQL`
19    fn enum_type_name(&self) -> &'static str;
20
21    fn as_enum(&self) -> &dyn PostgresEnum;
22
23    /// Get the string representation of this enum variant
24    fn variant_name(&self) -> &'static str;
25
26    /// Clone this enum as a boxed trait object
27    fn into_boxed(&self) -> Box<dyn PostgresEnum>;
28
29    /// Try to create this enum from a string value
30    ///
31    /// # Errors
32    ///
33    /// Returns [`DrizzleError::ConversionError`] when `value` is not a valid
34    /// variant name for this enum.
35    fn try_from_str(value: &str) -> Result<Self, DrizzleError>
36    where
37        Self: Sized;
38}
39
40impl core::fmt::Debug for &dyn PostgresEnum {
41    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
42        f.debug_struct("PostgresSQLEnum")
43            .field("type", &self.enum_type_name())
44            .field("variant", &self.variant_name())
45            .finish()
46    }
47}
48
49impl PartialEq for &dyn PostgresEnum {
50    fn eq(&self, other: &Self) -> bool {
51        self.enum_type_name() == other.enum_type_name()
52            && self.variant_name() == other.variant_name()
53    }
54}
55
56impl core::fmt::Debug for Box<dyn PostgresEnum> {
57    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
58        f.debug_struct("PostgresSQLEnum")
59            .field("type", &self.enum_type_name())
60            .field("variant", &self.variant_name())
61            .finish()
62    }
63}
64
65impl Clone for Box<dyn PostgresEnum> {
66    fn clone(&self) -> Self {
67        self.into_boxed()
68    }
69}
70
71impl PartialEq for Box<dyn PostgresEnum> {
72    fn eq(&self, other: &Self) -> bool {
73        self.enum_type_name() == other.enum_type_name()
74            && self.variant_name() == other.variant_name()
75    }
76}
77
78/// Marker trait for custom Rust types that map to a `PostgreSQL` column.
79///
80/// Generated by `#[derive(PostgresEnum)]`. The table macro uses this trait to
81/// auto-detect enum types without requiring `#[column(ENUM)]`.
82///
83/// # Associated Constants
84///
85/// - `SQLType`: The Drizzle SQL type marker used for typed expressions
86/// - `SQL_TYPE`: The `PostgreSQL` column type (e.g. `"text"`, `"integer"`, or the lowercased enum name)
87/// - `NEEDS_CREATE_TYPE`: Whether this requires a `CREATE TYPE` (native PG enum)
88///
89/// # Required Methods
90///
91/// - `decode`: Read self from a postgres Row at the given index
92/// - `encode`: Convert self to a `PostgresValue` for insertion/updates
93///
94/// The blanket `From<Self> for PostgresValue` owns the encoded value because
95/// insert/update models may store SQL fragments after the source value is
96/// dropped. Call `encode()` directly when you need an immediate borrowed value.
97/// Override `encode_owned()` when consuming `self` can avoid cloning owned data.
98#[cfg(any(feature = "postgres-sync", feature = "tokio-postgres"))]
99#[diagnostic::on_unimplemented(
100    message = "`{Self}` cannot be used as a PostgreSQL column type",
101    note = "add #[derive(PostgresEnum)] for enum types, or use a supported primitive type"
102)]
103pub trait DrizzlePostgresColumn: Sized {
104    /// Drizzle SQL type marker for this column.
105    ///
106    /// Use one of the built-in PostgreSQL markers, such as `Text`, `Int4`,
107    /// `Bytea`, `Boolean`, `Numeric`, `Enum`, or `Any`.
108    type SQLType: drizzle_core::types::DataType;
109
110    /// `PostgreSQL` column type: `"text"`, `"integer"`, or native enum type name
111    const SQL_TYPE: &'static str;
112
113    /// Whether this requires a `CREATE TYPE` (native PG enum).
114    const NEEDS_CREATE_TYPE: bool = false;
115
116    /// Decode self from a postgres Row at the given index.
117    ///
118    /// # Errors
119    ///
120    /// Returns [`DrizzleError::ConversionError`] when the column at `idx`
121    /// cannot be decoded into this type.
122    fn decode(row: &crate::Row, idx: usize) -> Result<Self, DrizzleError>;
123
124    /// Convert self to a `PostgresValue` for insertion/updates.
125    fn encode(&self) -> PostgresValue<'_>;
126
127    /// Convert self to an owned `PostgreSQL` value for stored bind parameters.
128    ///
129    /// The default implementation owns the borrowed result of [`encode`](Self::encode).
130    /// Override this for wrappers that can move an internal string or byte buffer
131    /// directly into the SQL parameter.
132    fn encode_owned(self) -> OwnedPostgresValue {
133        self.encode().into_owned()
134    }
135}
136
137impl<'a, T> From<T> for PostgresValue<'a>
138where
139    T: DrizzlePostgresColumn,
140{
141    fn from(value: T) -> Self {
142        value.encode_owned().into()
143    }
144}
145
146/// Stub trait when no postgres driver is enabled — allows enum derives to compile
147/// without a driver feature, but the table macro's `TryFrom` impls won't be generated.
148///
149/// The blanket `From<Self> for PostgresValue` owns the encoded value because
150/// insert/update models may store SQL fragments after the source value is
151/// dropped. Call `encode()` directly when you need an immediate borrowed value.
152/// Override `encode_owned()` when consuming `self` can avoid cloning owned data.
153#[cfg(not(any(feature = "postgres-sync", feature = "tokio-postgres")))]
154#[diagnostic::on_unimplemented(
155    message = "`{Self}` cannot be used as a PostgreSQL column type",
156    note = "add #[derive(PostgresEnum)] for enum types, or use a supported primitive type"
157)]
158pub trait DrizzlePostgresColumn: Sized {
159    /// Drizzle SQL type marker for this column.
160    ///
161    /// Use one of the built-in PostgreSQL markers, such as `Text`, `Int4`,
162    /// `Bytea`, `Boolean`, `Numeric`, `Enum`, or `Any`.
163    type SQLType: drizzle_core::types::DataType;
164
165    /// `PostgreSQL` column type: `"text"`, `"integer"`, or native enum type name
166    const SQL_TYPE: &'static str;
167
168    /// Whether this requires a `CREATE TYPE` (native PG enum).
169    const NEEDS_CREATE_TYPE: bool = false;
170
171    /// Convert self to a `PostgresValue` for insertion/updates.
172    fn encode(&self) -> PostgresValue<'_>;
173
174    /// Convert self to an owned `PostgreSQL` value for stored bind parameters.
175    ///
176    /// The default implementation owns the borrowed result of [`encode`](Self::encode).
177    /// Override this for wrappers that can move an internal string or byte buffer
178    /// directly into the SQL parameter.
179    fn encode_owned(self) -> OwnedPostgresValue {
180        self.encode().into_owned()
181    }
182}