Skip to main content

reifydb_engine/expression/cast/
boolean.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2025 ReifyDB
3
4use std::{fmt::Display, sync::Arc};
5
6use reifydb_core::value::column::data::ColumnData;
7use reifydb_type::{
8	error::diagnostic::{boolean::invalid_number_boolean, cast},
9	fragment::{Fragment, LazyFragment},
10	return_error,
11	value::{
12		boolean::parse::parse_bool,
13		container::{number::NumberContainer, utf8::Utf8Container},
14		is::IsNumber,
15		r#type::Type,
16	},
17};
18
19pub fn to_boolean(data: &ColumnData, lazy_fragment: impl LazyFragment) -> crate::Result<ColumnData> {
20	match data {
21		ColumnData::Int1(container) => from_int1(container, lazy_fragment),
22		ColumnData::Int2(container) => from_int2(container, lazy_fragment),
23		ColumnData::Int4(container) => from_int4(container, lazy_fragment),
24		ColumnData::Int8(container) => from_int8(container, lazy_fragment),
25		ColumnData::Int16(container) => from_int16(container, lazy_fragment),
26		ColumnData::Uint1(container) => from_uint1(container, lazy_fragment),
27		ColumnData::Uint2(container) => from_uint2(container, lazy_fragment),
28		ColumnData::Uint4(container) => from_uint4(container, lazy_fragment),
29		ColumnData::Uint8(container) => from_uint8(container, lazy_fragment),
30		ColumnData::Uint16(container) => from_uint16(container, lazy_fragment),
31		ColumnData::Float4(container) => from_float4(container, lazy_fragment),
32		ColumnData::Float8(container) => from_float8(container, lazy_fragment),
33		ColumnData::Utf8 {
34			container,
35			..
36		} => from_utf8(container, lazy_fragment),
37		_ => {
38			let source_type = data.get_type();
39			return_error!(cast::unsupported_cast(lazy_fragment.fragment(), source_type, Type::Boolean))
40		}
41	}
42}
43
44fn to_bool<T>(
45	container: &NumberContainer<T>,
46	lazy_fragment: impl LazyFragment,
47	validate: impl Fn(T) -> Option<bool>,
48) -> crate::Result<ColumnData>
49where
50	T: Copy + Display + IsNumber + Default,
51{
52	let mut out = ColumnData::with_capacity(Type::Boolean, container.len());
53	for idx in 0..container.len() {
54		if container.is_defined(idx) {
55			match validate(container[idx]) {
56				Some(b) => out.push::<bool>(b),
57				None => {
58					let base_fragment = lazy_fragment.fragment();
59					let error_fragment = Fragment::Statement {
60						text: Arc::from(container[idx].to_string()),
61						line: base_fragment.line(),
62						column: base_fragment.column(),
63					};
64					return_error!(invalid_number_boolean(error_fragment));
65				}
66			}
67		} else {
68			out.push_none();
69		}
70	}
71	Ok(out)
72}
73
74macro_rules! impl_integer_to_bool {
75	($fn_name:ident, $type:ty) => {
76		#[inline]
77		fn $fn_name(
78			container: &NumberContainer<$type>,
79			lazy_fragment: impl LazyFragment,
80		) -> crate::Result<ColumnData> {
81			to_bool(container, lazy_fragment, |val| match val {
82				0 => Some(false),
83				1 => Some(true),
84				_ => None,
85			})
86		}
87	};
88}
89
90macro_rules! impl_float_to_bool {
91	($fn_name:ident, $type:ty) => {
92		#[inline]
93		fn $fn_name(
94			container: &NumberContainer<$type>,
95			lazy_fragment: impl LazyFragment,
96		) -> crate::Result<ColumnData> {
97			to_bool(container, lazy_fragment, |val| {
98				if val == 0.0 {
99					Some(false)
100				} else if val == 1.0 {
101					Some(true)
102				} else {
103					None
104				}
105			})
106		}
107	};
108}
109
110impl_integer_to_bool!(from_int1, i8);
111impl_integer_to_bool!(from_int2, i16);
112impl_integer_to_bool!(from_int4, i32);
113impl_integer_to_bool!(from_int8, i64);
114impl_integer_to_bool!(from_int16, i128);
115impl_integer_to_bool!(from_uint1, u8);
116impl_integer_to_bool!(from_uint2, u16);
117impl_integer_to_bool!(from_uint4, u32);
118impl_integer_to_bool!(from_uint8, u64);
119impl_integer_to_bool!(from_uint16, u128);
120impl_float_to_bool!(from_float4, f32);
121impl_float_to_bool!(from_float8, f64);
122
123fn from_utf8(container: &Utf8Container, lazy_fragment: impl LazyFragment) -> crate::Result<ColumnData> {
124	use reifydb_type::fragment::Fragment;
125	let mut out = ColumnData::with_capacity(Type::Boolean, container.len());
126	for idx in 0..container.len() {
127		if container.is_defined(idx) {
128			// Parse with internal fragment, then replace with
129			// proper source fragment if error
130			let temp_fragment = Fragment::internal(&container[idx]);
131			match parse_bool(temp_fragment) {
132				Ok(b) => out.push(b),
133				Err(mut e) => {
134					// Replace the error's fragment with the
135					// proper source fragment
136					e.0.with_fragment(lazy_fragment.fragment());
137					return Err(e);
138				}
139			}
140		} else {
141			out.push_none();
142		}
143	}
144	Ok(out)
145}