use core::fmt;
use vitaminc_aead::{Bytes as RawBytes, Encrypt};
use crate::{OrderScheme, OrderTarget};
mod private {
pub trait Sealed {}
}
pub trait Target: private::Sealed + Copy + fmt::Debug {
fn runtime(self) -> OrderTarget;
}
pub trait StaticTarget: Target + Default {
const TARGET: OrderTarget;
}
impl private::Sealed for OrderTarget {}
impl Target for OrderTarget {
fn runtime(self) -> OrderTarget {
self
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a value that always fits `{T}`",
label = "does not always fit `{T}`",
note = "`encrypt` takes a value the encoding never refuses: a narrower integer, an `f32`, bytes, or a typed value such as a `NaiveDate`",
note = "a value the encoding can refuse (an integer that may be out of range, text, a `DateTime<Utc>`, or any other `Encrypt` value) goes through `encrypt_checked`, which checks it while the program runs"
)]
pub trait FitsIn<T: StaticTarget>: Encrypt + private::Sealed {}
#[diagnostic::on_unimplemented(
message = "`{Self}` has no order target of its own",
label = "has no order target of its own",
note = "name the target: `scheme.target(I64).encrypt_checked(value)`",
note = "there is no 8-bit target: an order term over 256 values reveals them, so an `i8` goes into `I16` or wider by name (`scheme.target(I16).encrypt(value)`), or into an equality index"
)]
pub trait OwnTarget: Encrypt + private::Sealed {
type Target: StaticTarget;
}
#[diagnostic::on_unimplemented(
message = "`{Self}` has no order term for `{T}`",
label = "does not implement `Supports<{T}>`",
note = "a scheme lists the targets it takes with `vitaminc_ore::supports_targets!(Scheme; I32, Date)`, which answers for each marker and for `OrderTarget`; pick one it lists, or an `OrderTarget` chosen while the program runs"
)]
pub trait Supports<T: Target>: OrderScheme {
fn supports(target: T) -> bool;
}
#[macro_export]
macro_rules! supports_targets {
($scheme:ty; $($marker:ident),* $(,)?) => {
impl $crate::Supports<$crate::OrderTarget> for $scheme {
fn supports(target: $crate::OrderTarget) -> bool {
#[allow(unreachable_patterns)]
match target {
$($crate::OrderTarget::$marker => true,)*
_ => false,
}
}
}
$(
impl $crate::Supports<$crate::target::$marker> for $scheme {
fn supports(_: $crate::target::$marker) -> bool {
true
}
}
)*
};
}
#[macro_export]
macro_rules! supports_every_target {
($scheme:ty) => {
$crate::supports_targets!($scheme;
I16, U16, I32, U32, I64, U64, I128, U128,
F64, Date, Timestamp, Decimal, Text, Bytes, Uuid);
};
}
macro_rules! markers {
($($(#[$doc:meta])* $marker:ident),* $(,)?) => {
$(
$(#[$doc])*
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct $marker;
impl private::Sealed for $marker {}
impl Target for $marker {
fn runtime(self) -> OrderTarget {
OrderTarget::$marker
}
}
impl StaticTarget for $marker {
const TARGET: OrderTarget = OrderTarget::$marker;
}
)*
};
}
markers! {
I16,
U16,
I32,
U32,
I64,
U64,
I128,
U128,
F64,
Date,
Timestamp,
Decimal,
Text,
Bytes,
Uuid,
}
macro_rules! sealed {
($($ty:ty),* $(,)?) => {
$(
impl private::Sealed for $ty {}
impl private::Sealed for &$ty {}
)*
};
}
macro_rules! fits {
($($target:ident: $($ty:ty),*;)*) => {
$($(
impl FitsIn<$target> for $ty {}
impl FitsIn<$target> for &$ty {}
)*)*
};
}
macro_rules! own {
($($ty:ty => $target:ident),* $(,)?) => {
$(
impl OwnTarget for $ty {
type Target = $target;
}
impl OwnTarget for &$ty {
type Target = $target;
}
)*
};
}
sealed!(
i8,
i16,
u16,
i32,
u32,
i64,
u64,
i128,
u128,
f32,
f64,
str,
String,
[u8],
Vec<u8>,
);
impl private::Sealed for RawBytes<'_> {}
impl private::Sealed for &RawBytes<'_> {}
impl<const N: usize> private::Sealed for [u8; N] {}
impl<const N: usize> private::Sealed for &[u8; N] {}
fits! {
I16: i8, i16;
U16: u16;
I32: i8, i16, i32, u16;
U32: u16, u32;
I64: i8, i16, i32, i64, u16, u32;
U64: u16, u32, u64;
I128: i8, i16, i32, i64, i128, u16, u32, u64;
U128: u16, u32, u64, u128;
F64: f32, f64;
Bytes: Vec<u8>;
}
impl FitsIn<Bytes> for &[u8] {}
impl FitsIn<Bytes> for RawBytes<'_> {}
impl FitsIn<Bytes> for &RawBytes<'_> {}
impl<const N: usize> FitsIn<Bytes> for [u8; N] {}
impl<const N: usize> FitsIn<Bytes> for &[u8; N] {}
own! {
i16 => I16,
u16 => U16,
i32 => I32,
u32 => U32,
i64 => I64,
u64 => U64,
i128 => I128,
u128 => U128,
f32 => F64,
f64 => F64,
String => Text,
Vec<u8> => Bytes,
}
impl OwnTarget for &str {
type Target = Text;
}
impl OwnTarget for &[u8] {
type Target = Bytes;
}
impl OwnTarget for RawBytes<'_> {
type Target = Bytes;
}
impl OwnTarget for &RawBytes<'_> {
type Target = Bytes;
}
impl<const N: usize> OwnTarget for [u8; N] {
type Target = Bytes;
}
impl<const N: usize> OwnTarget for &[u8; N] {
type Target = Bytes;
}
#[cfg(feature = "chrono")]
mod chrono_rows {
use super::*;
use chrono::{DateTime, NaiveDate, Utc};
use vitaminc_aead::Timestamp as Instant;
impl private::Sealed for NaiveDate {}
impl private::Sealed for &NaiveDate {}
impl private::Sealed for DateTime<Utc> {}
impl private::Sealed for &DateTime<Utc> {}
impl private::Sealed for Instant {}
impl private::Sealed for &Instant {}
impl FitsIn<Date> for NaiveDate {}
impl FitsIn<Date> for &NaiveDate {}
impl FitsIn<Timestamp> for Instant {}
impl FitsIn<Timestamp> for &Instant {}
impl OwnTarget for NaiveDate {
type Target = Date;
}
impl OwnTarget for &NaiveDate {
type Target = Date;
}
impl OwnTarget for DateTime<Utc> {
type Target = Timestamp;
}
impl OwnTarget for &DateTime<Utc> {
type Target = Timestamp;
}
impl OwnTarget for Instant {
type Target = Timestamp;
}
impl OwnTarget for &Instant {
type Target = Timestamp;
}
}
#[cfg(feature = "rust_decimal")]
mod decimal_rows {
use super::*;
impl private::Sealed for rust_decimal::Decimal {}
impl private::Sealed for &rust_decimal::Decimal {}
impl FitsIn<Decimal> for rust_decimal::Decimal {}
impl FitsIn<Decimal> for &rust_decimal::Decimal {}
impl OwnTarget for rust_decimal::Decimal {
type Target = Decimal;
}
impl OwnTarget for &rust_decimal::Decimal {
type Target = Decimal;
}
}
#[cfg(feature = "uuid")]
mod uuid_rows {
use super::*;
impl private::Sealed for uuid::Uuid {}
impl private::Sealed for &uuid::Uuid {}
impl FitsIn<Uuid> for uuid::Uuid {}
impl FitsIn<Uuid> for &uuid::Uuid {}
impl OwnTarget for uuid::Uuid {
type Target = Uuid;
}
impl OwnTarget for &uuid::Uuid {
type Target = Uuid;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_marker_stands_for_its_target_and_every_target_is_itself() {
fn check<T: StaticTarget>(expected: OrderTarget) {
assert_eq!(T::TARGET, expected);
assert_eq!(T::default().runtime(), expected);
}
check::<I16>(OrderTarget::I16);
check::<U16>(OrderTarget::U16);
check::<I32>(OrderTarget::I32);
check::<U32>(OrderTarget::U32);
check::<I64>(OrderTarget::I64);
check::<U64>(OrderTarget::U64);
check::<I128>(OrderTarget::I128);
check::<U128>(OrderTarget::U128);
check::<F64>(OrderTarget::F64);
check::<Date>(OrderTarget::Date);
check::<Timestamp>(OrderTarget::Timestamp);
check::<Decimal>(OrderTarget::Decimal);
check::<Text>(OrderTarget::Text);
check::<Bytes>(OrderTarget::Bytes);
check::<Uuid>(OrderTarget::Uuid);
for target in OrderTarget::ALL {
assert_eq!(target.runtime(), *target);
}
}
}