use reifydb_core::interface::catalog::property::ColumnSaturationStrategy;
use reifydb_type::{
Result,
error::TypeError,
fragment::Fragment,
value::{number::safe::convert::SafeConvert, r#type::get::GetType},
};
use crate::expression::context::EvalContext;
pub trait Convert {
fn convert<From, To>(&self, from: From, fragment: impl Into<Fragment>) -> Result<Option<To>>
where
From: SafeConvert<To> + GetType,
To: GetType;
}
impl Convert for EvalContext<'_> {
fn convert<From, To>(&self, from: From, fragment: impl Into<Fragment>) -> Result<Option<To>>
where
From: SafeConvert<To> + GetType,
To: GetType,
{
Convert::convert(&self, from, fragment)
}
}
impl Convert for &EvalContext<'_> {
fn convert<From, To>(&self, from: From, fragment: impl Into<Fragment>) -> Result<Option<To>>
where
From: SafeConvert<To> + GetType,
To: GetType,
{
let fragment = fragment.into();
match &self.saturation_policy() {
ColumnSaturationStrategy::Error => from
.checked_convert()
.ok_or_else(|| {
if From::get_type().is_integer() && To::get_type().is_floating_point() {
return TypeError::IntegerPrecisionLoss {
shape_type: From::get_type(),
target: To::get_type(),
fragment: fragment.clone(),
}
.into();
};
let descriptor = self.target.as_ref().and_then(|c| c.to_number_descriptor());
TypeError::NumberOutOfRange {
target: To::get_type(),
fragment: fragment.clone(),
descriptor,
}
.into()
})
.map(Some),
ColumnSaturationStrategy::None => match from.checked_convert() {
None => Ok(None),
Some(value) => Ok(Some(value)),
},
}
}
}