use std::ops::Deref;
use postgres_types::{FromSql, IsNull, ToSql, Type};
use bytes::{BufMut, BytesMut};
use byteorder::{BigEndian, ReadBytesExt};
use jiff::{SignedDuration, Span, SpanRelativeTo};
use std::error::Error;
#[derive(Debug, Clone)]
pub struct JiffSpan(pub Span);
impl JiffSpan {
#[must_use]
pub const fn into_inner(self) -> Span {
self.0
}
pub fn to_duration(self) -> Result<SignedDuration, jiff::Error> {
let span = self.into_inner();
let rel = SpanRelativeTo::days_are_24_hours();
span.to_duration(rel)
}
}
impl Deref for JiffSpan {
type Target = Span;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a> FromSql<'a> for JiffSpan {
fn from_sql(
ty: &Type,
raw: &'a [u8]
) -> Result<Self, Box<dyn Error + Sync + Send>> {
if !matches!(*ty, Type::INTERVAL) {
return Err("Type must be INTERVAL".into());
}
let mut reader = raw;
let micros = reader.read_i64::<BigEndian>()?;
let days = reader.read_i32::<BigEndian>()?;
let months = reader.read_i32::<BigEndian>()?;
let span = Span::new().months(months).days(days).microseconds(micros);
Ok(Self(span))
}
fn accepts(ty: &Type) -> bool {
matches!(*ty, Type::INTERVAL)
}
}
impl ToSql for JiffSpan {
fn to_sql(
&self,
ty: &Type,
out: &mut BytesMut
) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
if !matches!(*ty, Type::INTERVAL) {
return Err("Type must be INTERVAL".into());
}
let span = &self.0;
let ymonths = i32::from(span.get_years());
let ymonths = ymonths
.checked_mul(12)
.ok_or("Month overflow for Postgres Interval")?;
let total_months = ymonths
.checked_add(span.get_months())
.ok_or("Month overflow for Postgres Interval")?;
let total_days = span.get_days();
let total_micros = (i64::from(span.get_hours()) * 3_600_000_000)
+ (span.get_minutes() * 60_000_000)
+ (span.get_seconds() * 1_000_000)
+ (span.get_milliseconds() * 1_000)
+ (span.get_microseconds());
out.put_i64(total_micros);
out.put_i32(total_days);
out.put_i32(total_months);
Ok(IsNull::No)
}
fn accepts(ty: &Type) -> bool {
matches!(*ty, Type::INTERVAL)
}
fn to_sql_checked(
&self,
ty: &Type,
out: &mut BytesMut
) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
self.to_sql(ty, out)
}
}