Skip to main content

turbo_tasks/debug/
mod.rs

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