1
0
Fork 0
forked from 2sys/phonograph
phonograph/phono-models/src/datum.rs

68 lines
2.6 KiB
Rust
Raw Normal View History

2025-11-11 01:26:48 +00:00
use bigdecimal::BigDecimal;
2025-09-08 15:56:57 -07:00
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::{Postgres, QueryBuilder};
2025-09-08 15:56:57 -07:00
use uuid::Uuid;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(tag = "t", content = "c")]
pub enum Datum {
2025-11-11 01:26:48 +00:00
// BigDecimal is used because a user may insert a value directly via SQL
// which overflows the representational space of `rust_decimal::Decimal`.
// Note that by default, [`BigDecimal`] serializes to JSON as a string. This
// behavior can be modified, but it's a pain when paired with the [`Option`]
// type. String representation should be acceptable for the UI, as [`Datum`]
// values should always be parsed through Zod, which can coerce the value to
// a number transparently.
Numeric(Option<BigDecimal>),
2025-09-08 15:56:57 -07:00
Text(Option<String>),
Timestamp(Option<DateTime<Utc>>),
Uuid(Option<Uuid>),
}
impl Datum {
2025-09-08 15:56:57 -07:00
// TODO: Can something similar be achieved with a generic return type?
/// Bind this as a parameter to a sqlx query.
pub fn bind_onto<'a>(
self,
query: sqlx::query::Query<'a, Postgres, <sqlx::Postgres as sqlx::Database>::Arguments<'a>>,
) -> sqlx::query::Query<'a, Postgres, <sqlx::Postgres as sqlx::Database>::Arguments<'a>> {
match self {
2025-11-11 01:26:48 +00:00
Self::Numeric(value) => query.bind(value),
2025-09-08 15:56:57 -07:00
Self::Text(value) => query.bind(value),
Self::Timestamp(value) => query.bind(value),
Self::Uuid(value) => query.bind(value),
}
}
/// Push this as a parameter to a [`QueryBuilder`].
pub fn push_bind_onto(self, builder: &mut QueryBuilder<'_, Postgres>) {
match self {
Self::Numeric(value) => builder.push_bind(value),
Self::Text(value) => builder.push_bind(value),
Self::Timestamp(value) => builder.push_bind(value),
Self::Uuid(value) => builder.push_bind(value),
};
}
2025-09-08 15:56:57 -07:00
/// Transform the contained value into a serde_json::Value.
pub fn inner_as_value(&self) -> serde_json::Value {
let serialized = serde_json::to_value(self).unwrap();
#[derive(Deserialize)]
struct Tagged {
c: serde_json::Value,
}
let deserialized: Tagged = serde_json::from_value(serialized).unwrap();
deserialized.c
}
pub fn is_none(&self) -> bool {
match self {
2025-11-11 01:26:48 +00:00
Self::Numeric(None) | Self::Text(None) | Self::Timestamp(None) | Self::Uuid(None) => {
true
}
Self::Numeric(_) | Self::Text(_) | Self::Timestamp(_) | Self::Uuid(_) => false,
2025-09-08 15:56:57 -07:00
}
}
}