chandeliers_err/transparent.rs
1//! Defines the wrapper [`Transparent`] that trivially implements
2//! equality and hashing on any type by making all instances indistinguishable
3//! and forgeable.
4
5use std::fmt;
6use std::hash::{Hash, Hasher};
7
8/// A type that transparently implements `PartialEq` and `Hash`, to be used
9/// in structs that carry additional data that should not be relevant in comparisons.
10///
11/// It additionally supports creating dummy values and they will also compare
12/// equal to all other and hash identically.
13///
14/// The use of a `Transparent<_>` as a field of a struct should be understood
15/// to convey the following intent: this field is useful only for diagnostics
16/// and is never relevant for decisions.
17/// This makes `Transparent::forge` suitable for use in unit tests where said
18/// diagnostic data may not be available and we still want the computation to
19/// be unaffected.
20///
21/// In fact the computation is guaranteed to be unaffected because `Transparent`
22/// provides no peeking method, and the only way to extract data is `unwrap`
23/// that will panic if the inner value was forged.
24#[derive(Clone, Copy)]
25pub struct Transparent<T> {
26 /// Payload.
27 inner: Result<T, &'static str>,
28}
29
30impl<T> fmt::Debug for Transparent<T> {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 write!(f, "_")
33 }
34}
35
36impl<T> Transparent<T> {
37 /// Assert that the value was not forged and get its contents.
38 /// # Panics
39 /// fails if `self` was obtainedy by `Transparent::forge()`.
40 pub fn unwrap(self) -> T {
41 match self.inner {
42 Ok(x) => x,
43 Err(l) => crate::abort!(
44 "Attempted to read a forged transparent value. Forged at {}",
45 l
46 ),
47 }
48 }
49
50 /// Create a dummy value.
51 ///
52 /// It is recommended to use the `loc` parameter as a way to track the location
53 /// where it was forged so that if it is accidentally unwrapped the error message
54 /// is more readable, e.g. by using either an identifying string or even better
55 /// the output of the `here!()` macro that gives the location in the source code.
56 #[must_use]
57 pub fn forge(loc: &'static str) -> Self {
58 Self { inner: Err(loc) }
59 }
60}
61
62impl<T> Transparent<Option<T>> {
63 /// If there is not already data, add it.
64 /// This is a noop if the contents were not forged.
65 pub fn try_override_inner(&mut self, t: T) {
66 match &mut self.inner {
67 Ok(s) => {
68 if s.is_none() {
69 *s = Some(t);
70 }
71 }
72 Err(_) => self.inner = Ok(Some(t)),
73 }
74 }
75}
76
77impl<T> Transparent<Transparent<T>> {
78 /// Remove the outer `Transparent`
79 pub fn flatten(self) -> Transparent<T> {
80 match self.inner {
81 Ok(x1) => match x1.inner {
82 Ok(x2) => Transparent { inner: Ok(x2) },
83 Err(loc) => Transparent { inner: Err(loc) },
84 },
85 Err(loc) => Transparent { inner: Err(loc) },
86 }
87 }
88}
89
90impl<T> Transparent<Option<T>> {
91 /// Remove the outer `Transparent`
92 pub fn flatten(self) -> Option<Transparent<T>> {
93 match self.inner {
94 Ok(x1) => Some(Transparent { inner: Ok(x1?) }),
95 Err(loc) => Some(Transparent { inner: Err(loc) }),
96 }
97 }
98}
99
100impl<T> Transparent<T> {
101 /// Wrap in a transparent.
102 pub fn from(inner: T) -> Self {
103 Self { inner: Ok(inner) }
104 }
105}
106
107impl<T> PartialEq for Transparent<T> {
108 /// Trivial equality. All `Transparent` are indistinguishable
109 /// from each other.
110 fn eq(&self, _: &Self) -> bool {
111 true
112 }
113}
114
115impl<T> Eq for Transparent<T> {}
116
117impl<T> Hash for Transparent<T> {
118 // Trivial hash. All `Transparent` are transparent to hashing.
119 fn hash<H: Hasher>(&self, _: &mut H) {}
120}
121
122impl<T: fmt::Display> fmt::Display for Transparent<T> {
123 /// Almost transparent wrapper for display.
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 match &self.inner {
126 Ok(v) => write!(f, "{v}"),
127 Err(_) => write!(f, "<undef>"),
128 }
129 }
130}
131
132impl Transparent<proc_macro2::Span> {
133 /// Map `join` to the inner `span`s
134 #[must_use]
135 pub fn join(self, other: Self) -> Option<Self> {
136 let Ok(v1) = self.inner else {
137 return Some(self);
138 };
139 let Ok(v2) = other.inner else {
140 return Some(self);
141 };
142 Some(Self {
143 inner: Ok(v1.join(v2)?),
144 })
145 }
146}
147
148impl<T: crate::TrySpan> crate::TrySpan for Transparent<T> {
149 /// `Sp` always has a span, so `TrySpan` is guaranteed to succeed.
150 fn try_span(&self) -> Option<crate::Span> {
151 self.inner.try_span()
152 }
153}