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> {
15 type Db: ?Sized;
16
17 fn debug<'me>(&'me self, db: &'db Self::Db) -> DebugWith<'me, 'db, Self::Db>
18 where
19 Self: Sized + 'me,
20 {
21 DebugWith { value: BoxRef::Ref(self), db }
22 }
23
24 fn into_debug<'me>(self, db: &'db Self::Db) -> DebugWith<'me, 'db, Self::Db>
25 where
26 Self: Sized + 'me,
27 {
28 DebugWith { value: BoxRef::Box(Box::new(self)), db }
29 }
30
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result;
32}
33
34pub trait DebugDbUpcast<'db, T: ?Sized> {
36 fn debug_db_upcast(&'db self) -> &'db T;
37}
38impl<'db, T: ?Sized> DebugDbUpcast<'db, T> for T {
39 fn debug_db_upcast(&'db self) -> &'db T {
40 self
41 }
42}
43
44pub struct DebugWith<'me, 'db, Db: ?Sized> {
45 value: BoxRef<'me, dyn DebugWithDb<'db, Db = Db> + 'me>,
46 db: &'db Db,
47}
48
49enum BoxRef<'me, T: ?Sized> {
50 Box(Box<T>),
51 Ref(&'me T),
52}
53
54impl<T: ?Sized> std::ops::Deref for BoxRef<'_, T> {
55 type Target = T;
56
57 fn deref(&self) -> &Self::Target {
58 match self {
59 BoxRef::Box(b) => b,
60 BoxRef::Ref(r) => r,
61 }
62 }
63}
64
65impl<D: ?Sized> std::fmt::Debug for DebugWith<'_, '_, D> {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 DebugWithDb::fmt(&*self.value, f, self.db)
68 }
69}
70
71impl<'db, T: ?Sized> DebugWithDb<'db> for &T
72where
73 T: DebugWithDb<'db>,
74{
75 type Db = T::Db;
76
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
78 T::fmt(self, f, db)
79 }
80}
81
82impl<'db, T: ?Sized> DebugWithDb<'db> for Box<T>
83where
84 T: DebugWithDb<'db>,
85{
86 type Db = T::Db;
87
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
89 T::fmt(self, f, db)
90 }
91}
92
93impl<'db, T> DebugWithDb<'db> for Rc<T>
94where
95 T: DebugWithDb<'db>,
96{
97 type Db = T::Db;
98
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
100 T::fmt(self, f, db)
101 }
102}
103
104impl<'db, T: ?Sized> DebugWithDb<'db> for Arc<T>
105where
106 T: DebugWithDb<'db>,
107{
108 type Db = T::Db;
109
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
111 T::fmt(self, f, db)
112 }
113}
114
115impl<'db, T> DebugWithDb<'db> for [T]
116where
117 T: DebugWithDb<'db>,
118{
119 type Db = T::Db;
120
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
122 let elements = self.iter().map(|e| e.debug(db));
123 f.debug_list().entries(elements).finish()
124 }
125}
126
127impl<'db, T> DebugWithDb<'db> for Vec<T>
128where
129 T: DebugWithDb<'db>,
130{
131 type Db = T::Db;
132
133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
134 let elements = self.iter().map(|e| e.debug(db));
135 f.debug_list().entries(elements).finish()
136 }
137}
138
139impl<'db, T> DebugWithDb<'db> for Option<T>
140where
141 T: DebugWithDb<'db>,
142{
143 type Db = T::Db;
144
145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
146 let me = self.as_ref().map(|v| v.debug(db));
147 std::fmt::Debug::fmt(&me, f)
148 }
149}
150
151impl<'db, K, V, S> DebugWithDb<'db> for HashMap<K, V, S>
152where
153 K: DebugWithDb<'db>,
154 V: DebugWithDb<'db, Db = K::Db>,
155{
156 type Db = K::Db;
157
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
159 let elements = self.iter().map(|(k, v)| (k.debug(db), v.debug(db)));
160 f.debug_map().entries(elements).finish()
161 }
162}
163
164impl<'db, K, V> DebugWithDb<'db> for BTreeMap<K, V>
165where
166 K: DebugWithDb<'db>,
167 V: DebugWithDb<'db, Db = K::Db>,
168{
169 type Db = K::Db;
170
171 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
172 let elements = self.iter().map(|(k, v)| (k.debug(db), v.debug(db)));
173 f.debug_map().entries(elements).finish()
174 }
175}
176
177impl<'db, K: Hash + Eq, V> DebugWithDb<'db> for OrderedHashMap<K, V>
178where
179 K: DebugWithDb<'db>,
180 V: DebugWithDb<'db, Db = K::Db>,
181{
182 type Db = K::Db;
183 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
184 let elements = self.iter().map(|(k, v)| (k.debug(db), v.debug(db)));
185 f.debug_map().entries(elements).finish()
186 }
187}
188
189impl<'db, A> DebugWithDb<'db> for (A,)
190where
191 A: DebugWithDb<'db>,
192{
193 type Db = A::Db;
194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
195 f.debug_tuple("").field(&self.0.debug(db)).finish()
196 }
197}
198
199impl<'db, A, B> DebugWithDb<'db> for (A, B)
200where
201 A: DebugWithDb<'db>,
202 B: DebugWithDb<'db> + 'db,
203 A::Db: DebugDbUpcast<'db, B::Db>,
204{
205 type Db = A::Db;
206 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
207 f.debug_tuple("")
208 .field(&self.0.debug(db))
209 .field(&self.1.debug(db.debug_db_upcast()))
210 .finish()
211 }
212}
213
214impl<'db, A, B, C> DebugWithDb<'db> for (A, B, C)
215where
216 A: DebugWithDb<'db>,
217 B: DebugWithDb<'db> + 'db,
218 C: DebugWithDb<'db> + 'db,
219 A::Db: DebugDbUpcast<'db, B::Db>,
220 A::Db: DebugDbUpcast<'db, C::Db>,
221{
222 type Db = A::Db;
223 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
224 f.debug_tuple("")
225 .field(&self.0.debug(db))
226 .field(&self.1.debug(db.debug_db_upcast()))
227 .field(&self.2.debug(db.debug_db_upcast()))
228 .finish()
229 }
230}
231
232impl<'db, V, S> DebugWithDb<'db> for HashSet<V, S>
233where
234 V: DebugWithDb<'db>,
235{
236 type Db = V::Db;
237 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
238 let elements = self.iter().map(|e| e.debug(db));
239 f.debug_list().entries(elements).finish()
240 }
241}
242
243impl<'db, V> DebugWithDb<'db> for BTreeSet<V>
244where
245 V: DebugWithDb<'db>,
246{
247 type Db = V::Db;
248 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
249 let elements = self.iter().map(|e| e.debug(db));
250 f.debug_list().entries(elements).finish()
251 }
252}
253
254impl<'db, V: Hash + Eq> DebugWithDb<'db> for OrderedHashSet<V>
255where
256 V: DebugWithDb<'db>,
257{
258 type Db = V::Db;
259 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
260 let elements = self.iter().map(|e| e.debug(db));
261 f.debug_list().entries(elements).finish()
262 }
263}
264
265pub trait DebugWithDbOverride<'db, Db: ?Sized> {
269 fn fmt_override(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Db) -> std::fmt::Result;
270}
271
272impl<'db, T> DebugWithDb<'db> for id_arena::Id<T>
273where
274 T: DebugWithDb<'db>,
275 id_arena::Id<T>: DebugWithDbOverride<'db, T::Db>,
276{
277 type Db = T::Db;
278
279 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
280 self.fmt_override(f, db)
281 }
282}
283
284#[doc(hidden)]
288pub mod helper {
289 use std::fmt;
290 use std::marker::PhantomData;
291
292 use super::{DebugDbUpcast, DebugWith, DebugWithDb};
293
294 pub trait Fallback<'db, T: fmt::Debug, Db: ?Sized> {
295 fn helper_debug(a: &'db T, _db: &'db Db) -> &'db dyn fmt::Debug {
296 a
297 }
298 }
299
300 pub struct HelperDebug<T, Db: ?Sized>(PhantomData<T>, PhantomData<Db>);
301
302 impl<'db, T: DebugWithDb<'db>, Db: ?Sized + DebugDbUpcast<'db, T::Db>> HelperDebug<T, Db> {
303 #[allow(dead_code)]
304 pub fn helper_debug<'me>(a: &'me T, db: &'db Db) -> DebugWith<'me, 'db, T::Db> {
305 a.debug(db.debug_db_upcast())
306 }
307 }
308
309 impl<'db, Everything, T: fmt::Debug, Db: ?Sized> Fallback<'db, T, Db> for Everything {}
310}