use crate::{Bytes, Decimal, Int, IonType, Sequence, Str, Struct, Symbol, Timestamp};
use std::fmt;
use std::marker::PhantomData;
use thiserror::Error;
pub type ConversionOperationResult<FromType, ToType> =
Result<ToType, ConversionOperationError<FromType, ToType>>;
#[derive(Clone, Debug, Error, PartialEq)]
#[error("Type conversion error; expected a(n) {output_type}, found a(n) {input_type}")]
pub struct ConversionError {
input_type: String,
output_type: String,
}
impl<FromType, ToType> From<ConversionOperationError<FromType, ToType>> for ConversionError
where
FromType: ValueTypeExpectation,
ToType: TypeExpectation,
{
fn from(err: ConversionOperationError<FromType, ToType>) -> Self {
ConversionError {
input_type: err.input_value.expected_type_name(),
output_type: ToType::expected_type_name(),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ConversionOperationError<FromType, ToType> {
input_value: FromType,
output_type: PhantomData<ToType>,
}
impl<FromType, ToType> fmt::Display for ConversionOperationError<FromType, ToType>
where
FromType: ValueTypeExpectation,
ToType: TypeExpectation,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Type conversion error; expected a(n) {}, found a(n) {}",
ToType::expected_type_name(),
self.input_value.expected_type_name()
)
}
}
impl<FromType, ToType> std::error::Error for ConversionOperationError<FromType, ToType>
where
FromType: ValueTypeExpectation + fmt::Debug,
ToType: TypeExpectation + fmt::Debug,
{
}
impl<FromType, ToType> ConversionOperationError<FromType, ToType> {
pub fn new(input_value: FromType) -> Self {
Self {
input_value,
output_type: PhantomData,
}
}
pub fn original_value(self) -> FromType {
self.input_value
}
}
impl<FromType, ToType> From<FromType> for ConversionOperationError<FromType, ToType>
where
FromType: ValueTypeExpectation,
ToType: TypeExpectation,
{
fn from(input_value: FromType) -> Self {
Self::new(input_value)
}
}
pub trait IonTypeExpectation {
fn ion_type(&self) -> IonType;
}
pub trait ValueTypeExpectation {
fn expected_type_name(&self) -> String;
}
pub trait TypeExpectation {
fn expected_type_name() -> String;
}
macro_rules! impl_type_expectation {
($ty:ty, $e:expr) => {
impl TypeExpectation for $ty {
fn expected_type_name() -> String {
$e.to_string()
}
}
};
}
macro_rules! impl_type_and_ref_expectation {
($ty:ty, $e:expr) => {
impl_type_expectation!($ty, $e);
impl_type_expectation!(&$ty, $e);
};
}
impl_type_and_ref_expectation!(Int, IonType::Int);
impl_type_expectation!(i64, "i64 value");
impl_type_expectation!(usize, "usize value");
impl_type_expectation!(f64, IonType::Float);
impl_type_and_ref_expectation!(Decimal, IonType::Decimal);
impl_type_and_ref_expectation!(Timestamp, IonType::Timestamp);
impl_type_and_ref_expectation!(String, "text value");
impl_type_expectation!(&str, "text value");
impl_type_and_ref_expectation!(Str, IonType::String);
impl_type_and_ref_expectation!(Symbol, IonType::Symbol);
impl_type_expectation!(bool, IonType::Bool);
impl_type_and_ref_expectation!(Bytes, "lob value");
impl_type_expectation!(&[u8], "lob value");
impl_type_and_ref_expectation!(Sequence, "sequence value");
impl_type_and_ref_expectation!(Struct, IonType::Struct);
impl<T> ValueTypeExpectation for T
where
T: IonTypeExpectation,
{
fn expected_type_name(&self) -> String {
self.ion_type().to_string()
}
}