1#![doc = include_str!("../README.md")]
2
3use std::fmt::Debug;
4use std::ops::{Deref, DerefMut};
5
6pub trait Msg<T> {
8 fn fmt(value: &T, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error>;
13}
14
15#[derive(Debug, Clone)]
16pub struct WithTypeInfo;
17
18impl<T> Msg<T> for WithTypeInfo {
19 fn fmt(_value: &T, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
20 write!(f, "<no debug: {}>", std::any::type_name::<T>())
21 }
22}
23
24#[derive(Debug, Clone)]
25pub struct Ellipses;
26
27impl<T> Msg<T> for Ellipses {
28 fn fmt(_value: &T, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
29 write!(f, "...")
30 }
31}
32
33#[derive(Eq, Ord, Clone)]
35pub struct NoDebug<T, M: Msg<T> = WithTypeInfo>(T, std::marker::PhantomData<M>);
36
37impl<T: std::hash::Hash, M: Msg<T>> std::hash::Hash for NoDebug<T, M> {
38 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
39 self.0.hash::<H>(state)
40 }
41}
42
43impl<T: PartialOrd, M: Msg<T>> std::cmp::PartialOrd<T> for NoDebug<T, M> {
44 fn partial_cmp(&self, other: &T) -> Option<std::cmp::Ordering> {
45 self.0.partial_cmp(other)
46 }
47}
48
49impl<T: PartialOrd, M: Msg<T>, N: Msg<T>> std::cmp::PartialOrd<NoDebug<T, N>> for NoDebug<T, M> {
50 fn partial_cmp(&self, other: &NoDebug<T, N>) -> Option<std::cmp::Ordering> {
51 self.0.partial_cmp(&**other)
52 }
53}
54
55impl<T: PartialEq, M: Msg<T>> std::cmp::PartialEq<T> for NoDebug<T, M> {
56 fn eq(&self, other: &T) -> bool {
57 &self.0 == other
58 }
59}
60
61impl<T: PartialEq, M: Msg<T>, N: Msg<T>> std::cmp::PartialEq<NoDebug<T, N>> for NoDebug<T, M> {
62 fn eq(&self, other: &NoDebug<T, N>) -> bool {
63 **self == **other
64 }
65}
66
67impl<T, M: Msg<T>> NoDebug<T, M> {
68 pub fn take(self) -> T {
69 self.0
70 }
71}
72
73impl<T> NoDebug<T, WithTypeInfo> {
74 pub fn new(value: T) -> Self {
75 value.into()
76 }
77}
78
79impl<T, M: Msg<T>> From<T> for NoDebug<T, M> {
80 fn from(value: T) -> Self {
81 Self(value, std::marker::PhantomData::default())
82 }
83}
84
85impl<T, M: Msg<T>> Debug for NoDebug<T, M> {
86 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
87 M::fmt(&self.0, f)
88 }
89}
90
91impl<T, M: Msg<T>> Deref for NoDebug<T, M> {
92 type Target = T;
93 fn deref(&self) -> &Self::Target {
94 &self.0
95 }
96}
97
98impl<T, M: Msg<T>> DerefMut for NoDebug<T, M> {
99 fn deref_mut(&mut self) -> &mut Self::Target {
100 &mut self.0
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn cannot_debug_nodebug() {
110 let value = NoDebug::new(3);
111 assert_eq!(format!("{:?}", value), "<no debug: i32>")
112 }
113
114 #[test]
115 fn cannot_debug_nodebug_via_into() {
116 let value: NoDebug<i32> = 3.into();
117 assert_eq!(format!("{:?}", value), "<no debug: i32>")
118 }
119
120 #[test]
121 fn can_show_type_info() {
122 let value: NoDebug<i32, WithTypeInfo> = 3.into();
123 assert_eq!(format!("{:?}", value), "<no debug: i32>")
124 }
125
126 #[test]
127 fn can_show_custom_message() {
128 let value: NoDebug<i32, Ellipses> = 3.into();
129 assert_eq!(format!("{:?}", value), "...")
130 }
131
132 #[test]
133 fn dereferences_nodebug() {
134 let value = NoDebug::new(3);
135 assert_eq!(format!("{:?}", value), "<no debug: i32>");
136 assert_eq!(format!("{:?}", *value), "3");
137 }
138
139 #[test]
140 fn mut_dereferences_nodebug() {
141 let mut value = NoDebug::new(3);
142 *value = 4;
143 assert_eq!(format!("{:?}", value), "<no debug: i32>");
144 assert_eq!(format!("{:?}", *value), "4");
145 }
146
147 #[test]
148 fn take_gets_value_from_nodebug() {
149 let value = NoDebug::new(3);
150 assert_eq!(value.take(), 3);
151 }
152
153 #[test]
154 fn has_eq_with_inner() {
155 let value = NoDebug::new(3);
156 assert_eq!(*value, 3);
157 }
158
159 #[test]
160 fn has_eq_with_raw_value() {
161 let value = NoDebug::new(3);
162 assert_eq!(value, 3);
163 }
164
165 #[test]
166 fn has_eq_with_another_no_debug() {
167 let value = NoDebug::new(3);
168 let other = NoDebug::new(3);
169 assert_eq!(value, other);
170 }
171
172 #[test]
173 fn has_eq_with_another_no_debug_with_different_msg() {
174 let value: NoDebug<i32, Ellipses> = 3.into();
175 let other: NoDebug<i32, WithTypeInfo> = 3.into();
176 assert_eq!(value, other);
177 }
178
179 #[test]
180 fn has_ord_with_raw_value() {
181 let value = NoDebug::new(2);
182 assert!(value < 3);
183 }
184
185 #[test]
186 fn has_ord_with_another_no_debug() {
187 let value = NoDebug::new(2);
188 let other = NoDebug::new(3);
189 assert!(value < other);
190 }
191
192 #[test]
193 fn has_ord_with_another_no_debug_with_different_msg() {
194 let value: NoDebug<i32, Ellipses> = 2.into();
195 let other: NoDebug<i32, WithTypeInfo> = 3.into();
196 assert!(value < other);
197 }
198
199 fn get_hash<T>(obj: T) -> u64
200 where
201 T: std::hash::Hash,
202 {
203 use std::collections::hash_map::DefaultHasher;
204 use std::hash::Hasher;
205 let mut hasher = DefaultHasher::new();
206 obj.hash(&mut hasher);
207 hasher.finish()
208 }
209
210 #[test]
211 fn has_hash_with_raw_value() {
212 let value: NoDebug<i32> = 3.into();
213 assert_eq!(get_hash(value), get_hash(3));
214 }
215
216 #[test]
217 fn has_hash_with_another_no_debug() {
218 let value: NoDebug<i32> = 3.into();
219 let other: NoDebug<i32> = 3.into();
220 assert_eq!(get_hash(value), get_hash(other));
221 }
222
223 #[test]
224 fn has_hash_with_another_no_debug_with_different_msg() {
225 let value: NoDebug<i32, Ellipses> = 3.into();
226 let other: NoDebug<i32, WithTypeInfo> = 3.into();
227 assert_eq!(get_hash(value), get_hash(other));
228 }
229}