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#[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 pub fn as_str(&self) -> &str {
39 &self.0
40 }
41}
42
43impl ValueDebugString {
44 pub fn new(s: String) -> Vc<Self> {
46 ValueDebugString::cell(ValueDebugString(s))
47 }
48}
49
50#[turbo_tasks::value_trait(no_debug)]
59pub trait ValueDebug {
60 #[turbo_tasks::function]
61 fn dbg(self: Vc<Self>) -> Vc<ValueDebugString>;
62
63 #[turbo_tasks::function]
65 fn dbg_depth(self: Vc<Self>, depth: usize) -> Vc<ValueDebugString>;
66}
67
68pub trait ValueDebugFormat {
72 fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString<'_>;
73}
74
75impl ValueDebugFormat for String {
76 fn value_debug_format(&self, _depth: usize) -> ValueDebugFormatString<'_> {
77 ValueDebugFormatString::Sync(format!("{self:#?}"))
78 }
79}
80
81impl ValueDebugFormat for RcStr {
82 fn value_debug_format(&self, _: usize) -> ValueDebugFormatString<'_> {
83 ValueDebugFormatString::Sync(self.to_string())
84 }
85}
86
87impl<T> ValueDebugFormat for &T
93where
94 T: Debug,
95{
96 fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString<'_> {
97 if depth == 0 {
98 return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
99 }
100
101 ValueDebugFormatString::Sync(format!("{self:#?}"))
102 }
103}
104
105impl<T> ValueDebugFormat for Option<T>
106where
107 T: ValueDebugFormat,
108{
109 fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString<'_> {
110 if depth == 0 {
111 return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
112 }
113
114 match self {
115 None => ValueDebugFormatString::Sync(format!("{:#?}", Option::<()>::None)),
116 Some(value) => match value.value_debug_format(depth.saturating_sub(1)) {
117 ValueDebugFormatString::Sync(string) => ValueDebugFormatString::Sync(format!(
118 "{:#?}",
119 Some(PassthroughDebug::new_string(string))
120 )),
121 ValueDebugFormatString::Async(future) => {
122 ValueDebugFormatString::Async(Box::pin(async move {
123 let string = future.await?;
124 Ok(format!("{:#?}", Some(PassthroughDebug::new_string(string))))
125 }))
126 }
127 },
128 }
129 }
130}
131
132impl<T> ValueDebugFormat for Vec<T>
133where
134 T: ValueDebugFormat,
135{
136 fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString<'_> {
137 if depth == 0 {
138 return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
139 }
140
141 let values = self
142 .iter()
143 .map(|value| value.value_debug_format(depth.saturating_sub(1)))
144 .collect::<Vec<_>>();
145
146 ValueDebugFormatString::Async(Box::pin(async move {
147 let mut values_string = vec![];
148 for value in values {
149 match value {
150 ValueDebugFormatString::Sync(string) => {
151 values_string.push(PassthroughDebug::new_string(string));
152 }
153 ValueDebugFormatString::Async(future) => {
154 values_string.push(PassthroughDebug::new_string(future.await?));
155 }
156 }
157 }
158 Ok(format!("{values_string:#?}"))
159 }))
160 }
161}
162
163impl<T, const N: usize> ValueDebugFormat for SmallVec<[T; N]>
164where
165 T: ValueDebugFormat,
166{
167 fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString<'_> {
168 if depth == 0 {
169 return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
170 }
171
172 let values = self
173 .iter()
174 .map(|value| value.value_debug_format(depth.saturating_sub(1)))
175 .collect::<Vec<_>>();
176
177 ValueDebugFormatString::Async(Box::pin(async move {
178 let mut values_string = vec![];
179 for value in values {
180 match value {
181 ValueDebugFormatString::Sync(string) => {
182 values_string.push(PassthroughDebug::new_string(string));
183 }
184 ValueDebugFormatString::Async(future) => {
185 values_string.push(PassthroughDebug::new_string(future.await?));
186 }
187 }
188 }
189 Ok(format!("{values_string:#?}"))
190 }))
191 }
192}
193
194impl<K> ValueDebugFormat for AutoSet<K>
195where
196 K: ValueDebugFormat,
197{
198 fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString<'_> {
199 if depth == 0 {
200 return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
201 }
202
203 let values = self
204 .iter()
205 .map(|item| item.value_debug_format(depth.saturating_sub(1)))
206 .collect::<Vec<_>>();
207
208 ValueDebugFormatString::Async(Box::pin(async move {
209 let mut values_string = Vec::with_capacity(values.len());
210 for item in values {
211 match item {
212 ValueDebugFormatString::Sync(string) => {
213 values_string.push(PassthroughDebug::new_string(string));
214 }
215 ValueDebugFormatString::Async(future) => {
216 values_string.push(PassthroughDebug::new_string(future.await?));
217 }
218 }
219 }
220 Ok(format!("{values_string:#?}"))
221 }))
222 }
223}
224
225impl<K, V, S> ValueDebugFormat for std::collections::HashMap<K, V, S>
226where
227 K: Debug,
228 V: ValueDebugFormat,
229{
230 fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString<'_> {
231 if depth == 0 {
232 return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
233 }
234
235 let values = self
236 .iter()
237 .map(|(key, value)| {
238 (
239 format!("{key:#?}"),
240 value.value_debug_format(depth.saturating_sub(1)),
241 )
242 })
243 .collect::<Vec<_>>();
244
245 ValueDebugFormatString::Async(Box::pin(async move {
246 let mut values_string = std::collections::HashMap::new();
247 for (key, value) in values {
248 match value {
249 ValueDebugFormatString::Sync(string) => {
250 values_string.insert(key, PassthroughDebug::new_string(string));
251 }
252 ValueDebugFormatString::Async(future) => {
253 values_string.insert(key, PassthroughDebug::new_string(future.await?));
254 }
255 }
256 }
257 Ok(format!("{values_string:#?}"))
258 }))
259 }
260}
261
262impl<K, V> ValueDebugFormat for AutoMap<K, V>
263where
264 K: Debug,
265 V: ValueDebugFormat,
266{
267 fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString<'_> {
268 if depth == 0 {
269 return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
270 }
271
272 let values = self
273 .iter()
274 .map(|(key, value)| {
275 (
276 format!("{key:#?}"),
277 value.value_debug_format(depth.saturating_sub(1)),
278 )
279 })
280 .collect::<Vec<_>>();
281
282 ValueDebugFormatString::Async(Box::pin(async move {
283 let mut values_string = AutoMap::new();
284 for (key, value) in values {
285 match value {
286 ValueDebugFormatString::Sync(string) => {
287 values_string.insert(key, PassthroughDebug::new_string(string));
288 }
289 ValueDebugFormatString::Async(future) => {
290 values_string.insert(key, PassthroughDebug::new_string(future.await?));
291 }
292 }
293 }
294 Ok(format!("{values_string:#?}"))
295 }))
296 }
297}
298
299impl<T> ValueDebugFormat for FxIndexSet<T>
300where
301 T: ValueDebugFormat,
302{
303 fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString<'_> {
304 if depth == 0 {
305 return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
306 }
307
308 let values = self
309 .iter()
310 .map(|value| value.value_debug_format(depth.saturating_sub(1)))
311 .collect::<Vec<_>>();
312
313 ValueDebugFormatString::Async(Box::pin(async move {
314 let mut values_string = FxIndexSet::default();
315 for value in values {
316 let value = match value {
317 ValueDebugFormatString::Sync(string) => string,
318 ValueDebugFormatString::Async(future) => future.await?,
319 };
320 values_string.insert(PassthroughDebug::new_string(value));
321 }
322 Ok(format!("{values_string:#?}"))
323 }))
324 }
325}
326
327impl<K, V> ValueDebugFormat for FxIndexMap<K, V>
328where
329 K: ValueDebugFormat,
330 V: ValueDebugFormat,
331{
332 fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString<'_> {
333 if depth == 0 {
334 return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
335 }
336
337 let values = self
338 .iter()
339 .map(|(key, value)| {
340 (
341 key.value_debug_format(depth.saturating_sub(1)),
342 value.value_debug_format(depth.saturating_sub(1)),
343 )
344 })
345 .collect::<Vec<_>>();
346
347 ValueDebugFormatString::Async(Box::pin(async move {
348 let mut values_string = FxIndexMap::default();
349 for (key, value) in values {
350 let key = match key {
351 ValueDebugFormatString::Sync(string) => string,
352 ValueDebugFormatString::Async(future) => future.await?,
353 };
354 let value = match value {
355 ValueDebugFormatString::Sync(string) => string,
356 ValueDebugFormatString::Async(future) => future.await?,
357 };
358 values_string.insert(
359 PassthroughDebug::new_string(key),
360 PassthroughDebug::new_string(value),
361 );
362 }
363 Ok(format!("{values_string:#?}"))
364 }))
365 }
366}
367
368macro_rules! tuple_impls {
369 ( $( $name:ident )+ ) => {
370 impl<$($name: ValueDebugFormat),+> ValueDebugFormat for ($($name,)+)
371 {
372 #[allow(non_snake_case)]
373 fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString<'_> {
374 if depth == 0 {
375 return ValueDebugFormatString::Sync(std::any::type_name::<Self>().to_string());
376 }
377
378 let ($($name,)+) = self;
379 let ($($name,)+) = ($($name.value_debug_format(depth.saturating_sub(1)),)+);
380
381 ValueDebugFormatString::Async(Box::pin(async move {
382 let values = ($(PassthroughDebug::new_string($name.try_to_string().await?),)+);
383 Ok(format!("{:#?}", values))
384 }))
385 }
386 }
387 };
388}
389
390tuple_impls! { A }
391tuple_impls! { A B }
392tuple_impls! { A B C }
393tuple_impls! { A B C D }
394tuple_impls! { A B C D E }
395tuple_impls! { A B C D E F }
396tuple_impls! { A B C D E F G }
397tuple_impls! { A B C D E F G H }
398tuple_impls! { A B C D E F G H I }
399tuple_impls! { A B C D E F G H I J }
400tuple_impls! { A B C D E F G H I J K }
401tuple_impls! { A B C D E F G H I J K L }
402
403pub enum ValueDebugFormatString<'a> {
405 Sync(String),
408 Async(
412 core::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<String>> + Send + 'a>>,
413 ),
414}
415
416impl ValueDebugFormatString<'_> {
417 pub async fn try_to_string(self) -> anyhow::Result<String> {
421 Ok(match self {
422 ValueDebugFormatString::Sync(value) => value,
423 ValueDebugFormatString::Async(future) => future.await?,
424 })
425 }
426
427 pub async fn try_to_value_debug_string(self) -> anyhow::Result<Vc<ValueDebugString>> {
431 Ok(ValueDebugString::new(self.try_to_string().await?))
432 }
433}