vortex_scalar/
bool.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::cmp::Ordering;
5use std::fmt::{Display, Formatter};
6
7use vortex_dtype::Nullability::NonNullable;
8use vortex_dtype::{DType, Nullability};
9use vortex_error::{VortexError, VortexExpect as _, VortexResult, vortex_bail, vortex_err};
10
11use crate::{InnerScalarValue, Scalar, ScalarValue};
12
13/// A scalar value representing a boolean.
14///
15/// This type provides a view into a boolean scalar value, which can be either
16/// true, false, or null.
17#[derive(Debug, Hash)]
18pub struct BoolScalar<'a> {
19    dtype: &'a DType,
20    value: Option<bool>,
21}
22
23impl Display for BoolScalar<'_> {
24    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
25        match self.value {
26            None => write!(f, "null"),
27            Some(v) => write!(f, "{v}"),
28        }
29    }
30}
31
32impl PartialEq for BoolScalar<'_> {
33    fn eq(&self, other: &Self) -> bool {
34        self.dtype.eq_ignore_nullability(other.dtype) && self.value == other.value
35    }
36}
37
38impl Eq for BoolScalar<'_> {}
39
40impl PartialOrd for BoolScalar<'_> {
41    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
42        Some(self.value.cmp(&other.value))
43    }
44}
45
46impl Ord for BoolScalar<'_> {
47    fn cmp(&self, other: &Self) -> Ordering {
48        self.value.cmp(&other.value)
49    }
50}
51
52impl<'a> BoolScalar<'a> {
53    /// Returns the data type of this boolean scalar.
54    #[inline]
55    pub fn dtype(&self) -> &'a DType {
56        self.dtype
57    }
58
59    /// Returns the boolean value, or None if null.
60    pub fn value(&self) -> Option<bool> {
61        self.value
62    }
63
64    pub(crate) fn cast(&self, dtype: &DType) -> VortexResult<Scalar> {
65        if !matches!(dtype, DType::Bool(..)) {
66            vortex_bail!("Can't cast bool to {}", dtype)
67        }
68        Ok(Scalar::bool(
69            self.value.vortex_expect("nullness handled in Scalar::cast"),
70            dtype.nullability(),
71        ))
72    }
73
74    /// Returns a new boolean scalar with the inverted value.
75    ///
76    /// Null values remain null.
77    pub fn invert(self) -> BoolScalar<'a> {
78        BoolScalar {
79            dtype: self.dtype,
80            value: self.value.map(|v| !v),
81        }
82    }
83
84    /// Converts this boolean scalar into a general scalar.
85    pub fn into_scalar(self) -> Scalar {
86        Scalar {
87            dtype: self.dtype.clone(),
88            value: self
89                .value
90                .map(|x| ScalarValue(InnerScalarValue::Bool(x)))
91                .unwrap_or_else(|| ScalarValue(InnerScalarValue::Null)),
92        }
93    }
94}
95
96impl Scalar {
97    /// Creates a new boolean scalar with the given value and nullability.
98    pub fn bool(value: bool, nullability: Nullability) -> Self {
99        Self {
100            dtype: DType::Bool(nullability),
101            value: ScalarValue(InnerScalarValue::Bool(value)),
102        }
103    }
104}
105
106impl<'a> TryFrom<&'a Scalar> for BoolScalar<'a> {
107    type Error = VortexError;
108
109    fn try_from(value: &'a Scalar) -> Result<Self, Self::Error> {
110        if !matches!(value.dtype(), DType::Bool(_)) {
111            vortex_bail!("Expected bool scalar, found {}", value.dtype())
112        }
113        Ok(Self {
114            dtype: value.dtype(),
115            value: value.value.as_bool()?,
116        })
117    }
118}
119
120impl TryFrom<&Scalar> for bool {
121    type Error = VortexError;
122
123    fn try_from(value: &Scalar) -> VortexResult<Self> {
124        <Option<bool>>::try_from(value)?
125            .ok_or_else(|| vortex_err!("Can't extract present value from null scalar"))
126    }
127}
128
129impl TryFrom<&Scalar> for Option<bool> {
130    type Error = VortexError;
131
132    fn try_from(value: &Scalar) -> VortexResult<Self> {
133        Ok(BoolScalar::try_from(value)?.value())
134    }
135}
136
137impl TryFrom<Scalar> for bool {
138    type Error = VortexError;
139
140    fn try_from(value: Scalar) -> VortexResult<Self> {
141        Self::try_from(&value)
142    }
143}
144
145impl TryFrom<Scalar> for Option<bool> {
146    type Error = VortexError;
147
148    fn try_from(value: Scalar) -> VortexResult<Self> {
149        Self::try_from(&value)
150    }
151}
152
153impl From<bool> for Scalar {
154    fn from(value: bool) -> Self {
155        Self {
156            dtype: DType::Bool(NonNullable),
157            value: value.into(),
158        }
159    }
160}
161
162impl From<bool> for ScalarValue {
163    fn from(value: bool) -> Self {
164        ScalarValue(InnerScalarValue::Bool(value))
165    }
166}
167
168#[cfg(test)]
169mod test {
170    use vortex_dtype::Nullability::*;
171
172    use super::*;
173
174    #[test]
175    fn into_from() {
176        let scalar: Scalar = false.into();
177        assert!(!bool::try_from(&scalar).unwrap());
178    }
179
180    #[test]
181    fn equality() {
182        assert_eq!(&Scalar::bool(true, Nullable), &Scalar::bool(true, Nullable));
183        // Equality ignores nullability
184        assert_eq!(
185            &Scalar::bool(true, Nullable),
186            &Scalar::bool(true, NonNullable)
187        );
188    }
189}