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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
mod column;
mod table;
mod value;
#[cfg(not(feature = "std"))]
use crate::prelude::*;
pub use column::*;
use core::any::Any;
use drizzle_core::error::DrizzleError;
pub use table::*;
pub use value::*;
use crate::values::{OwnedPostgresValue, PostgresValue};
/// Trait for `PostgreSQL` native enum types that can be used as dyn objects
#[allow(clippy::wrong_self_convention)]
pub trait PostgresEnum: Send + Sync + Any {
/// Get the enum type name for `PostgreSQL`
fn enum_type_name(&self) -> &'static str;
fn as_enum(&self) -> &dyn PostgresEnum;
/// Get the string representation of this enum variant
fn variant_name(&self) -> &'static str;
/// Clone this enum as a boxed trait object
fn into_boxed(&self) -> Box<dyn PostgresEnum>;
/// Try to create this enum from a string value
///
/// # Errors
///
/// Returns [`DrizzleError::ConversionError`] when `value` is not a valid
/// variant name for this enum.
fn try_from_str(value: &str) -> Result<Self, DrizzleError>
where
Self: Sized;
}
impl core::fmt::Debug for &dyn PostgresEnum {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PostgresSQLEnum")
.field("type", &self.enum_type_name())
.field("variant", &self.variant_name())
.finish()
}
}
impl PartialEq for &dyn PostgresEnum {
fn eq(&self, other: &Self) -> bool {
self.enum_type_name() == other.enum_type_name()
&& self.variant_name() == other.variant_name()
}
}
impl core::fmt::Debug for Box<dyn PostgresEnum> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PostgresSQLEnum")
.field("type", &self.enum_type_name())
.field("variant", &self.variant_name())
.finish()
}
}
impl Clone for Box<dyn PostgresEnum> {
fn clone(&self) -> Self {
self.into_boxed()
}
}
impl PartialEq for Box<dyn PostgresEnum> {
fn eq(&self, other: &Self) -> bool {
self.enum_type_name() == other.enum_type_name()
&& self.variant_name() == other.variant_name()
}
}
/// Marker trait for custom Rust types that map to a `PostgreSQL` column.
///
/// Generated by `#[derive(PostgresEnum)]`. The table macro uses this trait to
/// auto-detect enum types without requiring `#[column(ENUM)]`.
///
/// # Associated Constants
///
/// - `SQLType`: The Drizzle SQL type marker used for typed expressions
/// - `SQL_TYPE`: The `PostgreSQL` column type (e.g. `"text"`, `"integer"`, or the lowercased enum name)
/// - `NEEDS_CREATE_TYPE`: Whether this requires a `CREATE TYPE` (native PG enum)
///
/// # Required Methods
///
/// - `decode`: Read self from a postgres Row at the given index
/// - `encode`: Convert self to a `PostgresValue` for insertion/updates
///
/// The blanket `From<Self> for PostgresValue` owns the encoded value because
/// insert/update models may store SQL fragments after the source value is
/// dropped. Call `encode()` directly when you need an immediate borrowed value.
/// Override `encode_owned()` when consuming `self` can avoid cloning owned data.
#[cfg(any(feature = "postgres-sync", feature = "tokio-postgres"))]
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be used as a PostgreSQL column type",
note = "add #[derive(PostgresEnum)] for enum types, or use a supported primitive type"
)]
pub trait DrizzlePostgresColumn: Sized {
/// Drizzle SQL type marker for this column.
///
/// Use one of the built-in PostgreSQL markers, such as `Text`, `Int4`,
/// `Bytea`, `Boolean`, `Numeric`, `Enum`, or `Any`.
type SQLType: drizzle_core::types::DataType;
/// `PostgreSQL` column type: `"text"`, `"integer"`, or native enum type name
const SQL_TYPE: &'static str;
/// Whether this requires a `CREATE TYPE` (native PG enum).
const NEEDS_CREATE_TYPE: bool = false;
/// Decode self from a postgres Row at the given index.
///
/// # Errors
///
/// Returns [`DrizzleError::ConversionError`] when the column at `idx`
/// cannot be decoded into this type.
fn decode(row: &crate::Row, idx: usize) -> Result<Self, DrizzleError>;
/// Convert self to a `PostgresValue` for insertion/updates.
fn encode(&self) -> PostgresValue<'_>;
/// Convert self to an owned `PostgreSQL` value for stored bind parameters.
///
/// The default implementation owns the borrowed result of [`encode`](Self::encode).
/// Override this for wrappers that can move an internal string or byte buffer
/// directly into the SQL parameter.
fn encode_owned(self) -> OwnedPostgresValue {
self.encode().into_owned()
}
}
impl<'a, T> From<T> for PostgresValue<'a>
where
T: DrizzlePostgresColumn,
{
fn from(value: T) -> Self {
value.encode_owned().into()
}
}
/// Stub trait when no postgres driver is enabled — allows enum derives to compile
/// without a driver feature, but the table macro's `TryFrom` impls won't be generated.
///
/// The blanket `From<Self> for PostgresValue` owns the encoded value because
/// insert/update models may store SQL fragments after the source value is
/// dropped. Call `encode()` directly when you need an immediate borrowed value.
/// Override `encode_owned()` when consuming `self` can avoid cloning owned data.
#[cfg(not(any(feature = "postgres-sync", feature = "tokio-postgres")))]
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be used as a PostgreSQL column type",
note = "add #[derive(PostgresEnum)] for enum types, or use a supported primitive type"
)]
pub trait DrizzlePostgresColumn: Sized {
/// Drizzle SQL type marker for this column.
///
/// Use one of the built-in PostgreSQL markers, such as `Text`, `Int4`,
/// `Bytea`, `Boolean`, `Numeric`, `Enum`, or `Any`.
type SQLType: drizzle_core::types::DataType;
/// `PostgreSQL` column type: `"text"`, `"integer"`, or native enum type name
const SQL_TYPE: &'static str;
/// Whether this requires a `CREATE TYPE` (native PG enum).
const NEEDS_CREATE_TYPE: bool = false;
/// Convert self to a `PostgresValue` for insertion/updates.
fn encode(&self) -> PostgresValue<'_>;
/// Convert self to an owned `PostgreSQL` value for stored bind parameters.
///
/// The default implementation owns the borrowed result of [`encode`](Self::encode).
/// Override this for wrappers that can move an internal string or byte buffer
/// directly into the SQL parameter.
fn encode_owned(self) -> OwnedPostgresValue {
self.encode().into_owned()
}
}