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#[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 fn dbg_depth(
33 &self,
34 depth: usize,
35 ) -> Pin<Box<dyn Future<Output = anyhow::Result<String>> + Send + '_>>;
36}
37
38#[cfg(not(debug_assertions))]
42pub trait ValueDebug {}
43
44#[cfg(not(debug_assertions))]
45impl<T: ?Sized> ValueDebug for T {}
46
47pub 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#[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 pub enum ValueDebugFormatString<'a> {
73 Sync(String),
76 Async(BoxFuture<'a, anyhow::Result<String>>),
80 }
81
82 impl ValueDebugFormatString<'_> {
83 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 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}