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 fn dbg(self: Vc<Self>) -> Vc<ValueDebugString>;
61
62 fn dbg_depth(self: Vc<Self>, depth: usize) -> Vc<ValueDebugString>;
64}
65
66pub 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
85impl<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
401pub enum ValueDebugFormatString<'a> {
403 Sync(String),
406 Async(
410 core::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<String>> + Send + 'a>>,
411 ),
412}
413
414impl ValueDebugFormatString<'_> {
415 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 pub async fn try_to_value_debug_string(self) -> anyhow::Result<Vc<ValueDebugString>> {
429 Ok(ValueDebugString::new(self.try_to_string().await?))
430 }
431}