debug_value/
lib.rs

1#![no_std]
2//! A `DebugValue<T>` is a `T` that is only present if debug assertions are enabled.
3//! On debug builds it has the exact same layout as `T`.
4//! On release builds it is a zero-sized-type.
5//!```
6//! # use debug_value::DebugValue;
7//! # fn f(s:&mut String){}
8//! let mut x = String::from("Mutated but restored by f");
9//! let passed_in = DebugValue::new_cloned(&x);
10//! f(&mut x);
11//! passed_in.assert_eq(&x);
12//! ```
13use core::fmt::Debug;
14#[cfg(not(debug_assertions))]
15use core::marker::PhantomData;
16
17#[repr(transparent)]
18pub struct DebugValue<T>(
19    #[cfg(debug_assertions)] T,
20    #[cfg(not(debug_assertions))] PhantomData<T>,
21);
22
23macro_rules! if_else {
24    ($a:expr,$b:expr) => {{
25        #[cfg(debug_assertions)]
26        {
27            $a
28        }
29        #[cfg(not(debug_assertions))]
30        {
31            $b
32        }
33    }};
34    ($a:expr) => {
35        if_else! { $a,PhantomData }
36    };
37}
38
39#[cfg_attr(not(debug_assertions), allow(unused_variables))]
40impl<T> DebugValue<T> {
41    pub fn new_cloned(x: &T) -> Self
42    where
43        T: Clone,
44    {
45        Self(if_else!(x.clone()))
46    }
47
48    #[inline]
49    pub fn new_with(f: impl FnOnce() -> T) -> Self {
50        Self(if_else!(f()))
51    }
52
53    #[inline]
54    pub fn new(x: T) -> Self {
55        Self(if_else!(x))
56    }
57
58    #[inline]
59    pub fn map<R>(self, f: impl FnOnce(T) -> R) -> DebugValue<R> {
60        DebugValue(if_else!(f(self.0)))
61    }
62
63    #[inline]
64    pub fn zip<U>(self, other: DebugValue<U>) -> DebugValue<(T, U)> {
65        DebugValue(if_else!((self.0, other.0)))
66    }
67
68    #[inline]
69    pub fn as_ref(&self) -> DebugValue<&T> {
70        DebugValue(if_else!(&self.0))
71    }
72
73    #[inline]
74    pub fn as_mut(&mut self) -> DebugValue<&mut T> {
75        DebugValue(if_else!(&mut self.0))
76    }
77
78    #[inline]
79    pub fn assert_eq(&self, other: &T)
80    where
81        T: Debug + PartialEq,
82    {
83        #[cfg(debug_assertions)]
84        {
85            assert_eq!(&self.0, other)
86        }
87    }
88
89    #[inline]
90    pub fn into_option(self) -> Option<T> {
91        if_else!(Some(self.0), None)
92    }
93
94    #[inline]
95    pub fn from_option(x: Option<T>) -> Self {
96        DebugValue(if_else!(x.unwrap(), PhantomData))
97    }
98
99    #[inline]
100    pub fn try_from_option(x: Option<T>) -> Option<Self> {
101        Some(DebugValue(if_else!(x?, PhantomData)))
102    }
103}