1use std::{cell::RefCell, fmt};
2
3use flux_arc_interner::{Internable, Interned};
4use flux_common::index::IndexGen;
5use flux_config as config;
6use rustc_abi::FieldIdx;
7use rustc_data_structures::unord::UnordMap;
8use rustc_hash::FxHashSet;
9use rustc_hir::def_id::DefId;
10use rustc_index::newtype_index;
11use rustc_middle::ty::TyCtxt;
12use rustc_span::{Pos, Span};
13use rustc_type_ir::{BoundVar, DebruijnIndex, INNERMOST};
14use serde::Serialize;
15
16#[macro_export]
17macro_rules! _with_cx {
18 ($cx:expr, $e:expr) => {
19 $crate::pretty::WithCx::new($cx, $e)
20 };
21}
22pub use crate::_with_cx as with_cx;
23use crate::def_id::{FluxDefId, FluxLocalDefId};
24
25#[macro_export]
26macro_rules! _format_args_cx {
27 ($cx:ident, $fmt:literal, $($args:tt)*) => {
28 $crate::_format_args_cx!(@go ($cx, $fmt; $($args)*) -> ())
29 };
30 ($cx:expr, $fmt:literal) => {
31 format_args!($fmt)
32 };
33 (@go ($cx:ident, $fmt:literal; ^$head:expr, $($tail:tt)*) -> ($($accum:tt)*)) => {
34 $crate::_format_args_cx!(@go ($cx, $fmt; $($tail)*) -> ($($accum)* $head,))
35 };
36 (@go ($cx:ident, $fmt:literal; $head:expr, $($tail:tt)*) -> ($($accum:tt)*)) => {
37 $crate::_format_args_cx!(@go ($cx, $fmt; $($tail)*) -> ($($accum)* $crate::pretty::with_cx!($cx, $head),))
38 };
39 (@go ($cx:ident, $fmt:literal; ^$head:expr) -> ($($accum:tt)*)) => {
40 $crate::_format_args_cx!(@as_expr format_args!($fmt, $($accum)* $head,))
41 };
42 (@go ($cx:ident, $fmt:literal; $head:expr) -> ($($accum:tt)*)) => {
43 $crate::_format_args_cx!(@as_expr format_args!($fmt, $($accum)* $crate::pretty::with_cx!($cx, $head),))
44 };
45 (@as_expr $e:expr) => { $e };
46}
47pub use crate::_format_args_cx as format_args_cx;
48
49#[macro_export]
50macro_rules! _format_cx {
51 ($($arg:tt)*) => {
52 std::fmt::format($crate::_format_args_cx!($($arg)*))
53 }
54}
55pub use crate::_format_cx as format_cx;
56
57#[macro_export]
58macro_rules! _w {
59 ($cx:expr, $f:expr, $fmt:literal, $($args:tt)*) => {{
60 #[allow(unused_variables)]
61 let cx = $cx;
62 $f.write_fmt($crate::_format_args_cx!(cx, $fmt, $($args)*))
63 }};
64 ($cx:expr, $f:expr, $fmt:literal) => {
65 $f.write_fmt($crate::_format_args_cx!($cx, $fmt))
66 };
67}
68pub use crate::_w as w;
69
70#[macro_export]
71macro_rules! _join {
72 ($sep:expr, $iter:expr) => {
73 $crate::pretty::Join::new($sep, $iter)
74 };
75}
76pub use crate::_join as join;
77
78#[macro_export]
79macro_rules! _parens {
80 ($val:expr, $parenthesize:expr) => {
81 $crate::pretty::Parens::new(&$val, $parenthesize)
82 };
83}
84pub use crate::_parens as parens;
85
86#[macro_export]
87macro_rules! _impl_debug_with_default_cx {
88 ($($ty:ty $(=> $key:literal)?),* $(,)?) => {$(
89 impl std::fmt::Debug for $ty {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 #[allow(unused_mut, unused_assignments)]
92 let mut key = None;
93 $(
94 key = Some($key);
95 )?
96 $crate::pretty::pprint_with_default_cx(f, self, key)
97 }
98 }
99 )*};
100}
101
102pub fn pprint_with_default_cx<T: Pretty>(
103 f: &mut std::fmt::Formatter<'_>,
104 t: &T,
105 cfg_key: Option<&'static str>,
106) -> std::fmt::Result {
107 rustc_middle::ty::tls::with(|tcx| {
108 #[allow(unused_mut)]
109 let mut cx = <T>::default_cx(tcx);
110
111 if let Some(pprint) = flux_config::CONFIG_FILE
112 .get("dev")
113 .and_then(|dev| dev.get("pprint"))
114 {
115 if let Some(opts) = pprint.get("default") {
116 cx.merge(opts);
117 }
118
119 if let Some(key) = cfg_key
120 && let Some(opts) = pprint.get(key)
121 {
122 cx.merge(opts);
123 }
124 }
125
126 if let Some(key) = cfg_key
127 && let Some(opts) = flux_config::CONFIG_FILE
128 .get("dev")
129 .and_then(|dev| dev.get("pprint"))
130 .and_then(|pprint| pprint.get(key))
131 {
132 cx.merge(opts);
133 }
134 Pretty::fmt(t, &cx, f)
135 })
136}
137
138pub use crate::_impl_debug_with_default_cx as impl_debug_with_default_cx;
139use crate::{
140 global_env::GlobalEnv,
141 rty::{AdtSortDef, BoundReft, BoundReftKind, BoundVariableKind, EarlyReftParam},
142};
143
144#[derive(Copy, Clone)]
145pub enum KVarArgs {
146 All,
147 SelfOnly,
148 Hide,
149}
150
151#[derive(Clone, Copy)]
152pub enum GenvOrTcx<'genv, 'tcx> {
153 Genv(GlobalEnv<'genv, 'tcx>),
154 Tcx(TyCtxt<'tcx>),
155}
156
157impl<'genv, 'tcx> GenvOrTcx<'genv, 'tcx> {
158 fn tcx(self) -> TyCtxt<'tcx> {
159 match self {
160 GenvOrTcx::Genv(genv) => genv.tcx(),
161 GenvOrTcx::Tcx(tcx) => tcx,
162 }
163 }
164
165 fn genv(self) -> Option<GlobalEnv<'genv, 'tcx>> {
166 match self {
167 GenvOrTcx::Genv(genv) => Some(genv),
168 GenvOrTcx::Tcx(_) => None,
169 }
170 }
171}
172
173impl<'tcx> From<TyCtxt<'tcx>> for GenvOrTcx<'_, 'tcx> {
174 fn from(v: TyCtxt<'tcx>) -> Self {
175 Self::Tcx(v)
176 }
177}
178
179impl<'genv, 'tcx> From<GlobalEnv<'genv, 'tcx>> for GenvOrTcx<'genv, 'tcx> {
180 fn from(v: GlobalEnv<'genv, 'tcx>) -> Self {
181 Self::Genv(v)
182 }
183}
184
185pub struct PrettyCx<'genv, 'tcx> {
186 pub cx: GenvOrTcx<'genv, 'tcx>,
187 pub kvar_args: KVarArgs,
188 pub fully_qualified_paths: bool,
189 pub simplify_exprs: bool,
190 pub tags: bool,
191 pub bindings_chain: bool,
192 pub preds_chain: bool,
193 pub full_spans: bool,
194 pub hide_uninit: bool,
195 pub hide_refinements: bool,
196 pub hide_regions: bool,
197 pub hide_sorts: bool,
198 pub bvar_env: BoundVarEnv,
199 pub earlyparam_env: RefCell<Option<EarlyParamEnv>>,
200}
201
202macro_rules! set_opts {
203 ($cx:expr, $opts:expr, [$($opt:ident),+ $(,)?]) => {
204 $(
205 if let Some(val) = $opts.get(stringify!($opt)).and_then(|v| FromOpt::from_opt(v)) {
206 $cx.$opt = val;
207 }
208 )+
209 };
210}
211
212impl<'genv, 'tcx> PrettyCx<'genv, 'tcx> {
213 pub fn default(cx: impl Into<GenvOrTcx<'genv, 'tcx>>) -> Self {
214 PrettyCx {
215 cx: cx.into(),
216 kvar_args: KVarArgs::SelfOnly,
217 fully_qualified_paths: false,
218 simplify_exprs: true,
219 tags: true,
220 bindings_chain: true,
221 preds_chain: true,
222 full_spans: false,
223 hide_uninit: true,
224 hide_refinements: false,
225 hide_regions: false,
226 hide_sorts: true,
227 bvar_env: BoundVarEnv::default(),
228 earlyparam_env: RefCell::new(None),
229 }
230 }
231
232 pub fn tcx(&self) -> TyCtxt<'tcx> {
233 self.cx.tcx()
234 }
235
236 pub fn genv(&self) -> Option<GlobalEnv<'genv, 'tcx>> {
237 self.cx.genv()
238 }
239
240 pub fn adt_sort_def_of(&self, def_id: DefId) -> Option<AdtSortDef> {
241 self.genv()
242 .and_then(|genv| genv.adt_sort_def_of(def_id).ok())
243 }
244
245 pub fn merge(&mut self, opts: &config::Value) {
246 set_opts!(
247 self,
248 opts,
249 [
250 kvar_args,
251 fully_qualified_paths,
252 simplify_exprs,
253 tags,
254 bindings_chain,
255 preds_chain,
256 full_spans,
257 hide_uninit,
258 hide_refinements,
259 hide_regions,
260 hide_sorts,
261 ]
262 );
263 }
264
265 pub fn with_bound_vars<R>(&self, vars: &[BoundVariableKind], f: impl FnOnce() -> R) -> R {
266 self.bvar_env.push_layer(vars, FxHashSet::default(), None);
267 let r = f();
268 self.bvar_env.pop_layer();
269 r
270 }
271
272 pub fn with_bound_vars_removable<R1, R2>(
273 &self,
274 vars: &[BoundVariableKind],
275 vars_to_remove: FxHashSet<BoundVar>,
276 fn_root_layer_type: Option<FnRootLayerType>,
277 fmt_body: impl FnOnce(&mut String) -> Result<R1, fmt::Error>,
278 fmt_vars_with_body: impl FnOnce(R1, BoundVarLayer, String) -> Result<R2, fmt::Error>,
279 ) -> Result<R2, fmt::Error> {
280 self.bvar_env
281 .push_layer(vars, vars_to_remove, fn_root_layer_type);
282 let mut body = String::new();
283 let r1 = fmt_body(&mut body)?;
284 let r2 = fmt_vars_with_body(r1, self.bvar_env.peek_layer().unwrap(), body)?;
291 self.bvar_env.pop_layer();
292 Ok(r2)
293 }
294
295 pub fn fmt_bound_vars(
296 &self,
297 print_infer_mode: bool,
298 left: &str,
299 vars: &[BoundVariableKind],
300 right: &str,
301 f: &mut impl fmt::Write,
302 ) -> fmt::Result {
303 w!(self, f, "{left}")?;
304 for (i, var) in vars.iter().enumerate() {
305 if i > 0 {
306 w!(self, f, ", ")?;
307 }
308 match var {
309 BoundVariableKind::Region(re) => w!(self, f, "{:?}", re)?,
310 BoundVariableKind::Refine(sort, mode, BoundReftKind::Named(name)) => {
311 if print_infer_mode {
312 w!(self, f, "{}", ^mode.prefix_str())?;
313 }
314 w!(self, f, "{}", ^name)?;
315 if !self.hide_sorts {
316 w!(self, f, ": {:?}", sort)?;
317 }
318 }
319 BoundVariableKind::Refine(sort, mode, BoundReftKind::Anon) => {
320 if print_infer_mode {
321 w!(self, f, "{}", ^mode.prefix_str())?;
322 }
323 if let Some(name) = self.bvar_env.lookup(INNERMOST, BoundVar::from_usize(i)) {
324 w!(self, f, "{:?}", ^name)?;
325 } else {
326 w!(self, f, "_")?;
327 }
328 if !self.hide_sorts {
329 w!(self, f, ": {:?}", sort)?;
330 }
331 }
332 }
333 }
334 w!(self, f, "{right}")
335 }
336
337 pub fn fmt_bound_reft(
338 &self,
339 debruijn: DebruijnIndex,
340 breft: BoundReft,
341 f: &mut fmt::Formatter<'_>,
342 ) -> fmt::Result {
343 match breft.kind {
344 BoundReftKind::Anon => {
345 if let Some(name) = self.bvar_env.lookup(debruijn, breft.var) {
346 w!(self, f, "{name:?}")
347 } else {
348 w!(self, f, "⭡{}/#{:?}", ^debruijn.as_usize(), ^breft.var)
349 }
350 }
351 BoundReftKind::Named(name) => {
352 w!(self, f, "{name}")
353 }
354 }
355 }
356
357 pub fn with_early_params<R>(&self, f: impl FnOnce() -> R) -> R {
358 assert!(self.earlyparam_env.borrow().is_none(), "Already in an early param env");
359 *self.earlyparam_env.borrow_mut() = Some(FxHashSet::default());
360 let r = f();
361 *self.earlyparam_env.borrow_mut() = None;
362 r
363 }
364
365 pub fn kvar_args(self, kvar_args: KVarArgs) -> Self {
366 Self { kvar_args, ..self }
367 }
368
369 pub fn fully_qualified_paths(self, b: bool) -> Self {
370 Self { fully_qualified_paths: b, ..self }
371 }
372
373 pub fn hide_regions(self, b: bool) -> Self {
374 Self { hide_regions: b, ..self }
375 }
376
377 pub fn hide_sorts(self, b: bool) -> Self {
378 Self { hide_sorts: b, ..self }
379 }
380
381 pub fn hide_refinements(self, b: bool) -> Self {
382 Self { hide_refinements: b, ..self }
383 }
384}
385
386newtype_index! {
387 #[debug_format = "b{}"]
389 pub struct BoundVarName {}
390}
391
392#[derive(Copy, Clone)]
393pub enum FnRootLayerType {
394 FnArgs,
395 FnRet,
396}
397
398#[derive(Clone)]
399pub struct FnRootLayerMap {
400 pub name_map: UnordMap<BoundVar, BoundVarName>,
401 pub seen_vars: FxHashSet<BoundVar>,
402 pub layer_type: FnRootLayerType,
403}
404
405#[derive(Clone)]
406pub struct BoundVarLayer {
407 pub layer_map: BoundVarLayerMap,
408 pub vars_to_remove: FxHashSet<BoundVar>,
409 pub successfully_removed_vars: FxHashSet<BoundVar>,
410}
411
412#[derive(Clone)]
413pub enum BoundVarLayerMap {
414 LayerMap(UnordMap<BoundVar, BoundVarName>),
415 FnRootLayerMap(FnRootLayerMap),
431}
432
433impl BoundVarLayerMap {
434 fn name_map(&self) -> &UnordMap<BoundVar, BoundVarName> {
435 match self {
436 Self::LayerMap(name_map) => name_map,
437 Self::FnRootLayerMap(root_layer) => &root_layer.name_map,
438 }
439 }
440}
441
442#[derive(Default)]
443pub struct BoundVarEnv {
444 name_gen: IndexGen<BoundVarName>,
445 layers: RefCell<Vec<BoundVarLayer>>,
446}
447
448impl BoundVarEnv {
449 pub fn check_if_seen_fn_root_bvar(
457 &self,
458 debruijn: DebruijnIndex,
459 var: BoundVar,
460 ) -> Option<(bool, FnRootLayerType)> {
461 let num_layers = self.layers.borrow().len();
462 let mut layer = self.layers.borrow_mut();
463 match layer.get_mut(num_layers.checked_sub(debruijn.as_usize() + 1)?)? {
464 BoundVarLayer {
465 layer_map: BoundVarLayerMap::FnRootLayerMap(fn_root_layer), ..
466 } => Some((!fn_root_layer.seen_vars.insert(var), fn_root_layer.layer_type)),
467 _ => None,
468 }
469 }
470
471 pub fn should_remove_var(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<bool> {
472 let layers = self.layers.borrow();
473 Some(
474 layers
475 .get(layers.len().checked_sub(debruijn.as_usize() + 1)?)?
476 .vars_to_remove
477 .contains(&var),
478 )
479 }
480
481 pub fn mark_var_as_removed(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<bool> {
482 let mut layers = self.layers.borrow_mut();
483 let layer_index = layers.len().checked_sub(debruijn.as_usize() + 1)?;
484 Some(
485 layers
486 .get_mut(layer_index)?
487 .successfully_removed_vars
488 .insert(var),
489 )
490 }
491
492 fn lookup(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<BoundVarName> {
493 let layers = self.layers.borrow();
494 layers
495 .get(layers.len().checked_sub(debruijn.as_usize() + 1)?)?
496 .layer_map
497 .name_map()
498 .get(&var)
499 .copied()
500 }
501
502 fn push_layer(
503 &self,
504 vars: &[BoundVariableKind],
505 vars_to_remove: FxHashSet<BoundVar>,
506 is_fn_root_layer: Option<FnRootLayerType>,
507 ) {
508 let mut name_map = UnordMap::default();
509 for (idx, var) in vars.iter().enumerate() {
510 if let BoundVariableKind::Refine(_, _, BoundReftKind::Anon) = var {
511 name_map.insert(BoundVar::from_usize(idx), self.name_gen.fresh());
512 }
513 }
514 let layer_map = if let Some(layer_type) = is_fn_root_layer {
515 BoundVarLayerMap::FnRootLayerMap(FnRootLayerMap {
516 name_map,
517 seen_vars: FxHashSet::default(),
518 layer_type,
519 })
520 } else {
521 BoundVarLayerMap::LayerMap(name_map)
522 };
523 let layer = BoundVarLayer {
524 layer_map,
525 vars_to_remove,
526 successfully_removed_vars: FxHashSet::default(),
527 };
528 self.layers.borrow_mut().push(layer);
529 }
530
531 fn peek_layer(&self) -> Option<BoundVarLayer> {
532 self.layers.borrow().last().cloned()
533 }
534
535 fn pop_layer(&self) -> Option<BoundVarLayer> {
536 self.layers.borrow_mut().pop()
537 }
538}
539
540type EarlyParamEnv = FxHashSet<EarlyReftParam>;
541
542pub struct WithCx<'a, 'genv, 'tcx, T> {
543 data: T,
544 cx: &'a PrettyCx<'genv, 'tcx>,
545}
546
547pub struct Join<'a, I> {
548 sep: &'a str,
549 iter: RefCell<Option<I>>,
550}
551
552pub struct Parens<'a, T> {
553 val: &'a T,
554 parenthesize: bool,
555}
556
557pub trait Pretty {
558 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result;
559
560 fn default_cx(tcx: TyCtxt) -> PrettyCx {
561 PrettyCx::default(tcx)
562 }
563}
564
565impl Pretty for String {
566 fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
567 write!(f, "{self}")
568 }
569}
570
571impl<'a, I> Join<'a, I> {
572 pub fn new<T: IntoIterator<IntoIter = I>>(sep: &'a str, iter: T) -> Self {
573 Self { sep, iter: RefCell::new(Some(iter.into_iter())) }
574 }
575}
576
577impl<'a, T> Parens<'a, T> {
578 pub fn new(val: &'a T, parenthesize: bool) -> Self {
579 Self { val, parenthesize }
580 }
581}
582
583impl<'a, 'genv, 'tcx, T> WithCx<'a, 'genv, 'tcx, T> {
584 pub fn new(cx: &'a PrettyCx<'genv, 'tcx>, data: T) -> Self {
585 Self { data, cx }
586 }
587}
588
589impl<T: Pretty + ?Sized> Pretty for &'_ T {
590 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
591 <T as Pretty>::fmt(self, cx, f)
592 }
593}
594
595impl<T: Pretty + Internable> Pretty for Interned<T> {
596 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
597 <T as Pretty>::fmt(self, cx, f)
598 }
599}
600
601impl<T, I> fmt::Debug for Join<'_, I>
602where
603 T: fmt::Debug,
604 I: Iterator<Item = T>,
605{
606 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
607 let Some(iter) = self.iter.borrow_mut().take() else {
608 panic!("Join: was already formatted once")
609 };
610 for (i, item) in iter.enumerate() {
611 if i > 0 {
612 write!(f, "{}", self.sep)?;
613 }
614 <T as fmt::Debug>::fmt(&item, f)?;
615 }
616 Ok(())
617 }
618}
619
620impl<T, I> Pretty for Join<'_, I>
621where
622 T: Pretty,
623 I: Iterator<Item = T>,
624{
625 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
626 let Some(iter) = self.iter.borrow_mut().take() else {
627 panic!("Join: was already formatted once")
628 };
629 for (i, item) in iter.enumerate() {
630 if i > 0 {
631 write!(f, "{}", self.sep)?;
632 }
633 <T as Pretty>::fmt(&item, cx, f)?;
634 }
635 Ok(())
636 }
637}
638impl<T> Pretty for Parens<'_, T>
639where
640 T: Pretty,
641{
642 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
643 if self.parenthesize {
644 write!(f, "(")?;
645 }
646 <T as Pretty>::fmt(self.val, cx, f)?;
647 if self.parenthesize {
648 write!(f, ")")?;
649 }
650 Ok(())
651 }
652}
653
654impl<T: Pretty> fmt::Debug for WithCx<'_, '_, '_, T> {
655 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
656 <T as Pretty>::fmt(&self.data, self.cx, f)
657 }
658}
659
660impl Pretty for DefId {
661 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
662 if cx.fully_qualified_paths {
663 w!(cx, f, "{}", ^cx.tcx().def_path_str(self))
664 } else {
665 let path = cx.tcx().def_path(*self);
666 w!(cx, f, "{}", ^path.data.last().unwrap().as_sym(false))
667 }
668 }
669}
670
671impl Pretty for FluxDefId {
672 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
673 if cx.fully_qualified_paths {
674 w!(cx, f, "{:?}::{}", self.parent(), ^self.name())
675 } else {
676 w!(cx, f, "{}", ^self.name())
677 }
678 }
679}
680
681impl Pretty for FluxLocalDefId {
682 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
683 w!(cx, f, "{:?}", self.to_def_id())
684 }
685}
686
687impl Pretty for FieldIdx {
688 fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
689 write!(f, "{}", self.as_u32())
690 }
691}
692
693impl Pretty for Span {
694 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
695 if cx.full_spans {
696 write!(f, "{self:?}")
697 } else {
698 let src_map = cx.tcx().sess.source_map();
699 let lo = src_map.lookup_char_pos(self.lo());
700 let hi = src_map.lookup_char_pos(self.hi());
701 write!(
724 f,
725 "{}:{}: {}:{}",
726 lo.line,
727 lo.col.to_usize() + 1,
728 hi.line,
729 hi.col.to_usize() + 1,
730 )
731 }
732 }
733}
734
735trait FromOpt: Sized {
736 fn from_opt(opt: &config::Value) -> Option<Self>;
737}
738
739impl FromOpt for bool {
740 fn from_opt(opt: &config::Value) -> Option<Self> {
741 opt.as_bool()
742 }
743}
744
745impl FromOpt for KVarArgs {
746 fn from_opt(opt: &config::Value) -> Option<Self> {
747 match opt.as_str() {
748 Some("self") => Some(KVarArgs::SelfOnly),
749 Some("hide") => Some(KVarArgs::Hide),
750 Some("all") => Some(KVarArgs::All),
751 _ => None,
752 }
753 }
754}
755
756#[derive(Serialize, Debug)]
759pub struct NestedString {
760 pub text: String,
761 pub key: Option<String>,
762 pub children: Option<Vec<NestedString>>,
763}
764
765pub fn debug_nested<T: Pretty>(cx: &PrettyCx, t: &T) -> Result<NestedString, fmt::Error> {
766 let t = WithCx::new(cx, t);
767 let text = format!("{t:?}");
768 Ok(NestedString { text, children: None, key: None })
769}
770
771pub fn float_children(children: Vec<Option<Vec<NestedString>>>) -> Option<Vec<NestedString>> {
772 let mut childrens: Vec<_> = children.into_iter().flatten().collect();
773 if childrens.is_empty() {
774 None
775 } else if childrens.len() == 1 {
776 let c = childrens.pop().unwrap();
777 Some(c)
778 } else {
779 let mut res = vec![];
780 for (i, children) in childrens.into_iter().enumerate() {
781 res.push(NestedString { text: format!("arg{i}"), children: Some(children), key: None });
782 }
783 Some(res)
784 }
785}
786
787pub trait PrettyNested {
788 fn fmt_nested(&self, cx: &PrettyCx) -> Result<NestedString, fmt::Error>;
789
790 fn nested_string(&self, cx: &PrettyCx) -> String {
791 let res = self.fmt_nested(cx).unwrap();
792 serde_json::to_string(&res).unwrap()
793 }
794}