wtx 0.44.1

A collection of different transport implementations and related tools focused primarily on web technologies.
Documentation
use crate::{
  calendar::{Date, DateTime, Time, Utc},
  codec::{Decode, Encode},
  collection::ShortStrU8,
  database::{
    DatabaseError, Typed,
    client::mysql::{DecodeWrapper, EncodeWrapper, Mysql, Ty, ty_params::TyParams},
  },
  misc::Usize,
};
use core::any::type_name;

impl<E> Decode<'_, Mysql<E>> for Date
where
  E: From<crate::Error>,
{
  #[inline]
  fn decode(dw: &mut DecodeWrapper<'_, '_>) -> Result<Self, E> {
    date_decode(dw).map(|el| el.1).map_err(E::from)
  }
}
impl<E> Encode<Mysql<E>> for Date
where
  E: From<crate::Error>,
{
  #[inline]
  fn encode(&self, ew: &mut EncodeWrapper<'_>) -> Result<(), E> {
    date_encode(self, ew, 4).map_err(E::from)
  }
}
impl<E> Typed<Mysql<E>> for Date
where
  E: From<crate::Error>,
{
  #[inline]
  fn runtime_ty(&self) -> Option<TyParams> {
    <Self as Typed<Mysql<E>>>::static_ty()
  }

  #[inline]
  fn static_ty() -> Option<TyParams> {
    Some(TyParams::binary(Ty::Date))
  }
}

impl<E> Decode<'_, Mysql<E>> for DateTime<Utc>
where
  E: From<crate::Error>,
{
  #[inline]
  fn decode(dw: &mut DecodeWrapper<'_, '_>) -> Result<Self, E> {
    let (len, date, bytes) = date_decode(dw).map_err(E::from)?;
    Ok(if len > 4 {
      Self::new(date, time_decode(bytes)?, Utc)
    } else {
      Self::new(date, Time::default(), Utc)
    })
  }
}
impl<E> Encode<Mysql<E>> for DateTime<Utc>
where
  E: From<crate::Error>,
{
  #[inline]
  fn encode(&self, ew: &mut EncodeWrapper<'_>) -> Result<(), E> {
    let len = date_len(&self.time());
    date_encode(&self.date(), ew, len)?;
    if len > 4 {
      time_encode(&self.time(), len > 7, ew)?;
    }
    Ok(())
  }
}
impl<E> Typed<Mysql<E>> for DateTime<Utc>
where
  E: From<crate::Error>,
{
  #[inline]
  fn runtime_ty(&self) -> Option<TyParams> {
    <Self as Typed<Mysql<E>>>::static_ty()
  }

  #[inline]
  fn static_ty() -> Option<TyParams> {
    Some(TyParams::binary(Ty::Datetime))
  }
}

fn date_decode<'de>(dw: &mut DecodeWrapper<'de, '_>) -> crate::Result<(u8, Date, &'de [u8])> {
  let [len, year_a, year_b, month, day, rest @ ..] = dw.bytes() else {
    return Err(
      DatabaseError::UnexpectedBufferSize {
        expected: 5,
        received: Usize::from(dw.bytes().len()).into_saturating_u32(),
      }
      .into(),
    );
  };
  let year = i16::from_le_bytes([*year_a, *year_b]);
  let date = Date::from_ymd(year.try_into()?, (*month).try_into()?, (*day).try_into()?)?;
  Ok((*len, date, rest))
}

fn date_encode(date: &Date, ew: &mut EncodeWrapper<'_>, len: u8) -> crate::Result<()> {
  let year =
    u16::try_from(date.year().num()).map_err(|_err| DatabaseError::UnexpectedValueFromBytes {
      expected: ShortStrU8::new_truncated_u8(type_name::<Date>()),
    })?;
  let [year_a, year_b] = year.to_le_bytes();
  ew.buffer().extend_from_copyable_slice(&[
    len,
    year_a,
    year_b,
    date.month().num(),
    date.day().num(),
  ])?;
  Ok(())
}

const fn date_len(time: &Time) -> u8 {
  match (time.hour().num(), time.minute().num(), time.second().num(), time.nanosecond().num()) {
    (0, 0, 0, 0) => 4,
    (_, _, _, 0) => 7,
    (_, _, _, _) => 11,
  }
}

fn time_decode(bytes: &[u8]) -> crate::Result<Time> {
  let (hours, minutes, seconds, micros) = if let [hours, minutes, seconds, a, b, c, d] = bytes {
    (*hours, *minutes, *seconds, u32::from_le_bytes([*a, *b, *c, *d]))
  } else if let [hours, minutes, seconds] = bytes {
    (*hours, *minutes, *seconds, 0)
  } else {
    return Err(
      DatabaseError::UnexpectedBufferSize {
        expected: 3,
        received: Usize::from(bytes.len()).into_saturating_u32(),
      }
      .into(),
    );
  };
  Ok(Time::from_hms_us(
    hours.try_into()?,
    minutes.try_into()?,
    seconds.try_into()?,
    micros.try_into()?,
  ))
}

fn time_encode(time: &Time, include_micros: bool, ew: &mut EncodeWrapper<'_>) -> crate::Result<()> {
  let hour = time.hour().num();
  let minute = time.minute().num();
  let second = time.second().num();
  if include_micros {
    let [a, b, c, d] = (time.nanosecond().num() / 1000).to_le_bytes();
    ew.buffer().extend_from_copyable_slice(&[hour, minute, second, a, b, c, d])?;
  } else {
    ew.buffer().extend_from_copyable_slice(&[hour, minute, second])?;
  }
  Ok(())
}

test!(
  date,
  Date,
  Date::from_ymd(
    2024.try_into().unwrap(),
    crate::calendar::Month::January,
    crate::calendar::Day::N6
  )
  .unwrap()
);
test!(datetime, DateTime<Utc>, DateTime::EPOCH);