turbo_tasks/debug/
mod.rs

1use std::fmt::{Debug, Display};
2
3use auto_hash_map::{AutoMap, AutoSet};
4use smallvec::SmallVec;
5use turbo_rcstr::RcStr;
6use turbo_tasks::{FxIndexMap, FxIndexSet, Vc};
7pub use turbo_tasks_macros::ValueDebugFormat;
8
9use crate::{self as turbo_tasks};
10
11#[doc(hidden)]
12pub mod internal;
13mod vdbg;
14
15use internal::PassthroughDebug;
16
17/// The return type of [`ValueDebug::dbg`].
18///
19/// We don't use [`Vc<RcStr>`][turbo_rcstr::RcStr] or [`String`] directly because we
20/// don't want the [`Debug`]/[`Display`] representations to be escaped.
21#[turbo_tasks::value]
22pub struct ValueDebugString(String);
23
24impl Debug for ValueDebugString {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        f.write_str(&self.0)
27    }
28}
29
30impl Display for ValueDebugString {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        f.write_str(&self.0)
33    }
34}
35
36impl ValueDebugString {
37    /// Returns the underlying string.
38    pub fn as_str(&self) -> &str {
39        &self.0
40    }
41}
42
43impl ValueDebugString {
44    /// Create a new `ValueDebugString` from a string.
45    pub fn new(s: String) -> Vc<Self> {
46        ValueDebugString::cell(ValueDebugString(s))
47    }
48}
49
50/// [`Debug`]-like trait for [`Vc`] types, automatically derived when using
51/// [`macro@turbo_tasks::value`] and [`turbo_tasks::value_trait`].
52///
53/// # Usage
54///
55/// ```ignore
56/// dbg!(any_vc.dbg().await?);
57/// ```
58#[turbo_tasks::value_trait(no_debug)]
59pub trait ValueDebug {
60    fn dbg(self: Vc<Self>) -> Vc<ValueDebugString>;
61
62    /// Like `dbg`, but with a depth limit.
63    fn dbg_depth(self: Vc<Self>, depth: usize) -> Vc<ValueDebugString>;
64}
65
66/// Use [autoref specialization] to implement [`ValueDebug`] for `T: Debug`.
67///
68/// [autoref specialization]: https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md
69pub trait ValueDebugFormat {
70    fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString;
71}
72
73impl ValueDebugFormat for String {
74    fn value_debug_format(&self, _depth: usize) -> ValueDebugFormatString {
75        ValueDebugFormatString::Sync(format!("{self:#?}"))
76    }
77}
78
79impl ValueDebugFormat for RcStr {
80    fn value_debug_format(&self, _: usize) -> ValueDebugFormatString {
81        ValueDebugFormatString::Sync(self.to_string())
82    }
83}
84
85// Use autoref specialization [1] to implement `ValueDebugFormat` for `T:
86// Debug` as a fallback if `T` does not implement it directly, hence the `for
87// &T` clause.
88//
89// [1] https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md
90impl<T> ValueDebugFormat for &T
91where
92    T: Debug,
93{
94    fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString {
95        if depth == 0 {
96            return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
97        }
98
99        ValueDebugFormatString::Sync(format!("{self:#?}"))
100    }
101}
102
103impl<T> ValueDebugFormat for Option<T>
104where
105    T: ValueDebugFormat,
106{
107    fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString {
108        if depth == 0 {
109            return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
110        }
111
112        match self {
113            None => ValueDebugFormatString::Sync(format!("{:#?}", Option::<()>::None)),
114            Some(value) => match value.value_debug_format(depth.saturating_sub(1)) {
115                ValueDebugFormatString::Sync(string) => ValueDebugFormatString::Sync(format!(
116                    "{:#?}",
117                    Some(PassthroughDebug::new_string(string))
118                )),
119                ValueDebugFormatString::Async(future) => {
120                    ValueDebugFormatString::Async(Box::pin(async move {
121                        let string = future.await?;
122                        Ok(format!("{:#?}", Some(PassthroughDebug::new_string(string))))
123                    }))
124                }
125            },
126        }
127    }
128}
129
130impl<T> ValueDebugFormat for Vec<T>
131where
132    T: ValueDebugFormat,
133{
134    fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString {
135        if depth == 0 {
136            return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
137        }
138
139        let values = self
140            .iter()
141            .map(|value| value.value_debug_format(depth.saturating_sub(1)))
142            .collect::<Vec<_>>();
143
144        ValueDebugFormatString::Async(Box::pin(async move {
145            let mut values_string = vec![];
146            for value in values {
147                match value {
148                    ValueDebugFormatString::Sync(string) => {
149                        values_string.push(PassthroughDebug::new_string(string));
150                    }
151                    ValueDebugFormatString::Async(future) => {
152                        values_string.push(PassthroughDebug::new_string(future.await?));
153                    }
154                }
155            }
156            Ok(format!("{values_string:#?}"))
157        }))
158    }
159}
160
161impl<T, const N: usize> ValueDebugFormat for SmallVec<[T; N]>
162where
163    T: ValueDebugFormat,
164{
165    fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString {
166        if depth == 0 {
167            return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
168        }
169
170        let values = self
171            .iter()
172            .map(|value| value.value_debug_format(depth.saturating_sub(1)))
173            .collect::<Vec<_>>();
174
175        ValueDebugFormatString::Async(Box::pin(async move {
176            let mut values_string = vec![];
177            for value in values {
178                match value {
179                    ValueDebugFormatString::Sync(string) => {
180                        values_string.push(PassthroughDebug::new_string(string));
181                    }
182                    ValueDebugFormatString::Async(future) => {
183                        values_string.push(PassthroughDebug::new_string(future.await?));
184                    }
185                }
186            }
187            Ok(format!("{values_string:#?}"))
188        }))
189    }
190}
191
192impl<K> ValueDebugFormat for AutoSet<K>
193where
194    K: ValueDebugFormat,
195{
196    fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString {
197        if depth == 0 {
198            return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
199        }
200
201        let values = self
202            .iter()
203            .map(|item| item.value_debug_format(depth.saturating_sub(1)))
204            .collect::<Vec<_>>();
205
206        ValueDebugFormatString::Async(Box::pin(async move {
207            let mut values_string = Vec::with_capacity(values.len());
208            for item in values {
209                match item {
210                    ValueDebugFormatString::Sync(string) => {
211                        values_string.push(PassthroughDebug::new_string(string));
212                    }
213                    ValueDebugFormatString::Async(future) => {
214                        values_string.push(PassthroughDebug::new_string(future.await?));
215                    }
216                }
217            }
218            Ok(format!("{values_string:#?}"))
219        }))
220    }
221}
222
223impl<K, V, S> ValueDebugFormat for std::collections::HashMap<K, V, S>
224where
225    K: Debug,
226    V: ValueDebugFormat,
227{
228    fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString {
229        if depth == 0 {
230            return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
231        }
232
233        let values = self
234            .iter()
235            .map(|(key, value)| {
236                (
237                    format!("{key:#?}"),
238                    value.value_debug_format(depth.saturating_sub(1)),
239                )
240            })
241            .collect::<Vec<_>>();
242
243        ValueDebugFormatString::Async(Box::pin(async move {
244            let mut values_string = std::collections::HashMap::new();
245            for (key, value) in values {
246                match value {
247                    ValueDebugFormatString::Sync(string) => {
248                        values_string.insert(key, PassthroughDebug::new_string(string));
249                    }
250                    ValueDebugFormatString::Async(future) => {
251                        values_string.insert(key, PassthroughDebug::new_string(future.await?));
252                    }
253                }
254            }
255            Ok(format!("{values_string:#?}"))
256        }))
257    }
258}
259
260impl<K, V> ValueDebugFormat for AutoMap<K, V>
261where
262    K: Debug,
263    V: ValueDebugFormat,
264{
265    fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString {
266        if depth == 0 {
267            return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
268        }
269
270        let values = self
271            .iter()
272            .map(|(key, value)| {
273                (
274                    format!("{key:#?}"),
275                    value.value_debug_format(depth.saturating_sub(1)),
276                )
277            })
278            .collect::<Vec<_>>();
279
280        ValueDebugFormatString::Async(Box::pin(async move {
281            let mut values_string = AutoMap::new();
282            for (key, value) in values {
283                match value {
284                    ValueDebugFormatString::Sync(string) => {
285                        values_string.insert(key, PassthroughDebug::new_string(string));
286                    }
287                    ValueDebugFormatString::Async(future) => {
288                        values_string.insert(key, PassthroughDebug::new_string(future.await?));
289                    }
290                }
291            }
292            Ok(format!("{values_string:#?}"))
293        }))
294    }
295}
296
297impl<T> ValueDebugFormat for FxIndexSet<T>
298where
299    T: ValueDebugFormat,
300{
301    fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString {
302        if depth == 0 {
303            return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
304        }
305
306        let values = self
307            .iter()
308            .map(|value| value.value_debug_format(depth.saturating_sub(1)))
309            .collect::<Vec<_>>();
310
311        ValueDebugFormatString::Async(Box::pin(async move {
312            let mut values_string = FxIndexSet::default();
313            for value in values {
314                let value = match value {
315                    ValueDebugFormatString::Sync(string) => string,
316                    ValueDebugFormatString::Async(future) => future.await?,
317                };
318                values_string.insert(PassthroughDebug::new_string(value));
319            }
320            Ok(format!("{values_string:#?}"))
321        }))
322    }
323}
324
325impl<K, V> ValueDebugFormat for FxIndexMap<K, V>
326where
327    K: ValueDebugFormat,
328    V: ValueDebugFormat,
329{
330    fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString {
331        if depth == 0 {
332            return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
333        }
334
335        let values = self
336            .iter()
337            .map(|(key, value)| {
338                (
339                    key.value_debug_format(depth.saturating_sub(1)),
340                    value.value_debug_format(depth.saturating_sub(1)),
341                )
342            })
343            .collect::<Vec<_>>();
344
345        ValueDebugFormatString::Async(Box::pin(async move {
346            let mut values_string = FxIndexMap::default();
347            for (key, value) in values {
348                let key = match key {
349                    ValueDebugFormatString::Sync(string) => string,
350                    ValueDebugFormatString::Async(future) => future.await?,
351                };
352                let value = match value {
353                    ValueDebugFormatString::Sync(string) => string,
354                    ValueDebugFormatString::Async(future) => future.await?,
355                };
356                values_string.insert(
357                    PassthroughDebug::new_string(key),
358                    PassthroughDebug::new_string(value),
359                );
360            }
361            Ok(format!("{values_string:#?}"))
362        }))
363    }
364}
365
366macro_rules! tuple_impls {
367    ( $( $name:ident )+ ) => {
368        impl<$($name: ValueDebugFormat),+> ValueDebugFormat for ($($name,)+)
369        {
370            #[allow(non_snake_case)]
371            fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString {
372                if depth == 0 {
373                    return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
374                }
375
376                let ($($name,)+) = self;
377                let ($($name,)+) = ($($name.value_debug_format(depth.saturating_sub(1)),)+);
378
379                ValueDebugFormatString::Async(Box::pin(async move {
380                    let values = ($(PassthroughDebug::new_string($name.try_to_string().await?),)+);
381                    Ok(format!("{:#?}", values))
382                }))
383            }
384        }
385    };
386}
387
388tuple_impls! { A }
389tuple_impls! { A B }
390tuple_impls! { A B C }
391tuple_impls! { A B C D }
392tuple_impls! { A B C D E }
393tuple_impls! { A B C D E F }
394tuple_impls! { A B C D E F G }
395tuple_impls! { A B C D E F G H }
396tuple_impls! { A B C D E F G H I }
397tuple_impls! { A B C D E F G H I J }
398tuple_impls! { A B C D E F G H I J K }
399tuple_impls! { A B C D E F G H I J K L }
400
401/// Output of `ValueDebugFormat::value_debug_format`.
402pub enum ValueDebugFormatString<'a> {
403    /// For the `T: Debug` fallback implementation, we can output a string
404    /// directly as the result of `format!("{:?}", t)`.
405    Sync(String),
406    /// For the `Vc` types and `Vc`-containing types implementations, we need to
407    /// resolve types asynchronously before we can format them, hence the need
408    /// for a future.
409    Async(
410        core::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<String>> + Send + 'a>>,
411    ),
412}
413
414impl ValueDebugFormatString<'_> {
415    /// Convert the `ValueDebugFormatString` into a `String`.
416    ///
417    /// This can fail when resolving `Vc` types.
418    pub async fn try_to_string(self) -> anyhow::Result<String> {
419        Ok(match self {
420            ValueDebugFormatString::Sync(value) => value,
421            ValueDebugFormatString::Async(future) => future.await?,
422        })
423    }
424
425    /// Convert the `ValueDebugFormatString` into a `Vc<ValueDebugString>`.
426    ///
427    /// This can fail when resolving `Vc` types.
428    pub async fn try_to_value_debug_string(self) -> anyhow::Result<Vc<ValueDebugString>> {
429        Ok(ValueDebugString::new(self.try_to_string().await?))
430    }
431}