use std::collections::{BTreeMap, BTreeSet};
use icydb_schema::{
Account, Blob, Date, Decimal, Duration, Float32, Float64, IntBig, NatBig, Principal,
Subaccount, Timestamp, Ulid, Unit,
};
#[doc(hidden)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TypedScalarValue {
Account(Account),
Blob(Blob),
Bool(bool),
Date(Date),
Decimal(Decimal),
Duration(Duration),
Float32(Float32),
Float64(Float64),
Int64(i64),
Int128(i128),
IntBig(IntBig),
Nat64(u64),
Nat128(u128),
NatBig(NatBig),
Principal(Principal),
Subaccount(Subaccount),
Text(String),
Timestamp(Timestamp),
Ulid(Ulid),
Unit,
}
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TypedEnumOutput<'a, T> {
Unit,
Payload(&'a T),
}
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TypedValueError {
SourceUnavailable,
ShapeMismatch,
}
#[doc(hidden)]
pub trait TypedAdapterContext {
type InputValue;
type OutputValue;
fn input_scalar(&self, value: TypedScalarValue) -> Self::InputValue;
fn input_list(&self, values: Vec<Self::InputValue>) -> Self::InputValue;
fn input_map(&self, entries: Vec<(Self::InputValue, Self::InputValue)>) -> Self::InputValue;
fn input_null(&self) -> Self::InputValue;
fn input_enum(
&self,
type_source_key: &'static str,
variant_source_key: &'static str,
payload: Option<Self::InputValue>,
) -> Result<Self::InputValue, TypedValueError>;
fn input_record(
&self,
type_source_key: &'static str,
fields: Vec<(&'static str, Self::InputValue)>,
) -> Result<Self::InputValue, TypedValueError>;
fn output_scalar(&self, value: &Self::OutputValue) -> Option<TypedScalarValue>;
fn output_list<'a>(&self, value: &'a Self::OutputValue) -> Option<&'a [Self::OutputValue]>;
fn output_map<'a>(
&self,
value: &'a Self::OutputValue,
) -> Option<&'a [(Self::OutputValue, Self::OutputValue)]>;
fn output_is_null(&self, value: &Self::OutputValue) -> bool;
fn output_enum_variant<'a>(
&self,
type_source_key: &'static str,
variant_source_key: &'static str,
value: &'a Self::OutputValue,
) -> Result<Option<TypedEnumOutput<'a, Self::OutputValue>>, TypedValueError>;
fn output_record<'a>(
&self,
type_source_key: &'static str,
member_source_keys: &[&'static str],
value: &'a Self::OutputValue,
) -> Result<Vec<&'a Self::OutputValue>, TypedValueError>;
}
#[doc(hidden)]
pub trait TypedNamedType {
const SOURCE_KEY: &'static str;
}
#[doc(hidden)]
pub trait TypedInputValue: Sized {
fn encode_typed_input<C>(self, context: &C) -> Result<C::InputValue, TypedValueError>
where
C: TypedAdapterContext;
}
#[doc(hidden)]
pub trait TypedOutputValue: Sized {
fn decode_typed_output<C>(context: &C, value: &C::OutputValue) -> Result<Self, TypedValueError>
where
C: TypedAdapterContext;
}
macro_rules! impl_typed_scalar_value {
($($ty:ty => $variant:ident),* $(,)?) => {
$(
impl TypedInputValue for $ty {
fn encode_typed_input<C>(
self,
context: &C,
) -> Result<C::InputValue, TypedValueError>
where
C: TypedAdapterContext,
{
Ok(context.input_scalar(TypedScalarValue::$variant(self.into())))
}
}
impl TypedOutputValue for $ty {
fn decode_typed_output<C>(
context: &C,
value: &C::OutputValue,
) -> Result<Self, TypedValueError>
where
C: TypedAdapterContext,
{
match context.output_scalar(value) {
Some(TypedScalarValue::$variant(value)) => {
Self::try_from(value).map_err(|_| TypedValueError::ShapeMismatch)
}
_ => Err(TypedValueError::ShapeMismatch),
}
}
}
)*
};
}
impl_typed_scalar_value!(
Account => Account,
Blob => Blob,
bool => Bool,
Date => Date,
Decimal => Decimal,
Duration => Duration,
Float32 => Float32,
Float64 => Float64,
i8 => Int64,
i16 => Int64,
i32 => Int64,
i64 => Int64,
i128 => Int128,
IntBig => IntBig,
NatBig => NatBig,
Principal => Principal,
String => Text,
Subaccount => Subaccount,
Timestamp => Timestamp,
u8 => Nat64,
u16 => Nat64,
u32 => Nat64,
u64 => Nat64,
u128 => Nat128,
Ulid => Ulid,
);
impl TypedInputValue for Unit {
fn encode_typed_input<C>(self, context: &C) -> Result<C::InputValue, TypedValueError>
where
C: TypedAdapterContext,
{
Ok(context.input_scalar(TypedScalarValue::Unit))
}
}
impl TypedOutputValue for Unit {
fn decode_typed_output<C>(context: &C, value: &C::OutputValue) -> Result<Self, TypedValueError>
where
C: TypedAdapterContext,
{
match context.output_scalar(value) {
Some(TypedScalarValue::Unit) => Ok(Self),
_ => Err(TypedValueError::ShapeMismatch),
}
}
}
impl<T> TypedInputValue for Box<T>
where
T: TypedInputValue,
{
fn encode_typed_input<C>(self, context: &C) -> Result<C::InputValue, TypedValueError>
where
C: TypedAdapterContext,
{
(*self).encode_typed_input(context)
}
}
impl<T> TypedOutputValue for Box<T>
where
T: TypedOutputValue,
{
fn decode_typed_output<C>(context: &C, value: &C::OutputValue) -> Result<Self, TypedValueError>
where
C: TypedAdapterContext,
{
T::decode_typed_output(context, value).map(Self::new)
}
}
impl<T> TypedInputValue for Option<T>
where
T: TypedInputValue,
{
fn encode_typed_input<C>(self, context: &C) -> Result<C::InputValue, TypedValueError>
where
C: TypedAdapterContext,
{
self.map_or_else(
|| Ok(context.input_null()),
|value| value.encode_typed_input(context),
)
}
}
impl<T> TypedOutputValue for Option<T>
where
T: TypedOutputValue,
{
fn decode_typed_output<C>(context: &C, value: &C::OutputValue) -> Result<Self, TypedValueError>
where
C: TypedAdapterContext,
{
if context.output_is_null(value) {
Ok(None)
} else {
T::decode_typed_output(context, value).map(Some)
}
}
}
impl<T> TypedInputValue for Vec<T>
where
T: TypedInputValue,
{
fn encode_typed_input<C>(self, context: &C) -> Result<C::InputValue, TypedValueError>
where
C: TypedAdapterContext,
{
self.into_iter()
.map(|value| value.encode_typed_input(context))
.collect::<Result<Vec<_>, _>>()
.map(|values| context.input_list(values))
}
}
impl<T> TypedOutputValue for Vec<T>
where
T: TypedOutputValue,
{
fn decode_typed_output<C>(context: &C, value: &C::OutputValue) -> Result<Self, TypedValueError>
where
C: TypedAdapterContext,
{
context
.output_list(value)
.ok_or(TypedValueError::ShapeMismatch)?
.iter()
.map(|value| T::decode_typed_output(context, value))
.collect()
}
}
impl<K, V> TypedInputValue for BTreeMap<K, V>
where
K: TypedInputValue,
V: TypedInputValue,
{
fn encode_typed_input<C>(self, context: &C) -> Result<C::InputValue, TypedValueError>
where
C: TypedAdapterContext,
{
self.into_iter()
.map(|(key, value)| {
Ok((
key.encode_typed_input(context)?,
value.encode_typed_input(context)?,
))
})
.collect::<Result<Vec<_>, _>>()
.map(|entries| context.input_map(entries))
}
}
impl<K, V> TypedOutputValue for BTreeMap<K, V>
where
K: Ord + TypedOutputValue,
V: TypedOutputValue,
{
fn decode_typed_output<C>(context: &C, value: &C::OutputValue) -> Result<Self, TypedValueError>
where
C: TypedAdapterContext,
{
let entries = context
.output_map(value)
.ok_or(TypedValueError::ShapeMismatch)?;
let mut decoded = Self::new();
for (key, value) in entries {
let key = K::decode_typed_output(context, key)?;
let value = V::decode_typed_output(context, value)?;
if decoded.insert(key, value).is_some() {
return Err(TypedValueError::ShapeMismatch);
}
}
Ok(decoded)
}
}
impl<T> TypedInputValue for BTreeSet<T>
where
T: TypedInputValue,
{
fn encode_typed_input<C>(self, context: &C) -> Result<C::InputValue, TypedValueError>
where
C: TypedAdapterContext,
{
self.into_iter()
.map(|value| value.encode_typed_input(context))
.collect::<Result<Vec<_>, _>>()
.map(|values| context.input_list(values))
}
}
impl<T> TypedOutputValue for BTreeSet<T>
where
T: Ord + TypedOutputValue,
{
fn decode_typed_output<C>(context: &C, value: &C::OutputValue) -> Result<Self, TypedValueError>
where
C: TypedAdapterContext,
{
let values = context
.output_list(value)
.ok_or(TypedValueError::ShapeMismatch)?;
let mut decoded = Self::new();
for value in values {
if !decoded.insert(T::decode_typed_output(context, value)?) {
return Err(TypedValueError::ShapeMismatch);
}
}
Ok(decoded)
}
}