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}