1#![allow(clippy::too_many_arguments)]
2
3use alloc::{
4 borrow::{Cow, ToOwned},
5 string::{String, ToString},
6};
7use core::ops::DerefMut;
8
9use derive_more::{Deref, DerefMut as DeriveDerefMut, From};
10use egui::{Align, Checkbox, Color32, Id, Layout, Response, TextEdit, Ui, UiBuilder, WidgetText};
11use facet::{Def, Facet, ListDef, MapDef, OptionDef, ScalarType, SetDef, Type, UserType};
12use facet_reflect::{
13 HasFields, Partial, Peek, PeekEnum, PeekListLike, PeekMap, PeekOption, PeekPointer, PeekSet,
14 PeekStruct, PeekTuple, Poke, PokeEnum, PokeList, PokeStruct,
15};
16
17use crate::{
18 MaybeMut,
19 layout::{ProbeHeader, ProbeLayout, swap_probe_header_state},
20 maybe_mut::{Guard, MakeLockErrorKind},
21};
22
23fn has_egui_skip(attributes: &[facet::Attr]) -> bool {
25 attributes
26 .iter()
27 .any(|a| matches!((a.ns, a.key), (Some("egui"), "skip")))
28}
29
30fn has_egui_as_display(attributes: &[facet::Attr]) -> bool {
32 attributes
33 .iter()
34 .any(|a| matches!((a.ns, a.key), (Some("egui"), "as_display")))
35}
36
37fn egui_rename(attributes: &[facet::Attr]) -> Option<&'static str> {
39 attributes.iter().find_map(|a| {
40 if matches!((a.ns, a.key), (Some("egui"), "rename")) {
41 a.get_as::<&'static str>().copied()
42 } else {
43 None
44 }
45 })
46}
47
48fn field_display_name(field: &facet::Field) -> String {
51 egui_rename(field.attributes)
52 .unwrap_or_else(|| field.effective_name())
53 .to_owned()
54}
55
56fn shape_display_name(shape: &facet::Shape) -> &str {
59 egui_rename(shape.attributes).unwrap_or_else(|| shape.effective_name())
60}
61
62fn text_line_count(s: &str) -> usize {
63 1 + s.chars().filter(|&c| c == '\n').count()
64}
65
66fn shift_enter_pressed(ui: &Ui) -> bool {
67 ui.input(|i| {
68 i.events.iter().any(|event| {
69 matches!(
70 event,
71 egui::Event::Key {
72 key: egui::Key::Enter,
73 pressed: true,
74 modifiers,
75 ..
76 } if modifiers.shift
77 )
78 })
79 })
80}
81
82fn should_render_as_display(shape: &facet::Shape, attributes: &[facet::Attr]) -> bool {
83 has_egui_as_display(attributes) || has_egui_as_display(shape.attributes)
84}
85
86#[must_use = "use [`FacetProbe::show`] to display the probe in the [`Ui`]"]
89#[derive(Deref, DeriveDerefMut)]
90pub struct FacetProbe<'mem, 'facet> {
91 header: Option<WidgetText>,
92 id: Option<Id>,
93 read_only: bool,
94 expand_all: bool,
95 force_reborrow: bool,
102 #[deref]
103 #[deref_mut]
104 inner: MaybeMut<'mem, 'facet>,
105}
106
107#[derive(Debug, From)]
108pub enum MaybeMutT<'mem, T> {
109 Not(&'mem T),
110 Mut(&'mem mut T),
111}
112
113impl<'mem, 'facet> FacetProbe<'mem, 'facet> {
114 pub fn readonly(self, readonly: bool) -> Self {
115 Self {
116 read_only: readonly,
117 ..self
118 }
119 }
120
121 pub fn expand_all(self, expand_all: bool) -> Self {
122 Self {
123 expand_all,
124 ..self
125 }
126 }
127
128 pub fn with_header(mut self, label: impl Into<WidgetText>) -> Self {
129 self.header = Some(label.into());
130 self
131 }
132
133 pub fn with_id_source(mut self, id_source: impl core::hash::Hash) -> Self {
138 self.id = Some(Id::new(id_source));
139 self
140 }
141
142 pub unsafe fn force_reborrow(self) -> Self {
151 Self {
152 force_reborrow: true,
153 ..self
154 }
155 }
156
157 pub fn new_peek(value: Peek<'mem, 'facet>) -> Self {
158 Self {
159 header: None,
160 id: None,
161 read_only: true,
162 expand_all: false,
163 force_reborrow: false,
164 inner: MaybeMut::Not(value),
165 }
166 }
167
168 pub fn new_poke(value: Poke<'mem, 'facet>) -> Self {
169 Self {
170 header: None,
171 id: None,
172 read_only: false,
173 expand_all: false,
174 force_reborrow: false,
175 inner: MaybeMut::Mut(value),
176 }
177 }
178
179 pub fn new<T>(value: impl Into<MaybeMutT<'mem, T>>) -> Self
180 where
181 T: Facet<'facet> + 'mem,
182 {
183 let v: MaybeMutT<'mem, T> = value.into();
184 let inner: MaybeMut = match v {
185 MaybeMutT::Mut(v) => Poke::new(v).into(),
186 MaybeMutT::Not(v) => Peek::new(v).into(),
187 };
188 Self {
189 header: None,
190 id: None,
191 read_only: false,
192 expand_all: false,
193 force_reborrow: false,
194 inner,
195 }
196 }
197
198 pub fn show<'lock>(self, ui: &mut Ui) -> Response
199 where
200 'mem: 'lock,
201 {
202 if has_egui_skip(self.shape().attributes) {
204 return ui.label("");
205 }
206
207 let mut changed = false;
208
209 let shape_attrs = self.shape().attributes;
210 let readonly = shape_attrs
211 .iter()
212 .any(|a| matches!((a.ns, a.key), (Some("egui"), "readonly")))
213 || self.read_only;
214
215 let expand_all = self.expand_all
217 || shape_attrs
218 .iter()
219 .any(|a| matches!((a.ns, a.key), (Some("egui"), "expand_all")));
220
221 let mut guard: Guard<'lock, 'facet> = if readonly {
222 let Ok(read) = self.inner.read() else {
223 return ui.colored_label(Color32::RED, "Read Failure");
224 };
225 read
226 } else {
227 match self.inner.write() {
228 Ok(write) => write,
229 Err(e) if matches!(e.kind, MakeLockErrorKind::NotLockable) => {
231 let Ok(read) = MaybeMut::Not(e.unchanged).read() else {
232 return ui.colored_label(Color32::RED, "Fallback Read Failure");
233 };
234 read
235 }
236 Err(e) if matches!(e.kind, MakeLockErrorKind::LockFailure) => {
237 return ui.colored_label(Color32::RED, "Lock Failure");
238 }
239 Err(e) => {
240 return ui.colored_label(Color32::RED, alloc::format!("Error: {e}"));
241 }
242 }
243 };
244
245 let maybe_mut = guard.deref_mut();
246 let ptr_salt = maybe_mut.as_peek().data().as_byte_ptr() as usize;
249 let probe_id = self
250 .id
251 .unwrap_or_else(|| Id::new(("facet_egui::probe", ptr_salt)));
252 let mut r = ui
253 .push_id(probe_id, |ui| {
254 let child_ui = &mut ui.new_child(
255 UiBuilder::new()
256 .max_rect(ui.max_rect())
257 .layout(Layout::top_down(Align::Min)),
258 );
259
260 let mut layout = ProbeLayout::load(child_ui.ctx(), probe_id.with("layout"));
261 let root_as_display = should_render_as_display(maybe_mut.shape(), &[]);
262
263 if let Some(label) = self.header {
264 let mut header = show_header(
266 label,
267 maybe_mut,
268 &mut layout,
269 0,
270 child_ui,
271 probe_id.with("root"),
272 &mut changed,
273 self.force_reborrow,
274 expand_all,
275 root_as_display,
276 );
277
278 if header.openness > 0.0 && !root_as_display {
279 show_body(
280 maybe_mut,
281 &mut header,
282 &mut layout,
283 0,
284 child_ui,
285 probe_id.with("root"),
286 &mut changed,
287 self.force_reborrow,
288 expand_all,
289 root_as_display,
290 );
291 }
292
293 header.store(child_ui.ctx());
294 } else {
295 show_body_direct(
297 maybe_mut,
298 &mut layout,
299 0,
300 child_ui,
301 probe_id.with("root"),
302 &mut changed,
303 self.force_reborrow,
304 expand_all,
305 root_as_display,
306 );
307 }
308
309 layout.store(child_ui.ctx());
310
311 let final_rect = child_ui.min_rect();
312 ui.advance_cursor_after_rect(final_rect);
313 })
314 .response;
315
316 drop(guard);
317
318 if changed {
319 r.mark_changed();
320 ui.ctx().request_repaint();
321 }
322
323 r
324 }
325}
326
327fn has_inner(value: &MaybeMut<'_, '_>) -> bool {
334 let peek = value.as_peek();
335 if let Ok(opt) = peek.into_option()
339 && let Some(inner) = opt.value()
340 {
341 return has_inner(&MaybeMut::Not(inner));
342 }
343 if let Ok(s) = peek.into_struct() {
344 return s.field_count() > 0;
345 }
346 if let Ok(e) = peek.into_enum()
347 && let Ok(v) = e.active_variant()
348 {
349 return !v.data.fields.is_empty();
350 }
351 if let Ok(l) = peek.into_list_like() {
352 return !l.is_empty();
353 }
354 if let Ok(m) = peek.into_map() {
355 return !m.is_empty();
356 }
357 if let Ok(t) = peek.into_tuple() {
358 return !t.is_empty();
359 }
360 if let Ok(p) = peek.into_pointer()
361 && let Some(inner) = p.borrow_inner()
362 {
363 return has_inner(&MaybeMut::Not(inner));
364 }
365 false
366}
367
368#[expect(clippy::too_many_arguments)]
371fn show_header(
372 label: impl Into<WidgetText>,
373 value: &mut MaybeMut<'_, '_>,
374 layout: &mut ProbeLayout,
375 indent: usize,
376 ui: &mut Ui,
377 id: Id,
378 changed: &mut bool,
379 force_reborrow: bool,
380 expand_all: bool,
381 as_display: bool,
382) -> ProbeHeader {
383 show_header_with_prefix(
384 |_| {},
385 true,
386 label,
387 value,
388 layout,
389 indent,
390 ui,
391 id,
392 changed,
393 force_reborrow,
394 expand_all,
395 as_display,
396 )
397}
398
399#[expect(clippy::too_many_arguments)]
402fn show_header_with_prefix(
403 prefix: impl FnOnce(&mut Ui),
404 animate: bool,
405 label: impl Into<WidgetText>,
406 value: &mut MaybeMut<'_, '_>,
407 layout: &mut ProbeLayout,
408 indent: usize,
409 ui: &mut Ui,
410 id: Id,
411 changed: &mut bool,
412 force_reborrow: bool,
413 expand_all: bool,
414 as_display: bool,
415) -> ProbeHeader {
416 let mut header = if animate {
417 ProbeHeader::load(ui.ctx(), id)
418 } else {
419 ProbeHeader::load_no_animation(ui.ctx(), id)
420 };
421 let row_has_inner = !as_display && has_inner(value);
422 header.set_has_inner(row_has_inner);
423 if !row_has_inner {
424 header.set_open(false);
425 }
426
427 if expand_all && row_has_inner {
428 header.set_open(true);
429 }
430
431 ui.horizontal(|ui| {
432 let label_response = layout.inner_label_ui(indent, id.with("label"), ui, |ui| {
433 prefix(ui);
434 if header.has_inner() {
435 header.collapse_button(ui);
436 }
437 ui.label(label)
438 });
439
440 layout.inner_value_ui(id.with("value"), ui, |ui| {
441 *changed |= show_inline_value(value, ui, id, force_reborrow, as_display)
442 .labelled_by(label_response.id)
443 .changed();
444 });
445 });
446
447 header
448}
449
450#[expect(clippy::too_many_arguments)]
452fn show_body(
453 value: &mut MaybeMut<'_, '_>,
454 header: &mut ProbeHeader,
455 layout: &mut ProbeLayout,
456 indent: usize,
457 ui: &mut Ui,
458 id: Id,
459 changed: &mut bool,
460 force_reborrow: bool,
461 expand_all: bool,
462 as_display: bool,
463) {
464 if as_display {
465 header.set_has_inner(false);
466 header.set_open(false);
467 return;
468 }
469
470 let cursor = ui.cursor();
471 let table_rect = egui::Rect::from_min_max(
472 egui::pos2(cursor.min.x, cursor.min.y - header.body_shift()),
473 ui.max_rect().max,
474 );
475
476 let mut table_ui = ui.new_child(
477 UiBuilder::new()
478 .max_rect(table_rect)
479 .layout(Layout::top_down(Align::Min))
480 .id_salt(id.with("body")),
481 );
482 table_ui.set_clip_rect(
483 ui.clip_rect()
484 .intersect(egui::Rect::everything_below(ui.min_rect().max.y)),
485 );
486
487 let got_inner = show_inner_rows(
488 value,
489 layout,
490 indent + 1,
491 id,
492 &mut table_ui,
493 changed,
494 force_reborrow,
495 expand_all,
496 );
497 header.set_has_inner(got_inner);
498
499 let final_table_rect = table_ui.min_rect();
500 ui.advance_cursor_after_rect(final_table_rect);
501 let table_height = ui.cursor().min.y - table_rect.min.y;
502 header.set_body_height(table_height);
503}
504
505fn show_body_direct(
508 value: &mut MaybeMut<'_, '_>,
509 layout: &mut ProbeLayout,
510 indent: usize,
511 ui: &mut Ui,
512 id: Id,
513 changed: &mut bool,
514 force_reborrow: bool,
515 expand_all: bool,
516 as_display: bool,
517) {
518 if as_display {
519 *changed |= show_inline_value(value, ui, id, force_reborrow, true).changed();
520 return;
521 }
522
523 let cursor = ui.cursor();
524 let table_rect =
525 egui::Rect::from_min_max(egui::pos2(cursor.min.x, cursor.min.y), ui.max_rect().max);
526
527 let mut table_ui = ui.new_child(
528 UiBuilder::new()
529 .max_rect(table_rect)
530 .layout(Layout::top_down(Align::Min))
531 .id_salt(id.with("body")),
532 );
533 table_ui.set_clip_rect(
534 ui.clip_rect()
535 .intersect(egui::Rect::everything_below(ui.min_rect().max.y)),
536 );
537
538 show_inner_rows(
539 value,
540 layout,
541 indent + 1,
542 id,
543 &mut table_ui,
544 changed,
545 force_reborrow,
546 expand_all,
547 );
548
549 let final_table_rect = table_ui.min_rect();
550 ui.advance_cursor_after_rect(final_table_rect);
551}
552
553fn show_inner_rows(
556 value: &mut MaybeMut<'_, '_>,
557 layout: &mut ProbeLayout,
558 indent: usize,
559 id: Id,
560 ui: &mut Ui,
561 changed: &mut bool,
562 force_reborrow: bool,
563 expand_all: bool,
564) -> bool {
565 match value {
566 MaybeMut::Mut(poke) => show_inner_rows_poke(
567 poke,
568 layout,
569 indent,
570 id,
571 ui,
572 changed,
573 force_reborrow,
574 expand_all,
575 ),
576 MaybeMut::Not(peek) => show_inner_rows_peek(
577 *peek,
578 layout,
579 indent,
580 id,
581 ui,
582 changed,
583 force_reborrow,
584 expand_all,
585 ),
586 }
587}
588
589fn lock_child<'mem, 'facet>(child: MaybeMut<'mem, 'facet>) -> Option<Guard<'mem, 'facet>> {
594 match child.write() {
595 Ok(guard) => Some(guard),
596 Err(e) if matches!(e.kind, MakeLockErrorKind::NotLockable) => {
597 MaybeMut::Not(e.unchanged).read().ok()
598 }
599 Err(_) => None,
600 }
601}
602
603fn show_inner_rows_poke(
604 poke: &mut Poke<'_, '_>,
605 layout: &mut ProbeLayout,
606 indent: usize,
607 id: Id,
608 ui: &mut Ui,
609 changed: &mut bool,
610 force_reborrow: bool,
611 expand_all: bool,
612) -> bool {
613 if let Def::Option(option_def) = poke.shape().def {
616 return show_inner_rows_poke_option(
617 poke,
618 option_def,
619 layout,
620 indent,
621 id,
622 ui,
623 changed,
624 force_reborrow,
625 expand_all,
626 );
627 }
628
629 if poke.is_enum() {
632 let enu_poke = match poke.try_reborrow() {
633 Some(rb) => rb,
634 None if force_reborrow => unsafe {
635 Poke::from_raw_parts(poke.data_mut(), poke.shape())
636 },
637 None => {
638 return show_inner_rows_peek(
639 poke.as_peek(),
640 layout,
641 indent,
642 id,
643 ui,
644 changed,
645 force_reborrow,
646 expand_all,
647 );
648 }
649 };
650 if let Ok(enu) = enu_poke.into_enum() {
651 return show_inner_rows_poke_enum(
652 enu,
653 layout,
654 indent,
655 id,
656 ui,
657 changed,
658 force_reborrow,
659 expand_all,
660 );
661 }
662 return show_inner_rows_peek(
663 poke.as_peek(),
664 layout,
665 indent,
666 id,
667 ui,
668 changed,
669 force_reborrow,
670 expand_all,
671 );
672 }
673
674 if poke.is_struct() {
676 let reborrow = match poke.try_reborrow() {
677 Some(rb) => rb,
678 None if force_reborrow => unsafe {
679 Poke::from_raw_parts(poke.data_mut(), poke.shape())
680 },
681 None => {
682 return show_inner_rows_peek(
683 poke.as_peek(),
684 layout,
685 indent,
686 id,
687 ui,
688 changed,
689 force_reborrow,
690 expand_all,
691 );
692 }
693 };
694 if let Ok(struc) = reborrow.into_struct() {
695 return show_inner_rows_poke_struct(
696 struc,
697 layout,
698 indent,
699 id,
700 ui,
701 changed,
702 force_reborrow,
703 expand_all,
704 );
705 }
706 }
707
708 if matches!(poke.shape().def, Def::Map(_) | Def::Set(_)) {
712 return show_inner_rows_peek(
713 poke.as_peek(),
714 layout,
715 indent,
716 id,
717 ui,
718 changed,
719 force_reborrow,
720 expand_all,
721 );
722 }
723
724 let data_mut = poke.data_mut();
725 let shape = poke.shape();
726 let poke = match poke.try_reborrow() {
727 Some(rb) => rb,
728 None if force_reborrow => unsafe { Poke::from_raw_parts(poke.data_mut(), poke.shape()) },
729 None => {
730 return show_inner_rows_peek(
731 poke.as_peek(),
732 layout,
733 indent,
734 id,
735 ui,
736 changed,
737 force_reborrow,
738 expand_all,
739 );
740 }
741 };
742 if let Ok(poke_list) = poke.into_list() {
743 show_inner_rows_poke_list(
744 poke_list,
745 layout,
746 indent,
747 id,
748 ui,
749 changed,
750 force_reborrow,
751 expand_all,
752 )
753 } else {
754 let poke = unsafe { Poke::from_raw_parts(data_mut, shape) };
758 show_inner_rows_peek(
760 poke.as_peek(),
761 layout,
762 indent,
763 id,
764 ui,
765 changed,
766 force_reborrow,
767 expand_all,
768 )
769 }
770}
771
772fn show_inner_rows_poke_list(
773 mut list: PokeList<'_, '_>,
774 layout: &mut ProbeLayout,
775 indent: usize,
776 id: Id,
777 ui: &mut Ui,
778 changed: &mut bool,
779 force_reborrow: bool,
780 expand_all: bool,
781) -> bool {
782 let len = list.len();
783 if len == 0 {
784 return false;
785 }
786 let can_swap = list.def().vtable.swap.is_some();
787 let mut pending_swap: Option<(usize, usize)> = None;
790 for idx in 0..len {
791 let label = alloc::format!("[{idx}]");
792 if let Some(field_poke) = list.get_mut(idx) {
793 let row_id = id.with(("list", idx));
794 let Some(mut guard) = lock_child(MaybeMut::Mut(field_poke)) else {
795 continue;
796 };
797 let child = &mut *guard;
798 let as_display = should_render_as_display(child.shape(), &[]);
799 let prefix = |ui: &mut Ui| {
800 if !can_swap {
801 return;
802 }
803 ui.scope(|ui| {
804 ui.spacing_mut().button_padding = egui::vec2(2.0, 0.0);
805 let up = ui
806 .add_enabled(idx > 0, egui::Button::new("⬆").small())
807 .on_hover_text("move up");
808 if up.clicked() {
809 pending_swap = Some((idx, idx - 1));
810 }
811 let down = ui
812 .add_enabled(idx + 1 < len, egui::Button::new("⬇").small())
813 .on_hover_text("move down");
814 if down.clicked() {
815 pending_swap = Some((idx, idx + 1));
816 }
817 });
818 };
819 let mut header = show_header_with_prefix(
820 prefix,
821 false,
822 &label,
823 child,
824 layout,
825 indent,
826 ui,
827 row_id,
828 changed,
829 force_reborrow,
830 expand_all,
831 as_display,
832 );
833 if header.openness > 0.0 && !as_display {
834 show_body(
835 child,
836 &mut header,
837 layout,
838 indent,
839 ui,
840 row_id,
841 changed,
842 force_reborrow,
843 expand_all,
844 as_display,
845 );
846 }
847 header.store(ui.ctx());
848 }
849 }
850 if let Some((a, b)) = pending_swap
851 && list.swap(a, b).is_ok()
852 {
853 swap_probe_header_state(ui.ctx(), id.with(("list", a)), id.with(("list", b)));
857 *changed = true;
858 }
859 true
860}
861
862fn show_inner_rows_poke_struct(
863 mut struc: PokeStruct<'_, '_>,
864 layout: &mut ProbeLayout,
865 indent: usize,
866 id: Id,
867 ui: &mut Ui,
868 changed: &mut bool,
869 force_reborrow: bool,
870 expand_all: bool,
871) -> bool {
872 let count = struc.field_count();
873 if count == 0 {
874 return false;
875 }
876 let mut got_inner = false;
877 for idx in 0..count {
878 let field = &struc.ty().fields[idx];
879 if has_egui_skip(field.attributes) {
880 continue;
881 }
882 if let Ok(field_poke) = struc.field(idx) {
883 let row_id = id.with(("struct", idx));
884 let Some(mut guard) = lock_child(MaybeMut::Mut(field_poke)) else {
885 continue;
886 };
887 let child = &mut *guard;
888 if field.is_flattened() {
889 ui.push_id(row_id, |ui| {
890 got_inner |= show_inner_rows(
891 child,
892 layout,
893 indent,
894 row_id,
895 ui,
896 changed,
897 force_reborrow,
898 expand_all,
899 );
900 });
901 continue;
902 }
903 got_inner = true;
904 let field_name = field_display_name(field);
905 let as_display = should_render_as_display(child.shape(), field.attributes);
906 let mut header = show_header(
907 &field_name,
908 child,
909 layout,
910 indent,
911 ui,
912 row_id,
913 changed,
914 force_reborrow,
915 expand_all,
916 as_display,
917 );
918 if header.openness > 0.0 && !as_display {
919 show_body(
920 child,
921 &mut header,
922 layout,
923 indent,
924 ui,
925 row_id,
926 changed,
927 force_reborrow,
928 expand_all,
929 as_display,
930 );
931 }
932 header.store(ui.ctx());
933 }
934 }
935 got_inner
936}
937
938fn show_inner_rows_poke_enum(
939 mut enu: PokeEnum<'_, '_>,
940 layout: &mut ProbeLayout,
941 indent: usize,
942 id: Id,
943 ui: &mut Ui,
944 changed: &mut bool,
945 force_reborrow: bool,
946 expand_all: bool,
947) -> bool {
948 let variant = match enu.active_variant() {
949 Ok(v) => v,
950 Err(_) => return false,
951 };
952 let field_count = variant.data.fields.len();
953 if field_count == 0 {
954 return false;
955 }
956 let mut got_inner = false;
957 for idx in 0..field_count {
958 let field = &variant.data.fields[idx];
959 if has_egui_skip(field.attributes) {
960 continue;
961 }
962 if let Ok(Some(field_poke)) = enu.field(idx) {
963 let row_id = id.with(("enum", idx));
964 let Some(mut guard) = lock_child(MaybeMut::Mut(field_poke)) else {
965 continue;
966 };
967 let child = &mut *guard;
968 if field.is_flattened() {
969 ui.push_id(row_id, |ui| {
970 got_inner |= show_inner_rows(
971 child,
972 layout,
973 indent,
974 row_id,
975 ui,
976 changed,
977 force_reborrow,
978 expand_all,
979 );
980 });
981 continue;
982 }
983 let field_name = field_display_name(field);
984 let as_display = should_render_as_display(child.shape(), field.attributes);
985 let mut header = show_header(
986 &field_name,
987 child,
988 layout,
989 indent,
990 ui,
991 row_id,
992 changed,
993 force_reborrow,
994 expand_all,
995 as_display,
996 );
997 if header.openness > 0.0 && !as_display {
998 show_body(
999 child,
1000 &mut header,
1001 layout,
1002 indent,
1003 ui,
1004 row_id,
1005 changed,
1006 force_reborrow,
1007 expand_all,
1008 as_display,
1009 );
1010 }
1011 header.store(ui.ctx());
1012 }
1013 }
1014 got_inner
1015}
1016
1017fn show_inner_rows_peek(
1018 peek: Peek<'_, '_>,
1019 layout: &mut ProbeLayout,
1020 indent: usize,
1021 id: Id,
1022 ui: &mut Ui,
1023 changed: &mut bool,
1024 force_reborrow: bool,
1025 expand_all: bool,
1026) -> bool {
1027 if let Ok(opt) = peek.into_option() {
1028 show_inner_rows_peek_option(
1029 opt,
1030 layout,
1031 indent,
1032 id,
1033 ui,
1034 changed,
1035 force_reborrow,
1036 expand_all,
1037 )
1038 } else if let Ok(struc) = peek.into_struct() {
1039 show_inner_rows_peek_struct(
1040 struc,
1041 layout,
1042 indent,
1043 id,
1044 ui,
1045 changed,
1046 force_reborrow,
1047 expand_all,
1048 )
1049 } else if let Ok(enu) = peek.into_enum() {
1050 show_inner_rows_peek_enum(
1051 enu,
1052 layout,
1053 indent,
1054 id,
1055 ui,
1056 changed,
1057 force_reborrow,
1058 expand_all,
1059 )
1060 } else if let Ok(list) = peek.into_list_like() {
1061 show_inner_rows_peek_list(
1062 list,
1063 layout,
1064 indent,
1065 id,
1066 ui,
1067 changed,
1068 force_reborrow,
1069 expand_all,
1070 )
1071 } else if let Ok(map) = peek.into_map() {
1072 show_inner_rows_peek_map(
1073 map,
1074 layout,
1075 indent,
1076 id,
1077 ui,
1078 changed,
1079 force_reborrow,
1080 expand_all,
1081 )
1082 } else if let Ok(set) = peek.into_set() {
1083 show_inner_rows_peek_set(
1084 set,
1085 layout,
1086 indent,
1087 id,
1088 ui,
1089 changed,
1090 force_reborrow,
1091 expand_all,
1092 )
1093 } else if let Ok(tuple) = peek.into_tuple() {
1094 show_inner_rows_peek_tuple(
1095 tuple,
1096 layout,
1097 indent,
1098 id,
1099 ui,
1100 changed,
1101 force_reborrow,
1102 expand_all,
1103 )
1104 } else if let Ok(ptr) = peek.into_pointer() {
1105 show_inner_rows_peek_pointer(
1106 ptr,
1107 layout,
1108 indent,
1109 id,
1110 ui,
1111 changed,
1112 force_reborrow,
1113 expand_all,
1114 )
1115 } else {
1116 false
1117 }
1118}
1119
1120fn show_inner_rows_peek_struct(
1121 struc: PeekStruct<'_, '_>,
1122 layout: &mut ProbeLayout,
1123 indent: usize,
1124 id: Id,
1125 ui: &mut Ui,
1126 changed: &mut bool,
1127 force_reborrow: bool,
1128 expand_all: bool,
1129) -> bool {
1130 let mut got_inner = false;
1131 for (idx, (field, value)) in struc.fields().enumerate() {
1132 let row_id = id.with(("struct", idx));
1133 if has_egui_skip(field.attributes) {
1134 continue;
1135 }
1136 let Some(mut guard) = lock_child(MaybeMut::Not(value)) else {
1137 continue;
1138 };
1139 let child = &mut *guard;
1140 if field.is_flattened() {
1141 ui.push_id(row_id, |ui| {
1142 got_inner |= show_inner_rows(
1143 child,
1144 layout,
1145 indent,
1146 row_id,
1147 ui,
1148 changed,
1149 force_reborrow,
1150 expand_all,
1151 );
1152 });
1153 continue;
1154 }
1155 got_inner = true;
1156 let field_name = field_display_name(&field);
1157 let as_display = should_render_as_display(child.shape(), field.attributes);
1158 let mut header = show_header(
1159 &field_name,
1160 child,
1161 layout,
1162 indent,
1163 ui,
1164 row_id,
1165 changed,
1166 force_reborrow,
1167 expand_all,
1168 as_display,
1169 );
1170 if header.openness > 0.0 && !as_display {
1171 show_body(
1172 child,
1173 &mut header,
1174 layout,
1175 indent,
1176 ui,
1177 row_id,
1178 changed,
1179 force_reborrow,
1180 expand_all,
1181 as_display,
1182 );
1183 }
1184 header.store(ui.ctx());
1185 }
1186 got_inner
1187}
1188
1189fn show_inner_rows_peek_enum(
1190 enu: PeekEnum<'_, '_>,
1191 layout: &mut ProbeLayout,
1192 indent: usize,
1193 id: Id,
1194 ui: &mut Ui,
1195 changed: &mut bool,
1196 force_reborrow: bool,
1197 expand_all: bool,
1198) -> bool {
1199 let mut got_inner = false;
1200 for (idx, (field, value)) in enu.fields().enumerate() {
1201 let row_id = id.with(("enum", idx));
1202 if has_egui_skip(field.attributes) {
1203 continue;
1204 }
1205 let Some(mut guard) = lock_child(MaybeMut::Not(value)) else {
1206 continue;
1207 };
1208 let child = &mut *guard;
1209 if field.is_flattened() {
1210 ui.push_id(row_id, |ui| {
1211 got_inner |= show_inner_rows(
1212 child,
1213 layout,
1214 indent,
1215 row_id,
1216 ui,
1217 changed,
1218 force_reborrow,
1219 expand_all,
1220 );
1221 });
1222 continue;
1223 }
1224 got_inner = true;
1225 let field_name = field_display_name(&field);
1226 let as_display = should_render_as_display(child.shape(), field.attributes);
1227 let mut header = show_header(
1228 &field_name,
1229 child,
1230 layout,
1231 indent,
1232 ui,
1233 row_id,
1234 changed,
1235 force_reborrow,
1236 expand_all,
1237 as_display,
1238 );
1239 if header.openness > 0.0 && !as_display {
1240 show_body(
1241 child,
1242 &mut header,
1243 layout,
1244 indent,
1245 ui,
1246 row_id,
1247 changed,
1248 force_reborrow,
1249 expand_all,
1250 as_display,
1251 );
1252 }
1253 header.store(ui.ctx());
1254 }
1255 got_inner
1256}
1257
1258fn show_inner_rows_peek_list(
1259 list: PeekListLike<'_, '_>,
1260 layout: &mut ProbeLayout,
1261 indent: usize,
1262 id: Id,
1263 ui: &mut Ui,
1264 changed: &mut bool,
1265 force_reborrow: bool,
1266 expand_all: bool,
1267) -> bool {
1268 let mut got_inner = false;
1269 for (idx, item) in list.iter().enumerate() {
1270 let row_id = id.with(("list", idx));
1271 got_inner = true;
1272 let label = alloc::format!("[{idx}]");
1273 let Some(mut guard) = lock_child(MaybeMut::Not(item)) else {
1274 continue;
1275 };
1276 let child = &mut *guard;
1277 let as_display = should_render_as_display(child.shape(), &[]);
1278 let mut header = show_header(
1279 &label,
1280 child,
1281 layout,
1282 indent,
1283 ui,
1284 row_id,
1285 changed,
1286 force_reborrow,
1287 expand_all,
1288 as_display,
1289 );
1290 if header.openness > 0.0 && !as_display {
1291 show_body(
1292 child,
1293 &mut header,
1294 layout,
1295 indent,
1296 ui,
1297 row_id,
1298 changed,
1299 force_reborrow,
1300 expand_all,
1301 as_display,
1302 );
1303 }
1304 header.store(ui.ctx());
1305 }
1306 got_inner
1307}
1308
1309fn show_inner_rows_peek_map(
1310 map: PeekMap<'_, '_>,
1311 layout: &mut ProbeLayout,
1312 indent: usize,
1313 id: Id,
1314 ui: &mut Ui,
1315 changed: &mut bool,
1316 force_reborrow: bool,
1317 expand_all: bool,
1318) -> bool {
1319 let mut got_inner = false;
1320 for (idx, (key, value)) in map.iter().enumerate() {
1321 let row_id = id.with(("map", idx));
1322 got_inner = true;
1323 let label = alloc::format!("{}", key);
1324 let Some(mut guard) = lock_child(MaybeMut::Not(value)) else {
1325 continue;
1326 };
1327 let child = &mut *guard;
1328 let as_display = should_render_as_display(child.shape(), &[]);
1329 let mut header = show_header(
1330 &label,
1331 child,
1332 layout,
1333 indent,
1334 ui,
1335 row_id,
1336 changed,
1337 force_reborrow,
1338 expand_all,
1339 as_display,
1340 );
1341 if header.openness > 0.0 && !as_display {
1342 show_body(
1343 child,
1344 &mut header,
1345 layout,
1346 indent,
1347 ui,
1348 row_id,
1349 changed,
1350 force_reborrow,
1351 expand_all,
1352 as_display,
1353 );
1354 }
1355 header.store(ui.ctx());
1356 }
1357 got_inner
1358}
1359
1360fn show_inner_rows_peek_set(
1361 set: PeekSet<'_, '_>,
1362 layout: &mut ProbeLayout,
1363 indent: usize,
1364 id: Id,
1365 ui: &mut Ui,
1366 changed: &mut bool,
1367 force_reborrow: bool,
1368 expand_all: bool,
1369) -> bool {
1370 let mut got_inner = false;
1371 for (idx, value) in set.iter().enumerate() {
1372 let row_id = id.with(("set", idx));
1373 got_inner = true;
1374 let label = alloc::format!("[{idx}]");
1375 let Some(mut guard) = lock_child(MaybeMut::Not(value)) else {
1376 continue;
1377 };
1378 let child = &mut *guard;
1379 let as_display = should_render_as_display(child.shape(), &[]);
1380 let mut header = show_header(
1381 &label,
1382 child,
1383 layout,
1384 indent,
1385 ui,
1386 row_id,
1387 changed,
1388 force_reborrow,
1389 expand_all,
1390 as_display,
1391 );
1392 if header.openness > 0.0 && !as_display {
1393 show_body(
1394 child,
1395 &mut header,
1396 layout,
1397 indent,
1398 ui,
1399 row_id,
1400 changed,
1401 force_reborrow,
1402 expand_all,
1403 as_display,
1404 );
1405 }
1406 header.store(ui.ctx());
1407 }
1408 got_inner
1409}
1410
1411fn show_inner_rows_peek_option(
1412 opt: PeekOption<'_, '_>,
1413 layout: &mut ProbeLayout,
1414 indent: usize,
1415 id: Id,
1416 ui: &mut Ui,
1417 changed: &mut bool,
1418 force_reborrow: bool,
1419 expand_all: bool,
1420) -> bool {
1421 if let Some(inner) = opt.value() {
1422 let Some(mut guard) = lock_child(MaybeMut::Not(inner)) else {
1423 return false;
1424 };
1425 let child = &mut *guard;
1426 return show_inner_rows(
1427 child,
1428 layout,
1429 indent,
1430 id.with("option"),
1431 ui,
1432 changed,
1433 force_reborrow,
1434 expand_all,
1435 );
1436 }
1437 false
1438}
1439
1440fn show_inner_rows_peek_tuple(
1441 tuple: PeekTuple<'_, '_>,
1442 layout: &mut ProbeLayout,
1443 indent: usize,
1444 id: Id,
1445 ui: &mut Ui,
1446 changed: &mut bool,
1447 force_reborrow: bool,
1448 expand_all: bool,
1449) -> bool {
1450 let mut got_inner = false;
1451 for (idx, (_field, value)) in tuple.fields().enumerate() {
1452 let row_id = id.with(("tuple", idx));
1453 got_inner = true;
1454 let label = alloc::format!("[{idx}]");
1455 let Some(mut guard) = lock_child(MaybeMut::Not(value)) else {
1456 continue;
1457 };
1458 let child = &mut *guard;
1459 let as_display = should_render_as_display(child.shape(), &[]);
1460 let mut header = show_header(
1461 &label,
1462 child,
1463 layout,
1464 indent,
1465 ui,
1466 row_id,
1467 changed,
1468 force_reborrow,
1469 expand_all,
1470 as_display,
1471 );
1472 if header.openness > 0.0 && !as_display {
1473 show_body(
1474 child,
1475 &mut header,
1476 layout,
1477 indent,
1478 ui,
1479 row_id,
1480 changed,
1481 force_reborrow,
1482 expand_all,
1483 as_display,
1484 );
1485 }
1486 header.store(ui.ctx());
1487 }
1488 got_inner
1489}
1490
1491fn show_inner_rows_peek_pointer(
1492 ptr: PeekPointer<'_, '_>,
1493 layout: &mut ProbeLayout,
1494 indent: usize,
1495 id: Id,
1496 ui: &mut Ui,
1497 changed: &mut bool,
1498 force_reborrow: bool,
1499 expand_all: bool,
1500) -> bool {
1501 if let Some(inner) = ptr.borrow_inner() {
1502 let Some(mut guard) = lock_child(MaybeMut::Not(inner)) else {
1503 return false;
1504 };
1505 let child = &mut *guard;
1506 return show_inner_rows(
1507 child,
1508 layout,
1509 indent,
1510 id.with("ptr"),
1511 ui,
1512 changed,
1513 force_reborrow,
1514 expand_all,
1515 );
1516 }
1517 false
1518}
1519
1520fn show_inline_value(
1526 value: &mut MaybeMut<'_, '_>,
1527 ui: &mut Ui,
1528 id: Id,
1529 force_reborrow: bool,
1530 as_display: bool,
1531) -> Response {
1532 match value {
1533 MaybeMut::Mut(poke) => show_inline_poke(poke, ui, id, force_reborrow, as_display),
1534 MaybeMut::Not(peek) => show_inline_peek(*peek, ui, id, as_display),
1535 }
1536}
1537
1538fn show_inline_poke(
1540 poke: &mut Poke<'_, '_>,
1541 ui: &mut Ui,
1542 id: Id,
1543 force_reborrow: bool,
1544 as_display: bool,
1545) -> Response {
1546 if as_display || should_render_as_display(poke.shape(), &[]) {
1547 return ui.label(alloc::format!("{}", poke.as_peek()));
1548 }
1549
1550 if let Some(scalar_type) = poke.as_peek().scalar_type() {
1551 return show_inline_poke_scalar(poke, scalar_type, ui);
1552 }
1553
1554 if let Def::Option(option_def) = poke.shape().def {
1558 return show_inline_poke_option(poke, option_def, ui, id, force_reborrow);
1559 }
1560 if poke.is_enum() {
1561 return show_inline_poke_enum(poke, ui, id);
1562 }
1563 if poke.is_struct() {
1564 return ui.weak(shape_display_name(poke.shape()));
1565 }
1566 if let Def::List(list_def) = poke.shape().def {
1567 return show_inline_poke_list(poke, list_def, ui);
1568 }
1569 if let Def::Map(map_def) = poke.shape().def {
1570 return show_inline_poke_map(poke, map_def, ui);
1571 }
1572 if let Def::Set(set_def) = poke.shape().def {
1573 return show_inline_poke_set(poke, set_def, ui);
1574 }
1575 if let Ok(tuple) = poke.as_peek().into_tuple() {
1576 return ui.weak(alloc::format!("({})", tuple.len()));
1577 }
1578 if let Ok(ptr) = poke.as_peek().into_pointer()
1579 && let Some(inner) = ptr.borrow_inner()
1580 {
1581 return show_inline_peek(inner, ui, id, false);
1582 }
1583
1584 ui.weak(shape_display_name(poke.shape()))
1585}
1586
1587fn show_inline_poke_list(poke: &mut Poke<'_, '_>, list_def: ListDef, ui: &mut Ui) -> Response {
1589 let len = poke
1590 .as_peek()
1591 .into_list_like()
1592 .map(|l| l.len())
1593 .unwrap_or(0);
1594 let item_shape = list_def.t();
1595 let has_default = item_shape.is_default();
1596 let has_push = list_def.push().is_some();
1597 let has_pop = list_def.pop().is_some();
1598
1599 let mut changed = false;
1600 let r = ui.horizontal(|ui| {
1601 ui.weak(alloc::format!("[{len}]"));
1602
1603 if has_push && has_default && ui.small_button("+").clicked() {
1604 changed |= try_push_default_to_list(poke, list_def);
1605 }
1606
1607 if has_pop && len > 0 && ui.small_button("-").clicked() {
1608 changed |= try_pop_from_list(poke);
1609 }
1610 });
1611
1612 let mut r = r.response;
1613 if changed {
1614 r.mark_changed();
1615 }
1616 r
1617}
1618
1619fn try_push_default_to_list(poke: &mut Poke<'_, '_>, list_def: ListDef) -> bool {
1621 let item_shape = list_def.t();
1622
1623 let partial = match unsafe { Partial::alloc_shape(item_shape) } {
1625 Ok(p) => p,
1626 Err(_) => return false,
1627 };
1628 let partial = match partial.set_default() {
1629 Ok(p) => p,
1630 Err(_) => return false,
1631 };
1632 let heap_value = match partial.build() {
1633 Ok(v) => v,
1634 Err(_) => return false,
1635 };
1636
1637 let Some(reborrow) = poke.try_reborrow() else {
1638 return false;
1639 };
1640 let Ok(mut list) = reborrow.into_list() else {
1641 return false;
1642 };
1643 list.push_from_heap(heap_value).is_ok()
1644}
1645
1646fn try_pop_from_list(poke: &mut Poke<'_, '_>) -> bool {
1649 let Some(reborrow) = poke.try_reborrow() else {
1650 return false;
1651 };
1652 let Ok(mut list) = reborrow.into_list() else {
1653 return false;
1654 };
1655 matches!(list.pop(), Ok(Some(_)))
1656}
1657
1658fn show_inline_poke_map(poke: &mut Poke<'_, '_>, map_def: MapDef, ui: &mut Ui) -> Response {
1662 let len = poke.as_peek().into_map().map(|m| m.len()).unwrap_or(0);
1663 let key_shape = map_def.k();
1664 let value_shape = map_def.v();
1665 let can_insert = key_shape.is_default() && value_shape.is_default();
1666
1667 let mut changed = false;
1668 let r = ui.horizontal(|ui| {
1669 ui.weak(alloc::format!("[{len}]"));
1670
1671 if can_insert
1672 && ui
1673 .small_button("+")
1674 .on_hover_text("insert default key/value")
1675 .clicked()
1676 {
1677 changed |= try_insert_default_into_map(poke, map_def);
1678 }
1679 });
1680
1681 let mut r = r.response;
1682 if changed {
1683 r.mark_changed();
1684 }
1685 r
1686}
1687
1688fn try_insert_default_into_map(poke: &mut Poke<'_, '_>, map_def: MapDef) -> bool {
1691 let key = match build_default_heap_value(map_def.k()) {
1692 Some(v) => v,
1693 None => return false,
1694 };
1695 let value = match build_default_heap_value(map_def.v()) {
1696 Some(v) => v,
1697 None => return false,
1698 };
1699
1700 let Some(reborrow) = poke.try_reborrow() else {
1701 return false;
1702 };
1703 let Ok(mut map) = reborrow.into_map() else {
1704 return false;
1705 };
1706 map.insert_from_heap(key, value).is_ok()
1707}
1708
1709fn show_inline_poke_set(poke: &mut Poke<'_, '_>, set_def: SetDef, ui: &mut Ui) -> Response {
1713 let len = poke.as_peek().into_set().map(|s| s.len()).unwrap_or(0);
1714 let elem_shape = set_def.t();
1715 let can_insert = elem_shape.is_default();
1716
1717 let mut changed = false;
1718 let r = ui.horizontal(|ui| {
1719 ui.weak(alloc::format!("[{len}]"));
1720
1721 if can_insert
1722 && ui
1723 .small_button("+")
1724 .on_hover_text("insert default value")
1725 .clicked()
1726 {
1727 changed |= try_insert_default_into_set(poke, set_def);
1728 }
1729 });
1730
1731 let mut r = r.response;
1732 if changed {
1733 r.mark_changed();
1734 }
1735 r
1736}
1737
1738fn try_insert_default_into_set(poke: &mut Poke<'_, '_>, set_def: SetDef) -> bool {
1741 let value = match build_default_heap_value(set_def.t()) {
1742 Some(v) => v,
1743 None => return false,
1744 };
1745
1746 let Some(reborrow) = poke.try_reborrow() else {
1747 return false;
1748 };
1749 let Ok(mut set) = reborrow.into_set() else {
1750 return false;
1751 };
1752 set.insert_from_heap(value).is_ok()
1753}
1754
1755fn build_default_heap_value(
1758 shape: &'static facet::Shape,
1759) -> Option<facet_reflect::HeapValue<'static, true>> {
1760 let partial = unsafe { Partial::alloc_shape(shape) }.ok()?;
1762 let partial = partial.set_default().ok()?;
1763 partial.build().ok()
1764}
1765
1766fn show_inline_poke_option(
1768 poke: &mut Poke<'_, '_>,
1769 option_def: OptionDef,
1770 ui: &mut Ui,
1771 id: Id,
1772 force_reborrow: bool,
1773) -> Response {
1774 let is_some = unsafe { (option_def.vtable.is_some)(poke.data_mut().as_const()) };
1775
1776 let mut changed = false;
1777 let r = ui.horizontal(|ui| {
1778 if ui.selectable_label(!is_some, "None").clicked()
1779 && is_some
1780 && let Some(reborrow) = poke.try_reborrow()
1781 && let Ok(mut opt) = reborrow.into_option()
1782 {
1783 opt.set_none();
1784 changed = true;
1785 }
1786 if ui.selectable_label(is_some, "Some").clicked() && !is_some {
1787 changed = try_set_option_to_some_default(poke, option_def);
1789 }
1790 if is_some {
1791 let inner_ptr = unsafe { (option_def.vtable.get_value)(poke.data_mut().as_const()) };
1792 if !inner_ptr.is_null() {
1793 let mut inner_poke = unsafe {
1796 Poke::from_raw_parts(facet::PtrMut::new(inner_ptr as *mut u8), option_def.t())
1797 };
1798 show_inline_poke(&mut inner_poke, ui, id.with("some"), force_reborrow, false);
1799 }
1800 }
1801 });
1802
1803 let mut r = r.response;
1804 if changed {
1805 r.mark_changed();
1806 }
1807 r
1808}
1809
1810fn try_set_option_to_some_default(poke: &mut Poke<'_, '_>, option_def: OptionDef) -> bool {
1812 let inner_shape = option_def.t();
1813
1814 let partial = match unsafe { Partial::alloc_shape(inner_shape) } {
1817 Ok(p) => p,
1818 Err(_) => return false,
1819 };
1820 let partial = match partial.set_default() {
1821 Ok(p) => p,
1822 Err(_) => return false,
1823 };
1824 let heap_value = match partial.build() {
1825 Ok(v) => v,
1826 Err(_) => return false,
1827 };
1828
1829 let Some(reborrow) = poke.try_reborrow() else {
1830 return false;
1831 };
1832 let Ok(mut opt) = reborrow.into_option() else {
1833 return false;
1834 };
1835 opt.set_some_from_heap(heap_value).is_ok()
1836}
1837
1838fn show_inner_rows_poke_option(
1840 poke: &mut Poke<'_, '_>,
1841 option_def: OptionDef,
1842 layout: &mut ProbeLayout,
1843 indent: usize,
1844 id: Id,
1845 ui: &mut Ui,
1846 changed: &mut bool,
1847 force_reborrow: bool,
1848 expand_all: bool,
1849) -> bool {
1850 let is_some = unsafe { (option_def.vtable.is_some)(poke.data_mut().as_const()) };
1851 if !is_some {
1852 return false;
1853 }
1854
1855 let inner_ptr = unsafe { (option_def.vtable.get_value)(poke.data_mut().as_const()) };
1856 if inner_ptr.is_null() {
1857 return false;
1858 }
1859
1860 let inner_poke =
1863 unsafe { Poke::from_raw_parts(facet::PtrMut::new(inner_ptr as *mut u8), option_def.t()) };
1864
1865 let mut child = MaybeMut::Mut(inner_poke);
1866 show_inner_rows(
1867 &mut child,
1868 layout,
1869 indent,
1870 id.with("option"),
1871 ui,
1872 changed,
1873 force_reborrow,
1874 expand_all,
1875 )
1876}
1877
1878fn show_inline_poke_enum(poke: &mut Poke<'_, '_>, ui: &mut Ui, id: Id) -> Response {
1880 let shape = poke.shape();
1881 let Type::User(UserType::Enum(enum_type)) = shape.ty else {
1882 return ui.weak("enum");
1883 };
1884
1885 let active_name = poke
1887 .as_peek()
1888 .into_enum()
1889 .ok()
1890 .and_then(|e| e.active_variant().ok())
1891 .map(|v| v.effective_name())
1892 .unwrap_or("?");
1893
1894 let mut changed = false;
1895 let r = egui::ComboBox::from_id_salt(id)
1896 .selected_text(active_name)
1897 .show_ui(ui, |ui| {
1898 for (idx, variant) in enum_type.variants.iter().enumerate() {
1899 let variant_name: &str = variant.effective_name();
1900 let is_active = variant_name == active_name;
1901 if ui.selectable_label(is_active, variant_name).clicked()
1902 && !is_active
1903 && try_change_variant(poke, idx)
1904 {
1905 changed = true;
1906 }
1907 }
1908 });
1909
1910 let mut r = r.response;
1911 if changed {
1912 r.mark_changed();
1913 }
1914 r
1915}
1916
1917fn try_change_variant(poke: &mut Poke<'_, '_>, variant_idx: usize) -> bool {
1921 let shape = poke.shape();
1922 let partial = match unsafe { Partial::alloc_shape(shape) } {
1925 Ok(p) => p,
1926 Err(e) => {
1927 log::debug!("alloc_shape failed: {e}");
1928 return false;
1929 }
1930 };
1931 let mut partial = match partial.select_nth_variant(variant_idx) {
1933 Ok(p) => p,
1934 Err(e) => {
1935 log::debug!("select_nth_variant failed: {e}");
1936 return false;
1937 }
1938 };
1939
1940 let Type::User(UserType::Enum(enum_type)) = shape.ty else {
1943 return false;
1944 };
1945 let variant = &enum_type.variants[variant_idx];
1946 for field_idx in 0..variant.data.fields.len() {
1947 partial = match partial.set_nth_field_to_default(field_idx) {
1948 Ok(p) => p,
1949 Err(e) => {
1950 log::debug!(
1951 "set_nth_field_to_default({field_idx}) failed for variant '{}': {e}",
1952 variant.effective_name()
1953 );
1954 return false;
1955 }
1956 };
1957 }
1958
1959 let heap_value = match partial.build() {
1960 Ok(v) => v,
1961 Err(e) => {
1962 log::debug!("build failed: {e}");
1963 return false;
1964 }
1965 };
1966
1967 let size = shape
1968 .layout
1969 .sized_layout()
1970 .expect("enum must be sized")
1971 .size();
1972
1973 assert_eq!(poke.shape(), heap_value.shape());
1975 unsafe {
1978 let dst = poke.data_mut().as_mut_byte_ptr();
1982 let src = heap_value.peek().data().as_byte_ptr() as *mut u8;
1983 core::ptr::swap_nonoverlapping(dst, src, size);
1984 }
1985 drop(heap_value);
1986
1987 true
1988}
1989
1990fn show_inline_poke_scalar(
1991 poke: &mut Poke<'_, '_>,
1992 scalar_type: ScalarType,
1993 ui: &mut Ui,
1994) -> Response {
1995 match scalar_type {
1996 ScalarType::Bool => {
1997 if let Ok(v) = poke.get_mut::<bool>() {
1998 return ui.add(Checkbox::without_text(v));
1999 }
2000 }
2001 ScalarType::U8 => {
2002 if let Ok(v) = poke.get_mut::<u8>() {
2003 return ui.add(egui::DragValue::new(v));
2004 }
2005 }
2006 ScalarType::U16 => {
2007 if let Ok(v) = poke.get_mut::<u16>() {
2008 return ui.add(egui::DragValue::new(v));
2009 }
2010 }
2011 ScalarType::U32 => {
2012 if let Ok(v) = poke.get_mut::<u32>() {
2013 return ui.add(egui::DragValue::new(v));
2014 }
2015 }
2016 ScalarType::U64 => {
2017 if let Ok(v) = poke.get_mut::<u64>() {
2018 return ui.add(egui::DragValue::new(v));
2019 }
2020 }
2021 ScalarType::U128 => {
2022 if let Ok(v) = poke.get::<u128>() {
2024 return ui.label(alloc::format!("{v}"));
2025 }
2026 }
2027 ScalarType::USize => {
2028 if let Ok(v) = poke.get_mut::<usize>() {
2029 return ui.add(egui::DragValue::new(v));
2030 }
2031 }
2032 ScalarType::I8 => {
2033 if let Ok(v) = poke.get_mut::<i8>() {
2034 return ui.add(egui::DragValue::new(v));
2035 }
2036 }
2037 ScalarType::I16 => {
2038 if let Ok(v) = poke.get_mut::<i16>() {
2039 return ui.add(egui::DragValue::new(v));
2040 }
2041 }
2042 ScalarType::I32 => {
2043 if let Ok(v) = poke.get_mut::<i32>() {
2044 return ui.add(egui::DragValue::new(v));
2045 }
2046 }
2047 ScalarType::I64 => {
2048 if let Ok(v) = poke.get_mut::<i64>() {
2049 return ui.add(egui::DragValue::new(v));
2050 }
2051 }
2052 ScalarType::I128 => {
2053 if let Ok(v) = poke.get::<i128>() {
2054 return ui.label(alloc::format!("{v}"));
2055 }
2056 }
2057 ScalarType::ISize => {
2058 if let Ok(v) = poke.get_mut::<isize>() {
2059 return ui.add(egui::DragValue::new(v));
2060 }
2061 }
2062 ScalarType::F32 => {
2063 if let Ok(v) = poke.get_mut::<f32>() {
2064 return ui.add(egui::DragValue::new(v));
2065 }
2066 }
2067 ScalarType::F64 => {
2068 if let Ok(v) = poke.get_mut::<f64>() {
2069 return ui.add(egui::DragValue::new(v));
2070 }
2071 }
2072 ScalarType::String => {
2073 if let Ok(v) = poke.get_mut::<String>() {
2074 if v.contains('\n') {
2075 let rows = text_line_count(v);
2076 return ui.add(TextEdit::multiline(v).desired_rows(rows));
2077 }
2078 let mut r = ui.add(TextEdit::singleline(v));
2079 if (r.has_focus() || r.lost_focus()) && shift_enter_pressed(ui) {
2080 v.push('\n');
2081 r.mark_changed();
2082 r.request_focus();
2083 }
2084 return r;
2085 }
2086 }
2087 ScalarType::Char => {
2088 if let Ok(v) = poke.get::<char>() {
2089 let s = v.to_string();
2090 return ui.add_enabled(false, TextEdit::singleline(&mut s.as_str()));
2091 }
2092 }
2093 ScalarType::Str => {
2094 if poke.shape().is_display() {
2096 return ui.label(alloc::format!("{}", poke.as_peek()));
2097 }
2098 }
2099 ScalarType::CowStr => {
2100 if let Ok(v) = poke.get::<Cow<'_, str>>() {
2101 let mut s = v.clone();
2102 if s.contains('\n') {
2103 let rows = text_line_count(&s);
2104 return ui.add_enabled(false, TextEdit::multiline(&mut s).desired_rows(rows));
2105 }
2106 return ui.add_enabled(false, TextEdit::singleline(&mut s));
2107 }
2108 }
2109 _ if poke.shape().is_display() => {
2110 return ui.label(alloc::format!("{}", poke.as_peek()));
2111 }
2112 _ if poke.shape().is_debug() => {
2113 return ui.label(alloc::format!("{:?}", poke.as_peek()));
2114 }
2115 _ => {}
2116 }
2117 ui.colored_label(
2118 Color32::YELLOW,
2119 alloc::format!("unsupported scalar: {scalar_type:?}"),
2120 )
2121}
2122
2123fn show_inline_peek(peek: Peek<'_, '_>, ui: &mut Ui, id: Id, as_display: bool) -> Response {
2124 if as_display || should_render_as_display(peek.shape(), &[]) {
2125 return ui.label(alloc::format!("{}", peek));
2126 }
2127
2128 if let Some(scalar_type) = peek.scalar_type() {
2129 return show_inline_peek_scalar(peek, scalar_type, ui);
2130 }
2131
2132 if let Ok(opt) = peek.into_option() {
2135 return show_inline_peek_option(opt, ui, id);
2136 }
2137 if let Ok(enu) = peek.into_enum() {
2138 if let Ok(variant) = enu.active_variant() {
2139 return ui.weak(variant.effective_name());
2140 }
2141 return ui.weak("enum");
2142 }
2143 if let Ok(_struc) = peek.into_struct() {
2144 return ui.weak(shape_display_name(peek.shape()));
2145 }
2146 if let Ok(list) = peek.into_list_like() {
2147 return ui.weak(alloc::format!("[{}]", list.len()));
2148 }
2149 if let Ok(map) = peek.into_map() {
2150 return ui.weak(alloc::format!("[{}]", map.len()));
2151 }
2152 if let Ok(tuple) = peek.into_tuple() {
2153 return ui.weak(alloc::format!("({})", tuple.len()));
2154 }
2155 if let Ok(ptr) = peek.into_pointer()
2156 && let Some(inner) = ptr.borrow_inner()
2157 {
2158 return show_inline_peek(inner, ui, id.with("ptr"), false);
2159 }
2160
2161 ui.weak(shape_display_name(peek.shape()))
2162}
2163
2164fn show_inline_peek_scalar(peek: Peek<'_, '_>, scalar_type: ScalarType, ui: &mut Ui) -> Response {
2165 match scalar_type {
2166 ScalarType::Bool => {
2167 if let Ok(v) = peek.get::<bool>() {
2168 let mut value = *v;
2169 return ui.add_enabled(false, Checkbox::without_text(&mut value));
2170 }
2171 }
2172 ScalarType::Char => {
2173 if let Ok(c) = peek.get::<char>() {
2174 let s = c.to_string();
2175 return ui.add_enabled(false, TextEdit::singleline(&mut s.as_str()));
2176 }
2177 }
2178 ScalarType::Str => {
2179 if let Ok(v) = peek.get::<str>() {
2180 let mut s = v;
2181 if s.contains('\n') {
2182 let rows = text_line_count(s);
2183 return ui.add_enabled(false, TextEdit::multiline(&mut s).desired_rows(rows));
2184 }
2185 return ui.add_enabled(false, TextEdit::singleline(&mut s));
2186 }
2187 }
2188 ScalarType::CowStr => {
2189 if let Ok(v) = peek.get::<Cow<'_, str>>() {
2190 let mut s = v.clone();
2191 if s.contains('\n') {
2192 let rows = text_line_count(&s);
2193 return ui.add_enabled(false, TextEdit::multiline(&mut s).desired_rows(rows));
2194 }
2195 return ui.add_enabled(false, TextEdit::singleline(&mut s));
2196 }
2197 }
2198 ScalarType::String => {
2199 if let Ok(v) = peek.get::<String>() {
2200 let mut s: Cow<'_, str> = Cow::Borrowed(v.as_str());
2201 if s.contains('\n') {
2202 let rows = text_line_count(&s);
2203 return ui.add_enabled(false, TextEdit::multiline(&mut s).desired_rows(rows));
2204 }
2205 return ui.add_enabled(false, TextEdit::singleline(&mut s));
2206 }
2207 }
2208 _ if peek.shape().is_display() => {
2209 return ui.label(alloc::format!("{}", peek));
2210 }
2211 _ if peek.shape().is_debug() => {
2212 return ui.label(alloc::format!("{:?}", peek));
2213 }
2214 _ => {}
2215 }
2216 ui.colored_label(
2217 Color32::YELLOW,
2218 alloc::format!("unsupported scalar: {scalar_type:?}"),
2219 )
2220}
2221
2222fn show_inline_peek_option(opt: PeekOption<'_, '_>, ui: &mut Ui, id: Id) -> Response {
2223 ui.horizontal(|ui| {
2224 let is_some = opt.value().is_some();
2225 let _ = ui.selectable_label(!is_some, "None");
2226 let _ = ui.selectable_label(is_some, "Some");
2227 if let Some(inner) = opt.value() {
2228 show_inline_peek(inner, ui, id.with("some"), false);
2229 }
2230 })
2231 .response
2232}