Skip to content

Commit

Permalink
Added support for time and made chrono/time optional
Browse files Browse the repository at this point in the history
Refactored impl_AsDbType macro
Closes #45
  • Loading branch information
gammelalf committed Sep 4, 2023
1 parent 57caae0 commit a2314ab
Show file tree
Hide file tree
Showing 9 changed files with 324 additions and 305 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ uuid = { version = "~1", optional = true }
linkme = { version = "~0.3" }

# Time and date library
chrono = { version = ">=0.4.20", default-features = false }
chrono = { version = ">=0.4.20", default-features = false, optional = true }
time = { version = "~0.3", optional = true }

# Allow wrapping futures and streams
pin-project = { version = "~1" }
Expand Down
45 changes: 38 additions & 7 deletions src/conditions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use std::borrow::Cow;

use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
// use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
use rorm_db::sql::{conditional, value};

use crate::internal::field::RawField;
Expand Down Expand Up @@ -79,11 +79,29 @@ pub enum Value<'a> {
/// binary representation
Binary(Cow<'a, [u8]>),
/// Naive Time representation
NaiveTime(NaiveTime),
#[cfg(feature = "chrono")]
ChronoNaiveTime(chrono::NaiveTime),
/// Naive Date representation
NaiveDate(NaiveDate),
#[cfg(feature = "chrono")]
ChronoNaiveDate(chrono::NaiveDate),
/// Naive DateTime representation
NaiveDateTime(NaiveDateTime),
#[cfg(feature = "chrono")]
ChronoNaiveDateTime(chrono::NaiveDateTime),
/// DateTime representation
#[cfg(feature = "chrono")]
ChronoDateTime(chrono::DateTime<chrono::Utc>),
/// time's date representation
#[cfg(feature = "time")]
TimeDate(time::Date),
/// time's time representation
#[cfg(feature = "time")]
TimeTime(time::Time),
/// time's offset datetime representation
#[cfg(feature = "time")]
TimeOffsetDateTime(time::OffsetDateTime),
/// time's primitive datetime representation
#[cfg(feature = "time")]
TimePrimitiveDateTime(time::PrimitiveDateTime),
}
impl<'a> Value<'a> {
/// Convert into an [`sql::Value`](value::Value) instead of an [`sql::Condition`](conditional::Condition) directly.
Expand All @@ -99,9 +117,22 @@ impl<'a> Value<'a> {
Value::F64(v) => value::Value::F64(*v),
Value::F32(v) => value::Value::F32(*v),
Value::Binary(v) => value::Value::Binary(v.as_ref()),
Value::NaiveTime(v) => value::Value::ChronoNaiveTime(*v),
Value::NaiveDate(v) => value::Value::ChronoNaiveDate(*v),
Value::NaiveDateTime(v) => value::Value::ChronoNaiveDateTime(*v),
#[cfg(feature = "chrono")]
Value::ChronoNaiveTime(v) => value::Value::ChronoNaiveTime(*v),
#[cfg(feature = "chrono")]
Value::ChronoNaiveDate(v) => value::Value::ChronoNaiveDate(*v),
#[cfg(feature = "chrono")]
Value::ChronoNaiveDateTime(v) => value::Value::ChronoNaiveDateTime(*v),
#[cfg(feature = "chrono")]
Value::ChronoDateTime(v) => value::Value::ChronoDateTime(*v),
#[cfg(feature = "time")]
Value::TimeDate(v) => value::Value::TimeDate(*v),
#[cfg(feature = "time")]
Value::TimeTime(v) => value::Value::TimeTime(*v),
#[cfg(feature = "time")]
Value::TimeOffsetDateTime(v) => value::Value::TimeOffsetDateTime(*v),
#[cfg(feature = "time")]
Value::TimePrimitiveDateTime(v) => value::Value::TimePrimitiveDateTime(*v),
}
}
}
Expand Down
205 changes: 205 additions & 0 deletions src/fields/types/chrono.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
use std::marker::PhantomData;

use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
use rorm_db::Error::DecodeError;
use rorm_db::{Error, Row};
use rorm_declaration::imr;

use crate::conditions::Value;
use crate::crud::decoder::{Decoder, DirectDecoder};
use crate::fields::traits::FieldType;
use crate::internal::field::as_db_type::AsDbType;
use crate::internal::field::decoder::FieldDecoder;
use crate::internal::field::{kind, AbstractField, ContainerField, FieldProxy, RawField};
use crate::internal::hmr::annotations::Annotations;
use crate::internal::hmr::{db_type, Source};
use crate::internal::query_context::QueryContext;
use crate::internal::relation_path::Path;
use crate::model::ConstNew;
use crate::{const_concat, impl_AsDbType, new_converting_decoder, sealed};

impl_AsDbType!(NaiveTime, db_type::Time, Value::ChronoNaiveTime);
impl_AsDbType!(NaiveDate, db_type::Date, Value::ChronoNaiveDate);
impl_AsDbType!(NaiveDateTime, db_type::DateTime, Value::ChronoNaiveDateTime);
impl_AsDbType!(DateTime<Utc>, db_type::DateTime, Value::ChronoDateTime);

/// Implementation detail of [`DateTime<FixedOffset>`]
const _: () = {
new_converting_decoder!(
/// [`FieldDecoder`] for [`FixedOffset`]
FixedOffsetDecoder,
|value: i32| -> FixedOffset {
FixedOffset::east_opt(value)
.ok_or_else(|| DecodeError(format!("Couldn't decode fixed offset: {value}")))
}
);
impl FieldType for FixedOffset {
type Kind = kind::AsDbType;

type Columns<T> = [T; 1];

fn into_values(self) -> Self::Columns<Value<'static>> {
[Value::I32(self.local_minus_utc())]
}

fn as_values(&self) -> Self::Columns<Value<'_>> {
[Value::I32(self.local_minus_utc())]
}

type Decoder = FixedOffsetDecoder;
}
impl AsDbType for FixedOffset {
type Primitive = i32;
type DbType = db_type::Int32;

fn from_primitive(primitive: Self::Primitive) -> Self {
FixedOffset::east_opt(primitive).unwrap() // TODO handle this
}
}
new_converting_decoder!(
/// [`FieldDecoder`] for [`Option<FixedOffset>`](FixedOffset)
OptionFixedOffsetDecoder,
|value: Option<i32>| -> Option<FixedOffset> {
value
.map(|value| {
FixedOffset::east_opt(value).ok_or_else(|| {
DecodeError(format!("Couldn't decode fixed offset: {value}"))
})
})
.transpose()
}
);
impl_AsDbType!(Option<FixedOffset>, OptionFixedOffsetDecoder);

impl FieldType for DateTime<FixedOffset> {
type Kind = kind::DateTime;

type Columns<T> = [T; 2];

fn into_values(self) -> Self::Columns<Value<'static>> {
let [offset] = self.offset().into_values();
[offset, Value::ChronoNaiveDateTime(self.naive_utc())]
}

fn as_values(&self) -> Self::Columns<Value<'_>> {
let [offset] = self.offset().as_values();
[offset, Value::ChronoNaiveDateTime(self.naive_utc())]
}

type Decoder = DateTimeDecoder;
}

/// [`FieldDecoder`] for [`DateTime<FixedOffset>`]
pub struct DateTimeDecoder {
offset: FixedOffsetDecoder,
utc: DirectDecoder<NaiveDateTime>,
}
impl Decoder for DateTimeDecoder {
type Result = DateTime<FixedOffset>;

fn by_name(&self, row: &Row) -> Result<Self::Result, Error> {
let offset = self.offset.by_name(row)?;
let utc = self.utc.by_name(row)?;
Ok(offset.from_utc_datetime(&utc))
}

fn by_index(&self, row: &Row) -> Result<Self::Result, Error> {
let offset = self.offset.by_index(row)?;
let utc = self.utc.by_index(row)?;
Ok(offset.from_utc_datetime(&utc))
}
}
impl FieldDecoder for DateTimeDecoder {
fn new<F, P>(ctx: &mut QueryContext, _: FieldProxy<F, P>) -> Self
where
F: RawField<Type = Self::Result>,
P: Path,
{
Self {
offset: FixedOffsetDecoder::new(ctx, FieldProxy::<__DateTime_offset<F>, P>::new()),
utc: DirectDecoder::new(ctx, FieldProxy::<__DateTime_utc<F>, P>::new()),
}
}
}
impl<F> AbstractField<kind::DateTime> for F
where
F: RawField<Kind = kind::DateTime, Type = DateTime<FixedOffset>>,
{
sealed!(impl);

fn push_imr(self, imr: &mut Vec<imr::Field>) {
__DateTime_offset::<F>::new().push_imr(imr);
__DateTime_utc::<F>::new().push_imr(imr);
}

const COLUMNS: &'static [&'static str] =
&[__DateTime_offset::<F>::NAME, __DateTime_utc::<F>::NAME];
}
impl<F, P> ContainerField<P, kind::DateTime> for F
where
P: Path,
F: RawField<Kind = kind::DateTime, Type = DateTime<FixedOffset>>,
{
type Target = __DateTime_Fields<F, P>;
}

/// [`DateTime<FixedOffset>`]'s internal fields
#[allow(non_camel_case_types)]
pub struct __DateTime_Fields<F, P> {
/// [`DateTime<FixedOffset>`]'s internal offset field
pub offset: FieldProxy<__DateTime_offset<F>, P>,

/// [`DateTime<FixedOffset>`]'s internal offset field
pub utc: FieldProxy<__DateTime_utc<F>, P>,
}
impl<F, P: 'static> ConstNew for __DateTime_Fields<F, P>
where
F: RawField<Kind = kind::DateTime, Type = DateTime<FixedOffset>>,
{
const NEW: Self = __DateTime_Fields {
offset: FieldProxy::new(),
utc: FieldProxy::new(),
};
const REF: &'static Self = &Self::NEW;
}

/// [`DateTime<FixedOffset>`]'s internal offset field
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
pub struct __DateTime_offset<F>(PhantomData<F>);
impl<F> RawField for __DateTime_offset<F>
where
F: RawField<Type = DateTime<FixedOffset>>,
{
type Kind = kind::AsDbType;
type Type = FixedOffset;
type Model = F::Model;
const INDEX: usize = F::INDEX + 1;
const NAME: &'static str = const_concat!(&[F::NAME, "_offset"]);
const EXPLICIT_ANNOTATIONS: Annotations = F::EXPLICIT_ANNOTATIONS;
const SOURCE: Option<Source> = F::SOURCE;
fn new() -> Self {
Self(PhantomData)
}
}

/// [`DateTime<FixedOffset>`]'s internal offset field
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
pub struct __DateTime_utc<F>(PhantomData<F>);
impl<F> RawField for __DateTime_utc<F>
where
F: RawField<Type = DateTime<FixedOffset>>,
{
type Kind = kind::AsDbType;
type Type = NaiveDateTime;
type Model = F::Model;
const INDEX: usize = F::INDEX + 2;
const NAME: &'static str = const_concat!(&[F::NAME, "_utc"]);
const EXPLICIT_ANNOTATIONS: Annotations = F::EXPLICIT_ANNOTATIONS;
const SOURCE: Option<Source> = F::SOURCE;
fn new() -> Self {
Self(PhantomData)
}
}
};
4 changes: 4 additions & 0 deletions src/fields/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
//! See [`rorm::fields`](crate::fields) for full list of supported field types

mod back_ref;
#[cfg(feature = "chrono")]
mod chrono;
mod foreign_model;
mod json;
#[cfg(feature = "msgpack")]
mod msgpack;
#[cfg(feature = "time")]
mod time;
#[cfg(feature = "uuid")]
mod uuid;

Expand Down
16 changes: 16 additions & 0 deletions src/fields/types/time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use crate::conditions::Value;
use crate::impl_AsDbType;
use crate::internal::hmr::db_type;

impl_AsDbType!(time::Time, db_type::Time, Value::TimeTime);
impl_AsDbType!(time::Date, db_type::Date, Value::TimeDate);
impl_AsDbType!(
time::OffsetDateTime,
db_type::DateTime,
Value::TimeOffsetDateTime
);
impl_AsDbType!(
time::PrimitiveDateTime,
db_type::DateTime,
Value::TimePrimitiveDateTime
);
4 changes: 2 additions & 2 deletions src/fields/types/uuid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::internal::field::as_db_type::AsDbType;
use crate::internal::field::kind;
use crate::internal::hmr::db_type;
use crate::Error::DecodeError;
use crate::{impl_option_as_db_type, new_converting_decoder};
use crate::{impl_AsDbType, new_converting_decoder};

new_converting_decoder!(UuidDecoder, |value: Vec<u8>| -> Uuid {
Uuid::from_slice(&value).map_err(|err| DecodeError(format!("Couldn't decode uuid: {err}")))
Expand Down Expand Up @@ -48,4 +48,4 @@ new_converting_decoder!(
.transpose()
}
);
impl_option_as_db_type!(Uuid, OptionUuidDecoder);
impl_AsDbType!(Option<Uuid>, OptionUuidDecoder);
Loading

0 comments on commit a2314ab

Please sign in to comment.