inline_option/
lib.rs

1#![doc = include_str!("../README.md")]
2
3/// A trait for defining nullable values.
4pub trait Nullable {
5    /// The null value for the type.
6    const NULL: Self;
7
8    /// Returns `true` if the value is null. Typically, this is a comparison with the null value.
9    fn is_null(&self) -> bool;
10}
11
12impl<T> Nullable for Option<T> {
13    const NULL: Self = None;
14
15    #[inline]
16    fn is_null(&self) -> bool {
17        self.is_none()
18    }
19}
20
21impl<T> Nullable for *const T {
22    const NULL: Self = core::ptr::null();
23
24    #[inline]
25    fn is_null(&self) -> bool {
26        *self == Self::NULL
27    }
28}
29
30impl<T> Nullable for *mut T {
31    const NULL: Self = core::ptr::null_mut();
32
33    #[inline]
34    fn is_null(&self) -> bool {
35        *self == Self::NULL
36    }
37}
38
39#[cfg(feature = "nullable-core-floats")]
40impl Nullable for f32 {
41    const NULL: Self = f32::MAX;
42
43    #[inline]
44    fn is_null(&self) -> bool {
45        *self == f32::MAX
46    }
47}
48
49#[cfg(feature = "nullable-core-floats")]
50impl Nullable for f64 {
51    const NULL: Self = f64::MAX;
52
53    #[inline]
54    fn is_null(&self) -> bool {
55        *self == f64::MAX
56    }
57}
58
59#[cfg(feature = "nullable-core-ints")]
60impl Nullable for i8 {
61    const NULL: Self = i8::MAX;
62
63    #[inline]
64    fn is_null(&self) -> bool {
65        *self == i8::MAX
66    }
67}
68
69#[cfg(feature = "nullable-core-ints")]
70impl Nullable for i16 {
71    const NULL: Self = i16::MAX;
72
73    #[inline]
74    fn is_null(&self) -> bool {
75        *self == i16::MAX
76    }
77}
78
79#[cfg(feature = "nullable-core-ints")]
80impl Nullable for i32 {
81    const NULL: Self = i32::MAX;
82
83    #[inline]
84    fn is_null(&self) -> bool {
85        *self == i32::MAX
86    }
87}
88
89#[cfg(feature = "nullable-core-ints")]
90impl Nullable for i64 {
91    const NULL: Self = i64::MAX;
92
93    #[inline]
94    fn is_null(&self) -> bool {
95        *self == i64::MAX
96    }
97}
98
99#[cfg(feature = "nullable-core-ints")]
100impl Nullable for i128 {
101    const NULL: Self = i128::MAX;
102
103    #[inline]
104    fn is_null(&self) -> bool {
105        *self == i128::MAX
106    }
107}
108
109#[cfg(feature = "nullable-core-ints")]
110impl Nullable for isize {
111    const NULL: Self = isize::MAX;
112
113    #[inline]
114    fn is_null(&self) -> bool {
115        *self == isize::MAX
116    }
117}
118
119#[cfg(feature = "nullable-core-ints")]
120impl Nullable for u8 {
121    const NULL: Self = u8::MAX;
122
123    #[inline]
124    fn is_null(&self) -> bool {
125        *self == u8::MAX
126    }
127}
128
129#[cfg(feature = "nullable-core-ints")]
130impl Nullable for u16 {
131    const NULL: Self = u16::MAX;
132
133    #[inline]
134    fn is_null(&self) -> bool {
135        *self == u16::MAX
136    }
137}
138
139#[cfg(feature = "nullable-core-ints")]
140impl Nullable for u32 {
141    const NULL: Self = u32::MAX;
142
143    #[inline]
144    fn is_null(&self) -> bool {
145        *self == u32::MAX
146    }
147}
148
149#[cfg(feature = "nullable-core-ints")]
150impl Nullable for u64 {
151    const NULL: Self = u64::MAX;
152
153    #[inline]
154    fn is_null(&self) -> bool {
155        *self == u64::MAX
156    }
157}
158
159#[cfg(feature = "nullable-core-ints")]
160impl Nullable for u128 {
161    const NULL: Self = u128::MAX;
162
163    #[inline]
164    fn is_null(&self) -> bool {
165        *self == u128::MAX
166    }
167}
168
169#[cfg(feature = "nullable-core-ints")]
170impl Nullable for usize {
171    const NULL: Self = usize::MAX;
172
173    #[inline]
174    fn is_null(&self) -> bool {
175        *self == usize::MAX
176    }
177}
178
179/// The `IOption` type, a transparent newtype wrapper around a [`Nullable`] value that provides a similar API to [`Option`][core::option::Option].
180///
181/// See the [module-level documentation](crate) for more information.
182#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
183#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
184#[repr(transparent)]
185pub struct IOption<T: Nullable>(T);
186
187impl<T: Nullable> Default for IOption<T> {
188    #[inline]
189    fn default() -> Self {
190        Self::new(T::NULL)
191    }
192}
193
194impl<T: Nullable> IOption<T> {
195    #[inline]
196    pub const fn new(value: T) -> Self {
197        Self(value)
198    }
199
200    #[inline]
201    pub const fn none() -> Self {
202        Self(T::NULL)
203    }
204
205    #[inline]
206    pub fn is_none(&self) -> bool {
207        self.0.is_null()
208    }
209
210    #[inline]
211    pub fn is_some(&self) -> bool {
212        !self.is_none()
213    }
214
215    #[inline]
216    pub fn into_inner(self) -> T {
217        self.0
218    }
219
220    #[inline]
221    pub fn as_ref(&self) -> Option<&T> {
222        if self.is_none() { None } else { Some(&self.0) }
223    }
224
225    #[inline]
226    pub fn as_mut(&mut self) -> Option<&mut T> {
227        if self.is_none() {
228            None
229        } else {
230            Some(&mut self.0)
231        }
232    }
233
234    #[inline]
235    pub fn map<U, F>(self, f: F) -> IOption<U>
236    where
237        U: Nullable,
238        F: FnOnce(T) -> U,
239    {
240        if self.is_none() {
241            IOption::new(U::NULL)
242        } else {
243            IOption::new(f(self.into_inner()))
244        }
245    }
246
247    #[inline]
248    pub fn map_or<U, F>(self, default: U, f: F) -> U
249    where
250        U: Nullable,
251        F: FnOnce(T) -> U,
252    {
253        if self.is_none() {
254            default
255        } else {
256            f(self.into_inner())
257        }
258    }
259
260    #[inline]
261    pub fn map_or_else<U, D, F>(self, default: D, f: F) -> U
262    where
263        U: Nullable,
264        D: FnOnce() -> U,
265        F: FnOnce(T) -> U,
266    {
267        if self.is_none() {
268            default()
269        } else {
270            f(self.into_inner())
271        }
272    }
273
274    #[inline]
275    pub fn ok_or<E>(self, err: E) -> Result<T, E> {
276        if self.is_none() {
277            Err(err)
278        } else {
279            Ok(self.into_inner())
280        }
281    }
282
283    #[inline]
284    pub fn ok_or_else<E, F>(self, err: F) -> Result<T, E>
285    where
286        F: FnOnce() -> E,
287    {
288        if self.is_none() {
289            Err(err())
290        } else {
291            Ok(self.into_inner())
292        }
293    }
294
295    #[inline]
296    pub fn and<U>(self, other: IOption<U>) -> IOption<U>
297    where
298        U: Nullable,
299    {
300        if self.is_none() {
301            IOption::new(U::NULL)
302        } else {
303            other
304        }
305    }
306
307    #[inline]
308    pub fn and_then<U, F>(self, f: F) -> IOption<U>
309    where
310        U: Nullable,
311        F: FnOnce(T) -> IOption<U>,
312    {
313        if self.is_none() {
314            IOption::new(U::NULL)
315        } else {
316            f(self.into_inner())
317        }
318    }
319
320    #[inline]
321    pub fn or(self, other: IOption<T>) -> IOption<T>
322    where
323        T: Nullable,
324    {
325        if self.is_none() { other } else { self }
326    }
327
328    #[inline]
329    pub fn or_else<F>(self, f: F) -> IOption<T>
330    where
331        T: Nullable,
332        F: FnOnce() -> IOption<T>,
333    {
334        if self.is_none() { f() } else { self }
335    }
336
337    #[inline]
338    #[track_caller]
339    pub fn unwrap(self) -> T {
340        if self.is_none() {
341            panic!("called `IOption::unwrap()` on a `None` value")
342        } else {
343            self.into_inner()
344        }
345    }
346
347    #[inline]
348    pub fn unwrap_or(self, default: T) -> T {
349        if self.is_none() {
350            default
351        } else {
352            self.into_inner()
353        }
354    }
355
356    #[inline]
357    pub fn unwrap_or_else<F>(self, f: F) -> T
358    where
359        F: FnOnce() -> T,
360    {
361        if self.is_none() {
362            f()
363        } else {
364            self.into_inner()
365        }
366    }
367
368    #[inline]
369    #[track_caller]
370    pub fn expect(self, msg: &str) -> T {
371        if self.is_none() {
372            panic!("{}", msg)
373        } else {
374            self.into_inner()
375        }
376    }
377
378    #[inline]
379    pub fn iter(&self) -> core::option::IntoIter<&T> {
380        self.as_ref().into_iter()
381    }
382
383    #[inline]
384    pub fn iter_mut(&mut self) -> core::option::IntoIter<&mut T> {
385        self.as_mut().into_iter()
386    }
387
388    #[inline]
389    pub fn filter<P>(self, predicate: P) -> IOption<T>
390    where
391        P: FnOnce(&T) -> bool,
392    {
393        let inner = self.into_inner();
394        if inner.is_null() || !predicate(&inner) {
395            IOption::new(T::NULL)
396        } else {
397            IOption::new(inner)
398        }
399    }
400
401    #[inline]
402    pub fn take(&mut self) -> IOption<T> {
403        core::mem::take(self)
404    }
405
406    #[inline]
407    pub fn replace(&mut self, value: T) -> IOption<T> {
408        core::mem::replace(self, IOption::new(value))
409    }
410
411    #[inline]
412    pub fn get_or_insert(&mut self, value: T) -> &mut T {
413        if self.is_none() {
414            *self = IOption::new(value);
415        }
416        self.as_mut().unwrap()
417    }
418
419    #[inline]
420    pub fn get_or_insert_with<F>(&mut self, f: F) -> &mut T
421    where
422        F: FnOnce() -> T,
423    {
424        if self.is_none() {
425            *self = IOption::new(f());
426        }
427        self.as_mut().unwrap()
428    }
429
430    #[inline]
431    pub fn get_or_insert_default(&mut self) -> &mut T
432    where
433        T: Default,
434    {
435        if self.is_none() {
436            *self = IOption::new(T::default());
437        }
438        self.as_mut().unwrap()
439    }
440}
441
442impl<T: Nullable> From<Option<T>> for IOption<T> {
443    #[inline]
444    fn from(option: Option<T>) -> Self {
445        match option {
446            Some(value) => IOption::new(value),
447            None => IOption::new(T::NULL),
448        }
449    }
450}
451
452impl<T: Nullable> From<IOption<T>> for Option<T> {
453    #[inline]
454    fn from(ioption: IOption<T>) -> Self {
455        if ioption.is_none() {
456            None
457        } else {
458            Some(ioption.into_inner())
459        }
460    }
461}
462
463impl<T: Nullable> From<T> for IOption<T> {
464    #[inline]
465    fn from(value: T) -> Self {
466        IOption::new(value)
467    }
468}
469
470#[cfg(test)]
471mod tests {
472    use super::*;
473
474    #[cfg(not(feature = "nullable-core-ints"))]
475    impl Nullable for i32 {
476        const NULL: Self = i32::MAX;
477
478        fn is_null(&self) -> bool {
479            *self == i32::MAX
480        }
481    }
482
483    #[test]
484    fn test_nullable() {
485        assert_eq!(i32::NULL, i32::MAX);
486        assert!(i32::MAX.is_null());
487        assert!(!42.is_null());
488    }
489
490    #[test]
491    fn test_size() {
492        assert_eq!(
493            core::mem::size_of::<IOption<i32>>(),
494            core::mem::size_of::<i32>()
495        );
496    }
497
498    #[test]
499    fn test_new() {
500        let ioption = IOption::new(42);
501        assert_eq!(ioption.into_inner(), 42);
502    }
503
504    #[test]
505    fn test_is_none() {
506        let ioption = IOption::new(42);
507        assert!(!ioption.is_none());
508
509        let ioption = IOption::new(i32::NULL);
510        assert!(ioption.is_none());
511    }
512
513    #[test]
514    fn test_is_some() {
515        let ioption = IOption::new(42);
516        assert!(ioption.is_some());
517
518        let ioption = IOption::new(i32::NULL);
519        assert!(!ioption.is_some());
520    }
521
522    #[test]
523    fn test_into_inner() {
524        let ioption = IOption::new(42);
525        assert_eq!(ioption.into_inner(), 42);
526    }
527
528    #[test]
529    fn test_as_ref() {
530        let ioption = IOption::new(42);
531        assert_eq!(ioption.as_ref(), Some(&42));
532
533        let ioption = IOption::new(i32::NULL);
534        assert_eq!(ioption.as_ref(), None);
535    }
536
537    #[test]
538    fn test_as_mut() {
539        let mut ioption = IOption::new(42);
540        assert_eq!(ioption.as_mut(), Some(&mut 42));
541
542        let mut ioption = IOption::new(i32::NULL);
543        assert_eq!(ioption.as_mut(), None);
544    }
545
546    #[test]
547    fn test_map() {
548        let ioption = IOption::new(42);
549        let ioption = ioption.map(|value| value * 2);
550        assert_eq!(ioption.into_inner(), 84);
551
552        let ioption = IOption::new(i32::NULL);
553        let ioption = ioption.map(|value| value * 2);
554        assert_eq!(ioption.into_inner(), i32::NULL);
555    }
556
557    #[test]
558    fn test_map_or() {
559        let ioption = IOption::new(42);
560        let value = ioption.map_or(0, |value| value * 2);
561        assert_eq!(value, 84);
562
563        let ioption = IOption::new(i32::NULL);
564        let value = ioption.map_or(0, |value| value * 2);
565        assert_eq!(value, 0);
566    }
567
568    #[test]
569    fn test_map_or_else() {
570        let ioption = IOption::new(42);
571        let value = ioption.map_or_else(|| 0, |value| value * 2);
572        assert_eq!(value, 84);
573
574        let ioption = IOption::new(i32::NULL);
575        let value = ioption.map_or_else(|| 0, |value| value * 2);
576        assert_eq!(value, 0);
577    }
578
579    #[test]
580    fn test_ok_or() {
581        let ioption = IOption::new(42);
582        let result = ioption.ok_or("error");
583        assert_eq!(result, Ok(42));
584
585        let ioption = IOption::new(i32::NULL);
586        let result = ioption.ok_or("error");
587        assert_eq!(result, Err("error"));
588    }
589
590    #[test]
591    fn test_ok_or_else() {
592        let ioption = IOption::new(42);
593        let result = ioption.ok_or_else(|| "error");
594        assert_eq!(result, Ok(42));
595
596        let ioption = IOption::new(i32::NULL);
597        let result = ioption.ok_or_else(|| "error");
598        assert_eq!(result, Err("error"));
599    }
600
601    #[test]
602    fn test_and() {
603        let ioption = IOption::new(42);
604        let other = IOption::new(84);
605        let result = ioption.and(other);
606        assert_eq!(result.into_inner(), 84);
607
608        let ioption = IOption::new(i32::NULL);
609        let other = IOption::new(84);
610        let result = ioption.and(other);
611        assert_eq!(result.into_inner(), i32::NULL);
612    }
613
614    #[test]
615    fn test_and_then() {
616        let ioption = IOption::new(42);
617        let result = ioption.and_then(|value| IOption::new(value * 2));
618        assert_eq!(result.into_inner(), 84);
619
620        let ioption = IOption::new(i32::NULL);
621        let result = ioption.and_then(|value| IOption::new(value * 2));
622        assert_eq!(result.into_inner(), i32::NULL);
623    }
624
625    #[test]
626    fn test_or() {
627        let ioption = IOption::new(42);
628        let other = IOption::new(84);
629        let result = ioption.or(other);
630        assert_eq!(result.into_inner(), 42);
631
632        let ioption = IOption::new(i32::NULL);
633        let other = IOption::new(84);
634        let result = ioption.or(other);
635        assert_eq!(result.into_inner(), 84);
636    }
637
638    #[test]
639    fn test_or_else() {
640        let ioption = IOption::new(42);
641        let result = ioption.or_else(|| IOption::new(84));
642        assert_eq!(result.into_inner(), 42);
643
644        let ioption = IOption::new(i32::NULL);
645        let result = ioption.or_else(|| IOption::new(84));
646        assert_eq!(result.into_inner(), 84);
647    }
648
649    #[test]
650    fn test_unwrap() {
651        let ioption = IOption::new(42);
652        assert_eq!(ioption.unwrap(), 42);
653    }
654
655    #[test]
656    #[should_panic]
657    fn test_unwrap_panic() {
658        let ioption = IOption::new(i32::NULL);
659        ioption.unwrap();
660    }
661
662    #[test]
663    fn test_unwrap_or() {
664        let ioption = IOption::new(42);
665        assert_eq!(ioption.unwrap_or(0), 42);
666
667        let ioption = IOption::new(i32::NULL);
668        assert_eq!(ioption.unwrap_or(0), 0);
669    }
670
671    #[test]
672    fn test_unwrap_or_else() {
673        let ioption = IOption::new(42);
674        assert_eq!(ioption.unwrap_or_else(|| 0), 42);
675
676        let ioption = IOption::new(i32::NULL);
677        assert_eq!(ioption.unwrap_or_else(|| 0), 0);
678    }
679
680    #[test]
681    fn test_expect() {
682        let ioption = IOption::new(42);
683        assert_eq!(ioption.expect("error"), 42);
684    }
685
686    #[test]
687    #[should_panic]
688    fn test_expect_panic() {
689        let ioption = IOption::new(i32::NULL);
690        ioption.expect("error");
691    }
692
693    #[test]
694    fn test_iter() {
695        let ioption = IOption::new(42);
696        let mut iter = ioption.iter();
697        assert_eq!(iter.next(), Some(&42));
698        assert_eq!(iter.next(), None);
699    }
700
701    #[test]
702    fn test_iter_mut() {
703        let mut ioption = IOption::new(42);
704        let mut iter = ioption.iter_mut();
705        assert_eq!(iter.next(), Some(&mut 42));
706        assert_eq!(iter.next(), None);
707    }
708
709    #[test]
710    fn test_filter() {
711        let ioption = IOption::new(42);
712        let result = ioption.filter(|value| *value % 2 == 0);
713        assert_eq!(result.into_inner(), 42);
714
715        let ioption = IOption::new(43);
716        let result = ioption.filter(|value| *value % 2 == 0);
717        assert_eq!(result.into_inner(), i32::NULL);
718    }
719
720    #[test]
721    fn test_take() {
722        let mut ioption = IOption::new(42);
723        let result = ioption.take();
724        assert_eq!(result.into_inner(), 42);
725        assert!(ioption.is_none());
726    }
727
728    #[test]
729    fn test_replace() {
730        let mut ioption = IOption::new(42);
731        let result = ioption.replace(84);
732        assert_eq!(result.into_inner(), 42);
733        assert_eq!(ioption.into_inner(), 84);
734    }
735
736    #[test]
737    fn test_get_or_insert() {
738        let mut ioption = IOption::new(42);
739        let value = ioption.get_or_insert(84);
740        assert_eq!(value, &42);
741        assert_eq!(ioption.into_inner(), 42);
742
743        let mut ioption = IOption::new(i32::NULL);
744        let value = ioption.get_or_insert(84);
745        assert_eq!(value, &84);
746        assert_eq!(ioption.into_inner(), 84);
747    }
748
749    #[test]
750    fn test_get_or_insert_with() {
751        let mut ioption = IOption::new(42);
752        let value = ioption.get_or_insert_with(|| 84);
753        assert_eq!(value, &42);
754        assert_eq!(ioption.into_inner(), 42);
755
756        let mut ioption = IOption::new(i32::NULL);
757        let value = ioption.get_or_insert_with(|| 84);
758        assert_eq!(value, &84);
759        assert_eq!(ioption.into_inner(), 84);
760    }
761
762    #[test]
763    fn test_get_or_insert_default() {
764        let mut ioption = IOption::new(42);
765        let value = ioption.get_or_insert_default();
766        assert_eq!(value, &42);
767        assert_eq!(ioption.into_inner(), 42);
768
769        let mut ioption = IOption::new(i32::NULL);
770        let value = ioption.get_or_insert_default();
771        assert_eq!(value, &Default::default());
772        assert_eq!(ioption.into_inner(), Default::default());
773    }
774
775    #[test]
776    fn test_from_option() {
777        let option = Some(42);
778        let ioption = IOption::<i32>::from(option);
779        assert_eq!(ioption.into_inner(), 42);
780
781        let option = None;
782        let ioption = IOption::<i32>::from(option);
783        assert_eq!(ioption.into_inner(), i32::NULL);
784    }
785
786    #[test]
787    fn test_from_ioption() {
788        let ioption = IOption::new(42);
789        let option = Option::from(ioption);
790        assert_eq!(option, Some(42));
791
792        let ioption = IOption::new(i32::NULL);
793        let option = Option::<i32>::from(ioption);
794        assert_eq!(option, None);
795    }
796
797    #[test]
798    fn test_from_value() {
799        let value = 42;
800        let ioption = IOption::from(value);
801        assert_eq!(ioption.into_inner(), 42);
802    }
803
804    #[test]
805    fn test_default() {
806        let ioption = IOption::<i32>::default();
807        assert!(ioption.is_none());
808    }
809}