rainier_orm/string_column.rs
1//! Support for custom column types stored as text — the common case being a
2//! Rust enum persisted as a string (`"pending"`, `"verified"`, …).
3//!
4//! Implement [`StringColumn`] for your type (how it maps to/from a string),
5//! then `impl_string_column!(YourType)` to wire it into the ORM: it gets a
6//! `Text` [`SqlType`](crate::SqlType), and [`ToColumn`](crate::ToColumn) /
7//! [`FromColumn`](crate::FromColumn) impls (for both `T` and `Option<T>`) so it
8//! can be a `#[derive(Entity)]` field like any primitive.
9//!
10//! ```ignore
11//! use rainier_orm::{StringColumn, impl_string_column, Result, Error};
12//!
13//! #[derive(Clone, PartialEq)]
14//! enum Status { Pending, Verified, Rejected }
15//!
16//! impl StringColumn for Status {
17//! fn to_column_str(&self) -> String {
18//! match self { Status::Pending => "pending", Status::Verified => "verified", Status::Rejected => "rejected" }.into()
19//! }
20//! fn from_column_str(s: &str) -> Result<Self> {
21//! match s {
22//! "pending" => Ok(Status::Pending),
23//! "verified" => Ok(Status::Verified),
24//! "rejected" => Ok(Status::Rejected),
25//! other => Err(Error::msg(format!("unknown Status `{other}`"))),
26//! }
27//! }
28//! }
29//! impl_string_column!(Status);
30//! ```
31
32/// How a custom type maps to and from its stored string form. The string is
33/// what lands in a `TEXT`/`VARCHAR` column.
34pub trait StringColumn: Sized {
35 fn to_column_str(&self) -> String;
36 fn from_column_str(s: &str) -> crate::Result<Self>;
37}
38
39/// Wire a [`StringColumn`] type into the ORM's column traits. See the
40/// [module docs](crate::string_column).
41#[macro_export]
42macro_rules! impl_string_column {
43 ($t:ty) => {
44 impl $crate::SqlType for $t {
45 const COLUMN_TYPE: $crate::ColumnType = $crate::ColumnType::Text;
46 }
47 impl $crate::ToColumn for $t {
48 fn to_value(&self) -> $crate::sea_query::Value {
49 $crate::sea_query::Value::String(::core::option::Option::Some(
50 ::std::boxed::Box::new($crate::StringColumn::to_column_str(self)),
51 ))
52 }
53 }
54 impl $crate::FromColumn for $t {
55 fn from_column(row: &dyn $crate::Row, col: &str) -> $crate::Result<Self> {
56 let s = $crate::Row::get_string(row, col)?
57 .ok_or_else(|| $crate::Error::msg(::std::format!("column `{col}` was NULL")))?;
58 <$t as $crate::StringColumn>::from_column_str(&s)
59 }
60 }
61 // `Option<$t>` support comes from the crate's blanket impls (driven by
62 // the `Text` SqlType above) — the orphan rule forbids implementing it
63 // here, and it isn't needed.
64 };
65}