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 assert_eq!(
171 &Scalar::bool(true, Nullable),
172 &Scalar::bool(true, NonNullable)
173 );
174 }
175}