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 /// Schema the custom type lives in (native PG enums with
117 /// `#[postgres_enum(schema = "...")]`). Defaults to `public`.
118 const SCHEMA: &'static str = "public";
119
120 /// Decode self from a postgres Row at the given index.
121 ///
122 /// # Errors
123 ///
124 /// Returns [`DrizzleError::ConversionError`] when the column at `idx`
125 /// cannot be decoded into this type.
126 fn decode(row: &crate::Row, idx: usize) -> Result<Self, DrizzleError>;
127
128 /// Convert self to a `PostgresValue` for insertion/updates.
129 fn encode(&self) -> PostgresValue<'_>;
130
131 /// Convert self to an owned `PostgreSQL` value for stored bind parameters.
132 ///
133 /// The default implementation owns the borrowed result of [`encode`](Self::encode).
134 /// Override this for wrappers that can move an internal string or byte buffer
135 /// directly into the SQL parameter.
136 fn encode_owned(self) -> OwnedPostgresValue {
137 self.encode().into_owned()
138 }
139}
140
141impl<'a, T> From<T> for PostgresValue<'a>
142where
143 T: DrizzlePostgresColumn,
144{
145 fn from(value: T) -> Self {
146 value.encode_owned().into()
147 }
148}
149
150/// Stub trait when no postgres driver is enabled — allows enum derives to compile
151/// without a driver feature, but the table macro's `TryFrom` impls won't be generated.
152///
153/// The blanket `From<Self> for PostgresValue` owns the encoded value because
154/// insert/update models may store SQL fragments after the source value is
155/// dropped. Call `encode()` directly when you need an immediate borrowed value.
156/// Override `encode_owned()` when consuming `self` can avoid cloning owned data.
157#[cfg(not(any(feature = "postgres-sync", feature = "tokio-postgres")))]
158#[diagnostic::on_unimplemented(
159 message = "`{Self}` cannot be used as a PostgreSQL column type",
160 note = "add #[derive(PostgresEnum)] for enum types, or use a supported primitive type"
161)]
162pub trait DrizzlePostgresColumn: Sized {
163 /// Drizzle SQL type marker for this column.
164 ///
165 /// Use one of the built-in PostgreSQL markers, such as `Text`, `Int4`,
166 /// `Bytea`, `Boolean`, `Numeric`, `Enum`, or `Any`.
167 type SQLType: drizzle_core::types::DataType;
168
169 /// `PostgreSQL` column type: `"text"`, `"integer"`, or native enum type name
170 const SQL_TYPE: &'static str;
171
172 /// Whether this requires a `CREATE TYPE` (native PG enum).
173 const NEEDS_CREATE_TYPE: bool = false;
174
175 /// Schema the custom type lives in (native PG enums with
176 /// `#[postgres_enum(schema = "...")]`). Defaults to `public`.
177 const SCHEMA: &'static str = "public";
178
179 /// Convert self to a `PostgresValue` for insertion/updates.
180 fn encode(&self) -> PostgresValue<'_>;
181
182 /// Convert self to an owned `PostgreSQL` value for stored bind parameters.
183 ///
184 /// The default implementation owns the borrowed result of [`encode`](Self::encode).
185 /// Override this for wrappers that can move an internal string or byte buffer
186 /// directly into the SQL parameter.
187 fn encode_owned(self) -> OwnedPostgresValue {
188 self.encode().into_owned()
189 }
190}