#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum RullstValue {
String(String),
Int(i32),
Float(f64),
Bool(bool),
I64(i64),
Null,
DateTime(chrono::DateTime<chrono::Utc>),
NaiveDateTime(chrono::NaiveDateTime),
Date(chrono::NaiveDate),
Time(chrono::NaiveTime),
}
type QueryArguments = <crate::RullstDatabase as sqlx::Database>::Arguments;
#[doc(hidden)]
pub trait BindRullstValue: Sized {
fn bind_rullst_value(self, value: &RullstValue) -> Self;
}
macro_rules! bind_rullst_value {
($query:expr, $value:expr) => {
match $value {
RullstValue::String(value) => $query.bind(value.clone()),
RullstValue::Int(value) => $query.bind(*value),
RullstValue::Float(value) => $query.bind(*value),
RullstValue::Bool(value) => $query.bind(*value),
RullstValue::I64(value) => $query.bind(*value),
RullstValue::Null => $query.bind(None::<String>),
RullstValue::DateTime(value) => $query.bind(temporal::date_time(value)),
RullstValue::NaiveDateTime(value) => $query.bind(temporal::naive_date_time(value)),
RullstValue::Date(value) => $query.bind(temporal::date(value)),
RullstValue::Time(value) => $query.bind(temporal::time(value)),
}
};
}
#[cfg(not(any(
feature = "strict-postgres",
feature = "strict-mysql",
feature = "strict-sqlite"
)))]
mod temporal {
use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, SecondsFormat, Utc};
pub(super) fn date_time(value: &DateTime<Utc>) -> String {
value.to_rfc3339_opts(SecondsFormat::AutoSi, false)
}
pub(super) fn naive_date_time(value: &NaiveDateTime) -> String {
value.format("%F %T%.f").to_string()
}
pub(super) fn date(value: &NaiveDate) -> String {
value.format("%F").to_string()
}
pub(super) fn time(value: &NaiveTime) -> String {
value.format("%T%.f").to_string()
}
}
#[cfg(any(
feature = "strict-postgres",
feature = "strict-mysql",
feature = "strict-sqlite"
))]
mod temporal {
use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc};
pub(super) fn date_time(value: &DateTime<Utc>) -> DateTime<Utc> {
*value
}
pub(super) fn naive_date_time(value: &NaiveDateTime) -> NaiveDateTime {
*value
}
pub(super) fn date(value: &NaiveDate) -> NaiveDate {
*value
}
pub(super) fn time(value: &NaiveTime) -> NaiveTime {
*value
}
}
impl BindRullstValue for sqlx::query::Query<'_, crate::RullstDatabase, QueryArguments> {
fn bind_rullst_value(self, value: &RullstValue) -> Self {
bind_rullst_value!(self, value)
}
}
impl<O> BindRullstValue for sqlx::query::QueryAs<'_, crate::RullstDatabase, O, QueryArguments> {
fn bind_rullst_value(self, value: &RullstValue) -> Self {
bind_rullst_value!(self, value)
}
}
impl From<&str> for RullstValue {
fn from(s: &str) -> Self {
RullstValue::String(s.to_string())
}
}
impl From<String> for RullstValue {
fn from(s: String) -> Self {
RullstValue::String(s)
}
}
impl From<i32> for RullstValue {
fn from(i: i32) -> Self {
RullstValue::Int(i)
}
}
impl From<f64> for RullstValue {
fn from(f: f64) -> Self {
RullstValue::Float(f)
}
}
impl From<bool> for RullstValue {
fn from(b: bool) -> Self {
RullstValue::Bool(b)
}
}
impl From<i64> for RullstValue {
fn from(value: i64) -> Self {
RullstValue::I64(value)
}
}
impl From<u32> for RullstValue {
fn from(value: u32) -> Self {
RullstValue::I64(i64::from(value))
}
}
impl From<chrono::DateTime<chrono::Utc>> for RullstValue {
fn from(value: chrono::DateTime<chrono::Utc>) -> Self {
RullstValue::DateTime(value)
}
}
impl From<chrono::NaiveDateTime> for RullstValue {
fn from(value: chrono::NaiveDateTime) -> Self {
RullstValue::NaiveDateTime(value)
}
}
impl From<chrono::NaiveDate> for RullstValue {
fn from(value: chrono::NaiveDate) -> Self {
RullstValue::Date(value)
}
}
impl From<chrono::NaiveTime> for RullstValue {
fn from(value: chrono::NaiveTime) -> Self {
RullstValue::Time(value)
}
}
impl<T: Into<RullstValue>> From<Option<T>> for RullstValue {
fn from(value: Option<T>) -> Self {
match value {
Some(value) => value.into(),
None => RullstValue::Null,
}
}
}
impl TryFrom<RullstValue> for String {
type Error = &'static str;
fn try_from(val: RullstValue) -> Result<Self, Self::Error> {
match val {
RullstValue::String(s) => Ok(s),
_ => Err("Not a string"),
}
}
}
impl TryFrom<RullstValue> for i32 {
type Error = &'static str;
fn try_from(val: RullstValue) -> Result<Self, Self::Error> {
match val {
RullstValue::Int(i) => Ok(i),
_ => Err("Not an i32"),
}
}
}
impl TryFrom<RullstValue> for i64 {
type Error = &'static str;
fn try_from(val: RullstValue) -> Result<Self, Self::Error> {
match val {
RullstValue::I64(i) => Ok(i),
RullstValue::Int(i) => Ok(i64::from(i)),
_ => Err("Not an i64"),
}
}
}
impl TryFrom<RullstValue> for f64 {
type Error = &'static str;
fn try_from(val: RullstValue) -> Result<Self, Self::Error> {
match val {
RullstValue::Float(f) => Ok(f),
_ => Err("Not an f64"),
}
}
}
impl TryFrom<RullstValue> for bool {
type Error = &'static str;
fn try_from(val: RullstValue) -> Result<Self, Self::Error> {
match val {
RullstValue::Bool(b) => Ok(b),
_ => Err("Not a bool"),
}
}
}
#[cfg(test)]
#[path = "value_tests.rs"]
mod tests;