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