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