reifydb_core/value/column/cast/
convert.rs1use reifydb_value::{
5 Result,
6 error::TypeError,
7 fragment::Fragment,
8 value::{number::safe::convert::SafeConvert, value_type::get::GetType},
9};
10
11use crate::interface::{
12 catalog::property::{ColumnPropertyKind, ColumnSaturationStrategy, DEFAULT_COLUMN_SATURATION_STRATEGY},
13 evaluate::TargetColumn,
14};
15
16pub trait Convert {
17 fn convert<From, To>(&self, from: From, fragment: impl Into<Fragment>) -> Result<Option<To>>
18 where
19 From: SafeConvert<To> + GetType,
20 To: GetType;
21}
22
23#[derive(Clone, Copy)]
24pub struct TargetConvert<'a> {
25 pub target: Option<&'a TargetColumn>,
26}
27
28impl TargetConvert<'_> {
29 pub fn saturation_policy(&self) -> ColumnSaturationStrategy {
30 self.target
31 .and_then(|t| {
32 t.properties()
33 .into_iter()
34 .map(|p| {
35 let ColumnPropertyKind::Saturation(policy) = p;
36 policy
37 })
38 .next()
39 })
40 .unwrap_or(DEFAULT_COLUMN_SATURATION_STRATEGY.clone())
41 }
42}
43
44impl Convert for TargetConvert<'_> {
45 fn convert<From, To>(&self, from: From, fragment: impl Into<Fragment>) -> Result<Option<To>>
46 where
47 From: SafeConvert<To> + GetType,
48 To: GetType,
49 {
50 let fragment = fragment.into();
51 match &self.saturation_policy() {
52 ColumnSaturationStrategy::Error => from
53 .checked_convert()
54 .ok_or_else(|| {
55 if From::get_type().is_integer() && To::get_type().is_floating_point() {
56 return TypeError::IntegerPrecisionLoss {
57 object_type: From::get_type(),
58 target: To::get_type(),
59 fragment: fragment.clone(),
60 }
61 .into();
62 };
63
64 let descriptor = self.target.and_then(|c| c.to_number_descriptor());
65 TypeError::NumberOutOfRange {
66 target: To::get_type(),
67 fragment: fragment.clone(),
68 descriptor,
69 }
70 .into()
71 })
72 .map(Some),
73 ColumnSaturationStrategy::None => match from.checked_convert() {
74 None => Ok(None),
75 Some(value) => Ok(Some(value)),
76 },
77 }
78 }
79}