Skip to main content

reifydb_engine/expression/
convert.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2025 ReifyDB
3
4use reifydb_core::interface::catalog::property::ColumnSaturationPolicy;
5use reifydb_type::{
6	Result,
7	error::TypeError,
8	fragment::Fragment,
9	value::{number::safe::convert::SafeConvert, r#type::get::GetType},
10};
11
12use crate::expression::context::EvalContext;
13
14pub trait Convert {
15	fn convert<From, To>(&self, from: From, fragment: impl Into<Fragment>) -> Result<Option<To>>
16	where
17		From: SafeConvert<To> + GetType,
18		To: GetType;
19}
20
21impl Convert for EvalContext<'_> {
22	fn convert<From, To>(&self, from: From, fragment: impl Into<Fragment>) -> Result<Option<To>>
23	where
24		From: SafeConvert<To> + GetType,
25		To: GetType,
26	{
27		Convert::convert(&self, from, fragment)
28	}
29}
30
31impl Convert for &EvalContext<'_> {
32	fn convert<From, To>(&self, from: From, fragment: impl Into<Fragment>) -> Result<Option<To>>
33	where
34		From: SafeConvert<To> + GetType,
35		To: GetType,
36	{
37		let fragment = fragment.into();
38		match &self.saturation_policy() {
39			ColumnSaturationPolicy::Error => from
40				.checked_convert()
41				.ok_or_else(|| {
42					if From::get_type().is_integer() && To::get_type().is_floating_point() {
43						return TypeError::IntegerPrecisionLoss {
44							source_type: From::get_type(),
45							target: To::get_type(),
46							fragment: fragment.clone(),
47						}
48						.into();
49					};
50
51					let descriptor = self.target.as_ref().and_then(|c| c.to_number_descriptor());
52					TypeError::NumberOutOfRange {
53						target: To::get_type(),
54						fragment: fragment.clone(),
55						descriptor,
56					}
57					.into()
58				})
59				.map(Some),
60			ColumnSaturationPolicy::None => match from.checked_convert() {
61				None => Ok(None),
62				Some(value) => Ok(Some(value)),
63			},
64		}
65	}
66}