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