vortex_dtype/
nullability.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::fmt::{Display, Formatter};
5use std::ops::BitOr;
6
7/// Whether an instance of a DType can be `null or not
8#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
9pub enum Nullability {
10    /// Instances of this DType are guaranteed to be non-nullable
11    #[default]
12    NonNullable,
13    /// Instances of this DType may contain a null value
14    Nullable,
15}
16
17impl Nullability {
18    /// A self-describing displayed form.
19    ///
20    /// The usual Display renders [Nullability::NonNullable] as the empty string.
21    pub fn verbose_display(&self) -> impl Display {
22        match self {
23            Nullability::NonNullable => "NonNullable",
24            Nullability::Nullable => "Nullable",
25        }
26    }
27}
28
29impl BitOr for Nullability {
30    type Output = Nullability;
31
32    fn bitor(self, rhs: Self) -> Self::Output {
33        match (self, rhs) {
34            (Self::NonNullable, Self::NonNullable) => Self::NonNullable,
35            _ => Self::Nullable,
36        }
37    }
38}
39
40impl From<bool> for Nullability {
41    fn from(value: bool) -> Self {
42        if value {
43            Self::Nullable
44        } else {
45            Self::NonNullable
46        }
47    }
48}
49
50impl From<Nullability> for bool {
51    fn from(value: Nullability) -> Self {
52        match value {
53            Nullability::NonNullable => false,
54            Nullability::Nullable => true,
55        }
56    }
57}
58
59impl Display for Nullability {
60    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
61        match self {
62            Self::NonNullable => write!(f, ""),
63            Self::Nullable => write!(f, "?"),
64        }
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn test_nullability_default() {
74        let default = Nullability::default();
75        assert_eq!(default, Nullability::NonNullable);
76    }
77
78    #[test]
79    fn test_nullability_bitor() {
80        use Nullability::*;
81
82        // NonNullable | NonNullable = NonNullable
83        assert_eq!(NonNullable | NonNullable, NonNullable);
84
85        // NonNullable | Nullable = Nullable
86        assert_eq!(NonNullable | Nullable, Nullable);
87
88        // Nullable | NonNullable = Nullable
89        assert_eq!(Nullable | NonNullable, Nullable);
90
91        // Nullable | Nullable = Nullable
92        assert_eq!(Nullable | Nullable, Nullable);
93    }
94
95    #[test]
96    fn test_nullability_from_bool() {
97        assert_eq!(Nullability::from(false), Nullability::NonNullable);
98        assert_eq!(Nullability::from(true), Nullability::Nullable);
99    }
100
101    #[test]
102    fn test_bool_from_nullability() {
103        assert!(!bool::from(Nullability::NonNullable));
104        assert!(bool::from(Nullability::Nullable));
105    }
106
107    #[test]
108    fn test_nullability_roundtrip() {
109        // Test roundtrip conversion bool -> Nullability -> bool
110        assert!(!bool::from(Nullability::from(false)));
111        assert!(bool::from(Nullability::from(true)));
112
113        // Test roundtrip conversion Nullability -> bool -> Nullability
114        assert_eq!(
115            Nullability::from(bool::from(Nullability::NonNullable)),
116            Nullability::NonNullable
117        );
118        assert_eq!(
119            Nullability::from(bool::from(Nullability::Nullable)),
120            Nullability::Nullable
121        );
122    }
123
124    #[test]
125    fn test_nullability_chained_bitor() {
126        // Test chaining multiple BitOr operations
127        let result = Nullability::NonNullable | Nullability::NonNullable | Nullability::NonNullable;
128        assert_eq!(result, Nullability::NonNullable);
129
130        let result = Nullability::NonNullable | Nullability::Nullable | Nullability::NonNullable;
131        assert_eq!(result, Nullability::Nullable);
132    }
133}