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(
312 &self,
313 print_infer_mode: bool,
314 left: &str,
315 vars: &[BoundVariableKind],
316 right: &str,
317 f: &mut impl fmt::Write,
318 ) -> fmt::Result {
319 w!(self, f, "{left}")?;
320 for (i, var) in vars.iter().enumerate() {
321 if i > 0 {
322 w!(self, f, ", ")?;
323 }
324 match var {
325 BoundVariableKind::Region(re) => w!(self, f, "{:?}", re)?,
326 BoundVariableKind::Refine(sort, mode, BoundReftKind::Named(name)) => {
327 if print_infer_mode {
328 w!(self, f, "{}", ^mode.prefix_str())?;
329 }
330 w!(self, f, "{}", ^name)?;
331 if !self.hide_sorts {
332 w!(self, f, ": {:?}", sort)?;
333 }
334 }
335 BoundVariableKind::Refine(sort, mode, BoundReftKind::Anon) => {
336 if print_infer_mode {
337 w!(self, f, "{}", ^mode.prefix_str())?;
338 }
339 if let Some(name) = self.bvar_env.lookup(INNERMOST, BoundVar::from_usize(i)) {
340 w!(self, f, "{:?}", ^name)?;
341 } else {
342 w!(self, f, "_")?;
343 }
344 if !self.hide_sorts {
345 w!(self, f, ": {:?}", sort)?;
346 }
347 }
348 }
349 }
350 w!(self, f, "{right}")
351 }
352
353 pub fn fmt_bound_reft(
354 &self,
355 debruijn: DebruijnIndex,
356 breft: BoundReft,
357 f: &mut fmt::Formatter<'_>,
358 ) -> fmt::Result {
359 match breft.kind {
360 BoundReftKind::Anon => {
361 if let Some(name) = self.bvar_env.lookup(debruijn, breft.var) {
362 w!(self, f, "{name:?}")
363 } else {
364 w!(self, f, "⭡{}/#{:?}", ^debruijn.as_usize(), ^breft.var)
365 }
366 }
367 BoundReftKind::Named(name) => {
368 w!(self, f, "{name}")
369 }
370 }
371 }
372
373 pub fn with_early_params<R>(&self, f: impl FnOnce() -> R) -> R {
374 assert!(self.earlyparam_env.borrow().is_none(), "Already in an early param env");
375 *self.earlyparam_env.borrow_mut() = Some(FxHashSet::default());
376 let r = f();
377 *self.earlyparam_env.borrow_mut() = None;
378 r
379 }
380
381 pub fn kvar_args(self, kvar_args: KVarArgs) -> Self {
382 Self { kvar_args, ..self }
383 }
384
385 pub fn fully_qualified_paths(self, b: bool) -> Self {
386 Self { fully_qualified_paths: b, ..self }
387 }
388
389 pub fn hide_regions(self, b: bool) -> Self {
390 Self { hide_regions: b, ..self }
391 }
392
393 pub fn hide_sorts(self, b: bool) -> Self {
394 Self { hide_sorts: b, ..self }
395 }
396
397 pub fn hide_refinements(self, b: bool) -> Self {
398 Self { hide_refinements: b, ..self }
399 }
400}
401
402newtype_index! {
403 #[debug_format = "b{}"]
405 pub struct BoundVarName {}
406}
407
408#[derive(Copy, Clone)]
409pub enum FnRootLayerType {
410 FnArgs,
411 FnRet,
412}
413
414#[derive(Clone)]
415pub struct FnRootLayerMap {
416 pub name_map: UnordMap<BoundVar, BoundVarName>,
417 pub seen_vars: FxHashSet<BoundVar>,
418 pub layer_type: FnRootLayerType,
419}
420
421#[derive(Clone)]
422pub struct BoundVarLayer {
423 pub layer_map: BoundVarLayerMap,
424 pub vars_to_remove: FxHashSet<BoundVar>,
425 pub successfully_removed_vars: FxHashSet<BoundVar>,
426}
427
428#[derive(Clone)]
429pub enum BoundVarLayerMap {
430 LayerMap(UnordMap<BoundVar, BoundVarName>),
431 FnRootLayerMap(FnRootLayerMap),
447}
448
449impl BoundVarLayerMap {
450 fn name_map(&self) -> &UnordMap<BoundVar, BoundVarName> {
451 match self {
452 Self::LayerMap(name_map) => name_map,
453 Self::FnRootLayerMap(root_layer) => &root_layer.name_map,
454 }
455 }
456}
457
458#[derive(Default)]
459pub struct BoundVarEnv {
460 name_gen: IndexGen<BoundVarName>,
461 layers: RefCell<Vec<BoundVarLayer>>,
462}
463
464impl BoundVarEnv {
465 pub fn check_if_seen_fn_root_bvar(
473 &self,
474 debruijn: DebruijnIndex,
475 var: BoundVar,
476 ) -> Option<(bool, FnRootLayerType)> {
477 let num_layers = self.layers.borrow().len();
478 let mut layer = self.layers.borrow_mut();
479 match layer.get_mut(num_layers.checked_sub(debruijn.as_usize() + 1)?)? {
480 BoundVarLayer {
481 layer_map: BoundVarLayerMap::FnRootLayerMap(fn_root_layer), ..
482 } => Some((!fn_root_layer.seen_vars.insert(var), fn_root_layer.layer_type)),
483 _ => None,
484 }
485 }
486
487 pub fn should_remove_var(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<bool> {
488 let layers = self.layers.borrow();
489 Some(
490 layers
491 .get(layers.len().checked_sub(debruijn.as_usize() + 1)?)?
492 .vars_to_remove
493 .contains(&var),
494 )
495 }
496
497 pub fn mark_var_as_removed(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<bool> {
498 let mut layers = self.layers.borrow_mut();
499 let layer_index = layers.len().checked_sub(debruijn.as_usize() + 1)?;
500 Some(
501 layers
502 .get_mut(layer_index)?
503 .successfully_removed_vars
504 .insert(var),
505 )
506 }
507
508 fn lookup(&self, debruijn: DebruijnIndex, var: BoundVar) -> Option<BoundVarName> {
509 let layers = self.layers.borrow();
510 layers
511 .get(layers.len().checked_sub(debruijn.as_usize() + 1)?)?
512 .layer_map
513 .name_map()
514 .get(&var)
515 .copied()
516 }
517
518 fn push_layer(
519 &self,
520 vars: &[BoundVariableKind],
521 vars_to_remove: FxHashSet<BoundVar>,
522 is_fn_root_layer: Option<FnRootLayerType>,
523 ) {
524 let mut name_map = UnordMap::default();
525 for (idx, var) in vars.iter().enumerate() {
526 if let BoundVariableKind::Refine(_, _, BoundReftKind::Anon) = var {
527 name_map.insert(BoundVar::from_usize(idx), self.name_gen.fresh());
528 }
529 }
530 let layer_map = if let Some(layer_type) = is_fn_root_layer {
531 BoundVarLayerMap::FnRootLayerMap(FnRootLayerMap {
532 name_map,
533 seen_vars: FxHashSet::default(),
534 layer_type,
535 })
536 } else {
537 BoundVarLayerMap::LayerMap(name_map)
538 };
539 let layer = BoundVarLayer {
540 layer_map,
541 vars_to_remove,
542 successfully_removed_vars: FxHashSet::default(),
543 };
544 self.layers.borrow_mut().push(layer);
545 }
546
547 fn peek_layer(&self) -> Option<BoundVarLayer> {
548 self.layers.borrow().last().cloned()
549 }
550
551 fn pop_layer(&self) -> Option<BoundVarLayer> {
552 self.layers.borrow_mut().pop()
553 }
554}
555
556type EarlyParamEnv = FxHashSet<EarlyReftParam>;
557
558pub struct WithCx<'a, 'genv, 'tcx, T> {
559 data: T,
560 cx: &'a PrettyCx<'genv, 'tcx>,
561}
562
563pub struct Join<'a, I> {
564 sep: &'a str,
565 iter: RefCell<Option<I>>,
566}
567
568pub struct Parens<'a, T> {
569 val: &'a T,
570 parenthesize: bool,
571}
572
573pub trait Pretty {
574 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result;
575
576 fn default_cx(tcx: TyCtxt) -> PrettyCx {
577 PrettyCx::default(tcx)
578 }
579}
580
581impl Pretty for String {
582 fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
583 write!(f, "{self}")
584 }
585}
586
587impl<'a, I> Join<'a, I> {
588 pub fn new<T: IntoIterator<IntoIter = I>>(sep: &'a str, iter: T) -> Self {
589 Self { sep, iter: RefCell::new(Some(iter.into_iter())) }
590 }
591}
592
593impl<'a, T> Parens<'a, T> {
594 pub fn new(val: &'a T, parenthesize: bool) -> Self {
595 Self { val, parenthesize }
596 }
597}
598
599impl<'a, 'genv, 'tcx, T> WithCx<'a, 'genv, 'tcx, T> {
600 pub fn new(cx: &'a PrettyCx<'genv, 'tcx>, data: T) -> Self {
601 Self { data, cx }
602 }
603}
604
605impl<T: Pretty + ?Sized> Pretty for &'_ T {
606 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
607 <T as Pretty>::fmt(self, cx, f)
608 }
609}
610
611impl<T: Pretty + Internable> Pretty for Interned<T> {
612 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
613 <T as Pretty>::fmt(self, cx, f)
614 }
615}
616
617impl<T, I> fmt::Debug for Join<'_, I>
618where
619 T: fmt::Debug,
620 I: Iterator<Item = T>,
621{
622 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
623 let Some(iter) = self.iter.borrow_mut().take() else {
624 panic!("Join: was already formatted once")
625 };
626 for (i, item) in iter.enumerate() {
627 if i > 0 {
628 write!(f, "{}", self.sep)?;
629 }
630 <T as fmt::Debug>::fmt(&item, f)?;
631 }
632 Ok(())
633 }
634}
635
636impl<T, I> Pretty for Join<'_, I>
637where
638 T: Pretty,
639 I: Iterator<Item = T>,
640{
641 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
642 let Some(iter) = self.iter.borrow_mut().take() else {
643 panic!("Join: was already formatted once")
644 };
645 for (i, item) in iter.enumerate() {
646 if i > 0 {
647 write!(f, "{}", self.sep)?;
648 }
649 <T as Pretty>::fmt(&item, cx, f)?;
650 }
651 Ok(())
652 }
653}
654impl<T> Pretty for Parens<'_, T>
655where
656 T: Pretty,
657{
658 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
659 if self.parenthesize {
660 write!(f, "(")?;
661 }
662 <T as Pretty>::fmt(self.val, cx, f)?;
663 if self.parenthesize {
664 write!(f, ")")?;
665 }
666 Ok(())
667 }
668}
669
670impl<T: Pretty> fmt::Debug for WithCx<'_, '_, '_, T> {
671 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
672 <T as Pretty>::fmt(&self.data, self.cx, f)
673 }
674}
675
676impl Pretty for DefId {
677 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
678 if cx.fully_qualified_paths {
679 w!(cx, f, "{}", ^cx.tcx().def_path_str(self))
680 } else {
681 let path = cx.tcx().def_path(*self);
682 w!(cx, f, "{}", ^path.data.last().unwrap().as_sym(false))
683 }
684 }
685}
686
687impl Pretty for FluxDefId {
688 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
689 if cx.fully_qualified_paths {
690 w!(cx, f, "{:?}::{}", self.parent(), ^self.name())
691 } else {
692 w!(cx, f, "{}", ^self.name())
693 }
694 }
695}
696
697impl Pretty for FluxLocalDefId {
698 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
699 w!(cx, f, "{:?}", self.to_def_id())
700 }
701}
702
703impl Pretty for FieldIdx {
704 fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
705 write!(f, "{}", self.as_u32())
706 }
707}
708
709impl Pretty for Span {
710 fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
711 if cx.full_spans {
712 write!(f, "{self:?}")
713 } else {
714 let src_map = cx.tcx().sess.source_map();
715 let lo = src_map.lookup_char_pos(self.lo());
716 let hi = src_map.lookup_char_pos(self.hi());
717 write!(
740 f,
741 "{}:{}: {}:{}",
742 lo.line,
743 lo.col.to_usize() + 1,
744 hi.line,
745 hi.col.to_usize() + 1,
746 )
747 }
748 }
749}
750
751trait FromOpt: Sized {
752 fn from_opt(opt: &config::Value) -> Option<Self>;
753}
754
755impl FromOpt for bool {
756 fn from_opt(opt: &config::Value) -> Option<Self> {
757 opt.as_bool()
758 }
759}
760
761impl FromOpt for KVarArgs {
762 fn from_opt(opt: &config::Value) -> Option<Self> {
763 match opt.as_str() {
764 Some("self") => Some(KVarArgs::SelfOnly),
765 Some("hide") => Some(KVarArgs::Hide),
766 Some("all") => Some(KVarArgs::All),
767 _ => None,
768 }
769 }
770}
771
772#[derive(Serialize, Debug)]
775pub struct NestedString {
776 pub text: String,
777 pub key: Option<String>,
778 pub children: Option<Vec<NestedString>>,
779}
780
781pub fn debug_nested<T: Pretty>(cx: &PrettyCx, t: &T) -> Result<NestedString, fmt::Error> {
782 let t = WithCx::new(cx, t);
783 let text = format!("{t:?}");
784 Ok(NestedString { text, children: None, key: None })
785}
786
787pub fn float_children(children: Vec<Option<Vec<NestedString>>>) -> Option<Vec<NestedString>> {
788 let mut childrens: Vec<_> = children.into_iter().flatten().collect();
789 if childrens.is_empty() {
790 None
791 } else if childrens.len() == 1 {
792 let c = childrens.pop().unwrap();
793 Some(c)
794 } else {
795 let mut res = vec![];
796 for (i, children) in childrens.into_iter().enumerate() {
797 res.push(NestedString { text: format!("arg{i}"), children: Some(children), key: None });
798 }
799 Some(res)
800 }
801}
802
803pub trait PrettyNested {
804 fn fmt_nested(&self, cx: &PrettyCx) -> Result<NestedString, fmt::Error>;
805
806 fn nested_string(&self, cx: &PrettyCx) -> String {
807 let res = self.fmt_nested(cx).unwrap();
808 serde_json::to_string(&res).unwrap()
809 }
810}