Skip to main content

reifydb_core/value/column/cast/
convert.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use 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}