vortex_scalar/
bool.rs

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