use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Auto<T> {
Unset,
Set(T),
}
impl<T> Default for Auto<T> {
fn default() -> Self {
Self::Unset
}
}
impl<T> From<T> for Auto<T> {
fn from(v: T) -> Self {
Self::Set(v)
}
}
impl<T> Auto<T> {
#[must_use]
pub fn get(&self) -> Option<&T> {
match self {
Self::Set(v) => Some(v),
Self::Unset => None,
}
}
#[must_use]
pub fn is_unset(&self) -> bool {
matches!(self, Self::Unset)
}
#[must_use]
pub fn is_set(&self) -> bool {
matches!(self, Self::Set(_))
}
pub fn take(&mut self) -> Option<T> {
match std::mem::replace(self, Self::Unset) {
Self::Set(v) => Some(v),
Self::Unset => None,
}
}
#[must_use]
pub fn into_inner(self) -> Option<T> {
match self {
Self::Set(v) => Some(v),
Self::Unset => None,
}
}
}
impl<T> From<Auto<T>> for crate::core::SqlValue
where
T: Into<crate::core::SqlValue>,
{
fn from(a: Auto<T>) -> Self {
match a {
Auto::Unset => Self::Null,
Auto::Set(v) => v.into(),
}
}
}
impl<'r, T> sqlx::Decode<'r, sqlx::Postgres> for Auto<T>
where
T: sqlx::Decode<'r, sqlx::Postgres>,
{
fn decode(
value: <sqlx::Postgres as sqlx::Database>::ValueRef<'r>,
) -> Result<Self, sqlx::error::BoxDynError> {
Ok(Self::Set(T::decode(value)?))
}
}
impl<T> sqlx::Type<sqlx::Postgres> for Auto<T>
where
T: sqlx::Type<sqlx::Postgres>,
{
fn type_info() -> sqlx::postgres::PgTypeInfo {
T::type_info()
}
fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
T::compatible(ty)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_unset() {
let a: Auto<i64> = Auto::default();
assert!(a.is_unset());
assert!(!a.is_set());
assert!(a.get().is_none());
}
#[test]
fn from_t_is_set() {
let a: Auto<i64> = 42_i64.into();
assert!(a.is_set());
assert_eq!(a.get(), Some(&42));
}
#[test]
fn into_inner_returns_value_or_none() {
assert_eq!(Auto::Set(7_i64).into_inner(), Some(7));
assert_eq!(Auto::<i64>::Unset.into_inner(), None);
}
#[test]
fn take_leaves_unset_behind() {
let mut a: Auto<i64> = 99_i64.into();
let v = a.take();
assert_eq!(v, Some(99));
assert!(a.is_unset());
assert_eq!(a.take(), None);
}
#[test]
fn serde_round_trip_set() {
let a: Auto<i64> = Auto::Set(42);
let json = serde_json::to_string(&a).unwrap();
let back: Auto<i64> = serde_json::from_str(&json).unwrap();
assert_eq!(back, Auto::Set(42));
}
#[test]
fn serde_round_trip_unset() {
let a: Auto<i64> = Auto::Unset;
let json = serde_json::to_string(&a).unwrap();
let back: Auto<i64> = serde_json::from_str(&json).unwrap();
assert_eq!(back, Auto::Unset);
}
}