1#[cfg(test)]
2#[path = "debug_test.rs"]
3mod test;
4
5use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
7use std::hash::Hash;
8use std::rc::Rc;
9use std::sync::Arc;
10
11use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
12use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
13
14pub trait DebugWithDb<Db: ?Sized> {
15 fn debug<'me, 'db>(&'me self, db: &'me Db) -> DebugWith<'me, Db>
16 where
17 Self: Sized + 'me,
18 {
19 DebugWith { value: BoxRef::Ref(self), db }
20 }
21
22 #[allow(dead_code)]
23 fn into_debug<'me, 'db>(self, db: &'me Db) -> DebugWith<'me, Db>
24 where
25 Self: Sized + 'me,
26 {
27 DebugWith { value: BoxRef::Box(Box::new(self)), db }
28 }
29
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &Db) -> std::fmt::Result;
31}
32
33pub struct DebugWith<'me, Db: ?Sized> {
34 value: BoxRef<'me, dyn DebugWithDb<Db> + 'me>,
35 db: &'me Db,
36}
37
38enum BoxRef<'me, T: ?Sized> {
39 Box(Box<T>),
40 Ref(&'me T),
41}
42
43impl<T: ?Sized> std::ops::Deref for BoxRef<'_, T> {
44 type Target = T;
45
46 fn deref(&self) -> &Self::Target {
47 match self {
48 BoxRef::Box(b) => b,
49 BoxRef::Ref(r) => r,
50 }
51 }
52}
53
54impl<D: ?Sized> std::fmt::Debug for DebugWith<'_, D> {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 DebugWithDb::fmt(&*self.value, f, self.db)
57 }
58}
59
60impl<Db: ?Sized, T: ?Sized> DebugWithDb<Db> for &T
61where
62 T: DebugWithDb<Db>,
63{
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &Db) -> std::fmt::Result {
65 T::fmt(self, f, db)
66 }
67}
68
69impl<Db: ?Sized, T: ?Sized> DebugWithDb<Db> for Box<T>
70where
71 T: DebugWithDb<Db>,
72{
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &Db) -> std::fmt::Result {
74 T::fmt(self, f, db)
75 }
76}
77
78impl<Db: ?Sized, T> DebugWithDb<Db> for Rc<T>
79where
80 T: DebugWithDb<Db>,
81{
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &Db) -> std::fmt::Result {
83 T::fmt(self, f, db)
84 }
85}
86
87impl<Db: ?Sized, T: ?Sized> DebugWithDb<Db> for Arc<T>
88where
89 T: DebugWithDb<Db>,
90{
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &Db) -> std::fmt::Result {
92 T::fmt(self, f, db)
93 }
94}
95
96impl<Db: ?Sized, T> DebugWithDb<Db> for [T]
97where
98 T: DebugWithDb<Db>,
99{
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &Db) -> std::fmt::Result {
101 let elements = self.iter().map(|e| e.debug(db));
102 f.debug_list().entries(elements).finish()
103 }
104}
105
106impl<Db: ?Sized, T> DebugWithDb<Db> for Vec<T>
107where
108 T: DebugWithDb<Db>,
109{
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &Db) -> std::fmt::Result {
111 let elements = self.iter().map(|e| e.debug(db));
112 f.debug_list().entries(elements).finish()
113 }
114}
115
116impl<Db: ?Sized, T> DebugWithDb<Db> for Option<T>
117where
118 T: DebugWithDb<Db>,
119{
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &Db) -> std::fmt::Result {
121 let me = self.as_ref().map(|v| v.debug(db));
122 std::fmt::Debug::fmt(&me, f)
123 }
124}
125
126impl<Db: ?Sized, K, V, S> DebugWithDb<Db> for HashMap<K, V, S>
127where
128 K: DebugWithDb<Db>,
129 V: DebugWithDb<Db>,
130{
131 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &Db) -> std::fmt::Result {
132 let elements = self.iter().map(|(k, v)| (k.debug(db), v.debug(db)));
133 f.debug_map().entries(elements).finish()
134 }
135}
136
137impl<Db: ?Sized, K, V> DebugWithDb<Db> for BTreeMap<K, V>
138where
139 K: DebugWithDb<Db>,
140 V: DebugWithDb<Db>,
141{
142 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &Db) -> std::fmt::Result {
143 let elements = self.iter().map(|(k, v)| (k.debug(db), v.debug(db)));
144 f.debug_map().entries(elements).finish()
145 }
146}
147
148impl<Db: ?Sized, K: Hash + Eq, V> DebugWithDb<Db> for OrderedHashMap<K, V>
149where
150 K: DebugWithDb<Db>,
151 V: DebugWithDb<Db>,
152{
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &Db) -> std::fmt::Result {
154 let elements = self.iter().map(|(k, v)| (k.debug(db), v.debug(db)));
155 f.debug_map().entries(elements).finish()
156 }
157}
158
159impl<Db: ?Sized, A> DebugWithDb<Db> for (A,)
160where
161 A: DebugWithDb<Db>,
162{
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &Db) -> std::fmt::Result {
164 f.debug_tuple("").field(&self.0.debug(db)).finish()
165 }
166}
167
168impl<Db: ?Sized, A, B> DebugWithDb<Db> for (A, B)
169where
170 A: DebugWithDb<Db>,
171 B: DebugWithDb<Db>,
172{
173 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &Db) -> std::fmt::Result {
174 f.debug_tuple("").field(&self.0.debug(db)).field(&self.1.debug(db)).finish()
175 }
176}
177
178impl<Db: ?Sized, A, B, C> DebugWithDb<Db> for (A, B, C)
179where
180 A: DebugWithDb<Db>,
181 B: DebugWithDb<Db>,
182 C: DebugWithDb<Db>,
183{
184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &Db) -> std::fmt::Result {
185 f.debug_tuple("")
186 .field(&self.0.debug(db))
187 .field(&self.1.debug(db))
188 .field(&self.2.debug(db))
189 .finish()
190 }
191}
192
193impl<Db: ?Sized, V, S> DebugWithDb<Db> for HashSet<V, S>
194where
195 V: DebugWithDb<Db>,
196{
197 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &Db) -> std::fmt::Result {
198 let elements = self.iter().map(|e| e.debug(db));
199 f.debug_list().entries(elements).finish()
200 }
201}
202
203impl<Db: ?Sized, V> DebugWithDb<Db> for BTreeSet<V>
204where
205 V: DebugWithDb<Db>,
206{
207 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &Db) -> std::fmt::Result {
208 let elements = self.iter().map(|e| e.debug(db));
209 f.debug_list().entries(elements).finish()
210 }
211}
212
213impl<Db: ?Sized, V: Hash + Eq> DebugWithDb<Db> for OrderedHashSet<V>
214where
215 V: DebugWithDb<Db>,
216{
217 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &Db) -> std::fmt::Result {
218 let elements = self.iter().map(|e| e.debug(db));
219 f.debug_list().entries(elements).finish()
220 }
221}
222
223#[doc(hidden)]
227pub mod helper {
228 use std::fmt;
229 use std::marker::PhantomData;
230
231 use super::{DebugWith, DebugWithDb};
232
233 pub trait Fallback<T: fmt::Debug, Db: ?Sized> {
234 fn helper_debug<'a>(a: &'a T, _db: &Db) -> &'a dyn fmt::Debug {
235 a
236 }
237 }
238
239 pub struct HelperDebug<T, Db: ?Sized>(PhantomData<T>, PhantomData<Db>);
240
241 impl<T: DebugWithDb<Db>, Db: ?Sized> HelperDebug<T, Db> {
242 #[allow(dead_code)]
243 pub fn helper_debug<'a, 'b: 'a>(a: &'a T, db: &'b Db) -> DebugWith<'a, Db> {
244 a.debug(db)
245 }
246 }
247
248 impl<Everything, Db: ?Sized, T: fmt::Debug> Fallback<T, Db> for Everything {}
249}