1use crate::{Trace, Trie};
2use std::any::Any;
3use std::fmt::{self, Debug, Display, Write};
4use std::sync::Arc;
5
6pub type DynTrie = Trie<Arc<dyn Any + Send + Sync>>;
8
9pub type DynTrace<Args, Ret> = Trace<Args, DynTrie, Ret>;
11
12pub trait DynAutoCast: Clone + Sized + 'static {
14 fn autocast(value: &(dyn Any + Send + Sync)) -> Option<Self>;
16}
17
18impl DynTrie {
19 pub fn read<V: 'static + Clone>(&self, addr: &str) -> V {
21 match self.search(addr) {
22 Some(v) => {
23 let v_typed = v.ref_inner().unwrap().downcast_ref::<V>();
24 match v_typed {
25 Some(v) => v.clone(),
26 None => {
27 panic!("read: failed when downcasting type at address \"{}\"", addr);
28 }
29 }
30 }
31 None => {
32 panic!("read: failed when searching empty address \"{}\"", addr);
33 }
34 }
35 }
36
37 pub unsafe fn auto<V: DynAutoCast>(&self, addr: &str) -> V {
48 match self.search(addr) {
49 Some(v) => {
50 let value = v.ref_inner().unwrap().as_ref();
51 if let Some(value) = value.downcast_ref::<V>() {
52 return value.clone();
53 }
54 V::autocast(value).unwrap_or_else(|| {
55 panic!("auto: failed when autocasting type at address \"{addr}\"")
56 })
57 }
58 None => {
59 panic!("auto: failed when searching empty address \"{addr}\"");
60 }
61 }
62 }
63}
64
65macro_rules! impl_exact_autocast {
66 ($($ty:ty),* $(,)?) => {
67 $(
68 impl DynAutoCast for $ty {
69 fn autocast(_: &(dyn Any + Send + Sync)) -> Option<Self> {
70 None
71 }
72 }
73 )*
74 };
75}
76
77macro_rules! impl_numeric_autocast {
78 ($target:ty; $($source:ty),* $(,)?) => {
79 impl DynAutoCast for $target {
80 fn autocast(value: &(dyn Any + Send + Sync)) -> Option<Self> {
81 $(
82 if let Some(value) = value.downcast_ref::<$source>() {
83 return Some(*value as $target);
84 }
85 )*
86 None
87 }
88 }
89 };
90}
91
92macro_rules! impl_tuple2_autocast {
93 (($target:ty, $target2:ty); $(($source:ty, $source2:ty)),* $(,)?) => {
94 impl DynAutoCast for ($target, $target2) {
95 fn autocast(value: &(dyn Any + Send + Sync)) -> Option<Self> {
96 $(
97 if let Some(value) = value.downcast_ref::<($source, $source2)>() {
98 return Some((value.0 as $target, value.1 as $target2));
99 }
100 )*
101 None
102 }
103 }
104 };
105}
106
107macro_rules! impl_tuple3_autocast {
108 (($target:ty, $target2:ty, $target3:ty); $(($source:ty, $source2:ty, $source3:ty)),* $(,)?) => {
109 impl DynAutoCast for ($target, $target2, $target3) {
110 fn autocast(value: &(dyn Any + Send + Sync)) -> Option<Self> {
111 $(
112 if let Some(value) = value.downcast_ref::<($source, $source2, $source3)>() {
113 return Some((
114 value.0 as $target,
115 value.1 as $target2,
116 value.2 as $target3,
117 ));
118 }
119 )*
120 None
121 }
122 }
123 };
124}
125
126macro_rules! impl_tuple4_autocast {
127 (($target:ty, $target2:ty, $target3:ty, $target4:ty); $(($source:ty, $source2:ty, $source3:ty, $source4:ty)),* $(,)?) => {
128 impl DynAutoCast for ($target, $target2, $target3, $target4) {
129 fn autocast(value: &(dyn Any + Send + Sync)) -> Option<Self> {
130 $(
131 if let Some(value) = value.downcast_ref::<($source, $source2, $source3, $source4)>() {
132 return Some((
133 value.0 as $target,
134 value.1 as $target2,
135 value.2 as $target3,
136 value.3 as $target4,
137 ));
138 }
139 )*
140 None
141 }
142 }
143 };
144}
145
146impl_exact_autocast!(bool, char, String, &'static str);
147
148impl_numeric_autocast!(i8; i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64);
149impl_numeric_autocast!(i16; i8, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64);
150impl_numeric_autocast!(i32; i8, i16, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64);
151impl_numeric_autocast!(i64; i8, i16, i32, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64);
152impl_numeric_autocast!(i128; i8, i16, i32, i64, isize, u8, u16, u32, u64, u128, usize, f32, f64);
153impl_numeric_autocast!(isize; i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usize, f32, f64);
154impl_numeric_autocast!(u8; i8, i16, i32, i64, i128, isize, u16, u32, u64, u128, usize, f32, f64);
155impl_numeric_autocast!(u16; i8, i16, i32, i64, i128, isize, u8, u32, u64, u128, usize, f32, f64);
156impl_numeric_autocast!(u32; i8, i16, i32, i64, i128, isize, u8, u16, u64, u128, usize, f32, f64);
157impl_numeric_autocast!(u64; i8, i16, i32, i64, i128, isize, u8, u16, u32, u128, usize, f32, f64);
158impl_numeric_autocast!(u128; i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, usize, f32, f64);
159impl_numeric_autocast!(usize; i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, f32, f64);
160impl_numeric_autocast!(f32; i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f64);
161impl_numeric_autocast!(f64; i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32);
162
163impl_tuple2_autocast!((f32, f32); (f64, f64));
164impl_tuple2_autocast!((f64, f64); (f32, f32));
165impl_tuple3_autocast!((f32, f32, f32); (f64, f64, f64));
166impl_tuple3_autocast!((f64, f64, f64); (f32, f32, f32));
167impl_tuple4_autocast!((f32, f32, f32, f32); (f64, f64, f64, f64));
168impl_tuple4_autocast!((f64, f64, f64, f64); (f32, f32, f32, f32));
169
170macro_rules! impl_array_autocast {
171 ($target:ty; $($source:ty),* $(,)?) => {
172 impl<const N: usize> DynAutoCast for [$target; N] {
173 fn autocast(value: &(dyn Any + Send + Sync)) -> Option<Self> {
174 $(
175 if let Some(value) = value.downcast_ref::<[$source; N]>() {
176 return Some(value.map(|x| x as $target));
177 }
178 )*
179 None
180 }
181 }
182 };
183}
184
185impl_array_autocast!(f32; f64);
186impl_array_autocast!(f64; f32);
187
188fn format_tuple2<T: fmt::Display>(value: &(T, T)) -> String {
189 format!("({}, {})", value.0, value.1)
190}
191
192fn format_tuple3<T: fmt::Display>(value: &(T, T, T)) -> String {
193 format!("({}, {}, {})", value.0, value.1, value.2)
194}
195
196fn format_tuple4<T: fmt::Display>(value: &(T, T, T, T)) -> String {
197 format!("({}, {}, {}, {})", value.0, value.1, value.2, value.3)
198}
199
200fn format_array<T: fmt::Display, const N: usize>(value: &[T; N]) -> String {
201 let mut out = String::from("[");
202 for (i, x) in value.iter().enumerate() {
203 if i > 0 {
204 out.push_str(", ");
205 }
206 write!(out, "{x}").unwrap();
207 }
208 out.push(']');
209 out
210}
211
212pub fn dyn_value_to_string(value: &(dyn Any + Send + Sync)) -> String {
219 dyn_value_to_string_with(value, &[])
220}
221
222pub type DynValueFormatter = fn(&(dyn Any + Send + Sync)) -> Option<String>;
224
225#[derive(Clone, Copy, Debug, Eq, PartialEq)]
227pub struct DynTracePrintOptions {
228 pub args: bool,
230
231 pub data: bool,
233
234 pub retv: bool,
236
237 pub logjp: bool,
239
240 pub weights: bool,
242}
243
244impl DynTracePrintOptions {
245 pub const fn set_args(mut self, args: bool) -> Self {
247 self.args = args;
248 self
249 }
250
251 pub const fn set_data(mut self, data: bool) -> Self {
253 self.data = data;
254 self
255 }
256
257 pub const fn set_retv(mut self, retv: bool) -> Self {
259 self.retv = retv;
260 self
261 }
262
263 pub const fn set_logjp(mut self, logjp: bool) -> Self {
265 self.logjp = logjp;
266 self
267 }
268
269 pub const fn set_weights(mut self, weights: bool) -> Self {
271 self.weights = weights;
272 self
273 }
274
275 pub const fn verbose(verbose: bool) -> Self {
280 Self {
281 args: verbose,
282 data: true,
283 retv: verbose,
284 logjp: verbose,
285 weights: verbose,
286 }
287 }
288}
289
290impl Default for DynTracePrintOptions {
291 fn default() -> Self {
292 Self {
293 args: true,
294 data: true,
295 retv: true,
296 logjp: true,
297 weights: false,
298 }
299 }
300}
301
302pub fn dyn_display_formatter<T: Display + 'static>(
304 value: &(dyn Any + Send + Sync),
305) -> Option<String> {
306 value.downcast_ref::<T>().map(ToString::to_string)
307}
308
309pub fn dyn_debug_formatter<T: Debug + 'static>(value: &(dyn Any + Send + Sync)) -> Option<String> {
311 value
312 .downcast_ref::<T>()
313 .map(|value| format!("{:?}", value))
314}
315
316pub fn dyn_value_to_string_with(
319 value: &(dyn Any + Send + Sync),
320 formatters: &[DynValueFormatter],
321) -> String {
322 for formatter in formatters {
323 if let Some(value) = formatter(value) {
324 return value;
325 }
326 }
327
328 macro_rules! downcast_display {
329 ($($ty:ty),* $(,)?) => {
330 $(
331 if let Some(value) = value.downcast_ref::<$ty>() {
332 return value.to_string();
333 }
334 )*
335 };
336 }
337
338 macro_rules! downcast_tuple {
339 ($formatter:ident; $($ty:ty),* $(,)?) => {
340 $(
341 if let Some(value) = value.downcast_ref::<$ty>() {
342 return $formatter(value);
343 }
344 )*
345 };
346 }
347
348 downcast_display!(
349 bool,
350 char,
351 String,
352 &'static str,
353 i8,
354 i16,
355 i32,
356 i64,
357 i128,
358 isize,
359 u8,
360 u16,
361 u32,
362 u64,
363 u128,
364 usize,
365 f32,
366 f64,
367 );
368
369 downcast_tuple!(format_tuple2; (f32, f32), (f64, f64));
370 downcast_tuple!(format_tuple3; (f32, f32, f32), (f64, f64, f64));
371 downcast_tuple!(format_tuple4; (f32, f32, f32, f32), (f64, f64, f64, f64));
372
373 macro_rules! downcast_array {
374 ($($ty:ty; $n:literal),* $(,)?) => {
375 $(
376 if let Some(value) = value.downcast_ref::<[$ty; $n]>() {
377 return format_array(value);
378 }
379 )*
380 };
381 }
382
383 downcast_array!(
384 f32; 2, f32; 3, f32; 4,
385 f64; 2, f64; 3, f64; 4,
386 );
387
388 "<unknown>".to_string()
389}
390
391fn write_dyntrie(
392 out: &mut String,
393 trie: &DynTrie,
394 indent: usize,
395 formatters: &[DynValueFormatter],
396 options: DynTracePrintOptions,
397) {
398 let mut entries = trie.iter().collect::<Vec<_>>();
399 entries.sort_by(|(left, _), (right, _)| left.cmp(right));
400
401 for (addr, subtrie) in entries {
402 let padding = " ".repeat(indent);
403 if subtrie.is_leaf() {
404 let value = subtrie.ref_inner().unwrap();
405 write!(
406 out,
407 "{padding}\"{addr}\" => {}",
408 dyn_value_to_string_with(value.as_ref(), formatters)
409 )
410 .unwrap();
411 if options.weights {
412 write!(out, " [weight = {}]", subtrie.weight()).unwrap();
413 }
414 writeln!(out).unwrap();
415 } else {
416 write!(out, "{padding}\"{addr}\" => {{").unwrap();
417 if options.weights {
418 write!(out, " [weight = {}]", subtrie.weight()).unwrap();
419 }
420 writeln!(out).unwrap();
421 write_dyntrie(out, subtrie, indent + 2, formatters, options);
422 writeln!(out, "{padding}}}").unwrap();
423 }
424 }
425}
426
427pub fn dyntrie_to_string(trie: &DynTrie) -> String {
429 dyntrie_to_string_with(trie, &[])
430}
431
432pub fn dyntrie_to_string_with(trie: &DynTrie, formatters: &[DynValueFormatter]) -> String {
435 dyntrie_to_string_with_options(trie, formatters, DynTracePrintOptions::default())
436}
437
438pub fn dyntrie_to_string_with_options(
441 trie: &DynTrie,
442 formatters: &[DynValueFormatter],
443 options: DynTracePrintOptions,
444) -> String {
445 let mut out = String::from("data: {\n");
446 write_dyntrie(&mut out, trie, 2, formatters, options);
447 out.push('}');
448 out
449}
450
451pub fn dyntrace_to_string<Args: Debug, Ret: Debug>(trace: &DynTrace<Args, Ret>) -> String {
453 dyntrace_to_string_with(trace, &[])
454}
455
456pub fn dyntrace_to_string_with<Args: Debug, Ret: Debug>(
459 trace: &DynTrace<Args, Ret>,
460 formatters: &[DynValueFormatter],
461) -> String {
462 dyntrace_to_string_with_options(trace, formatters, DynTracePrintOptions::default())
463}
464
465pub fn dyntrace_to_string_with_options<Args: Debug, Ret: Debug>(
468 trace: &DynTrace<Args, Ret>,
469 formatters: &[DynValueFormatter],
470 options: DynTracePrintOptions,
471) -> String {
472 let mut out = String::new();
473 if options.args {
474 writeln!(out, "args: {:?}", trace.args).unwrap();
475 }
476 if options.data {
477 writeln!(
478 out,
479 "{}",
480 dyntrie_to_string_with_options(&trace.data, formatters, options)
481 )
482 .unwrap();
483 }
484 if options.retv {
485 match trace.retv.as_ref() {
486 Some(retv) => writeln!(out, "retv: {:?}", retv).unwrap(),
487 None => writeln!(out, "retv: None").unwrap(),
488 }
489 }
490 if options.logjp {
491 writeln!(out, "logjp: {}", trace.logjp).unwrap();
492 }
493 out
494}
495
496pub fn print_dyntrace<Args: Debug, Ret: Debug>(trace: &DynTrace<Args, Ret>) {
498 print!("{}", dyntrace_to_string(trace));
499}
500
501pub fn print_dyntrace_with<Args: Debug, Ret: Debug>(
504 trace: &DynTrace<Args, Ret>,
505 formatters: &[DynValueFormatter],
506) {
507 print!("{}", dyntrace_to_string_with(trace, formatters));
508}
509
510pub fn print_dyntrace_with_options<Args: Debug, Ret: Debug>(
513 trace: &DynTrace<Args, Ret>,
514 formatters: &[DynValueFormatter],
515 options: DynTracePrintOptions,
516) {
517 print!(
518 "{}",
519 dyntrace_to_string_with_options(trace, formatters, options)
520 );
521}
522
523#[cfg(test)]
524mod tests {
525 use super::{
526 dyn_debug_formatter, dyn_display_formatter, dyn_value_to_string, dyn_value_to_string_with,
527 dyntrace_to_string_with_options, dyntrie_to_string, dyntrie_to_string_with_options,
528 DynTracePrintOptions,
529 };
530 use crate::{Trace, Trie};
531 use std::any::Any;
532 use std::fmt;
533 use std::sync::Arc;
534
535 #[derive(Debug)]
536 struct CustomStruct {
537 value: i64,
538 }
539
540 impl fmt::Display for CustomStruct {
541 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
542 write!(f, "CustomStruct(value = {})", self.value)
543 }
544 }
545
546 #[test]
547 fn dyn_value_to_string_prints_literals_and_unknown_custom_values() {
548 assert_eq!(dyn_value_to_string(&true), "true");
549 assert_eq!(dyn_value_to_string(&42_i64), "42");
550 assert_eq!(dyn_value_to_string(&"label"), "label");
551 assert_eq!(dyn_value_to_string(&(0.1_f64, 0.2_f64)), "(0.1, 0.2)");
552 assert_eq!(
553 dyn_value_to_string(&(0.3_f64, 0.5_f64, 0.2_f64)),
554 "(0.3, 0.5, 0.2)"
555 );
556 let custom = CustomStruct { value: 3 };
557 assert_eq!(custom.value, 3);
558 assert_eq!(dyn_value_to_string(&custom), "<unknown>");
559 assert_eq!(
560 dyn_value_to_string_with(&custom, &[dyn_display_formatter::<CustomStruct>]),
561 "CustomStruct(value = 3)"
562 );
563 assert_eq!(
564 dyn_value_to_string_with(&custom, &[dyn_debug_formatter::<CustomStruct>]),
565 "CustomStruct { value: 3 }"
566 );
567 }
568
569 #[test]
570 fn dyntrie_to_string_can_hide_weights() {
571 let mut trie = Trie::new();
572 trie.w_observe("x", Arc::new(1.0_f64) as Arc<dyn Any + Send + Sync>, -2.0);
573
574 assert!(!dyntrie_to_string(&trie).contains("[weight ="));
575 assert!(dyntrie_to_string_with_options(
576 &trie,
577 &[],
578 DynTracePrintOptions::default().set_weights(true)
579 )
580 .contains("[weight = -2]"));
581 assert!(!dyntrie_to_string_with_options(
582 &trie,
583 &[],
584 DynTracePrintOptions::default().set_weights(false)
585 )
586 .contains("[weight ="));
587 }
588
589 #[test]
590 fn dyntrace_print_options_control_fields_and_unwrap_retv() {
591 let mut trie = Trie::new();
592 trie.observe("x", Arc::new(1.0_f64) as Arc<dyn Any + Send + Sync>);
593 let trace = Trace::new("args", trie, "retv", -1.5);
594
595 let all_fields =
596 dyntrace_to_string_with_options(&trace, &[], DynTracePrintOptions::default());
597 assert!(all_fields.contains("args: \"args\""));
598 assert!(all_fields.contains("data: {"));
599 assert!(all_fields.contains("retv: \"retv\""));
600 assert!(!all_fields.contains("retv: Some"));
601 assert!(all_fields.contains("logjp: -1.5"));
602
603 let data_only = dyntrace_to_string_with_options(
604 &trace,
605 &[],
606 DynTracePrintOptions::default()
607 .set_args(false)
608 .set_retv(false)
609 .set_logjp(false),
610 );
611 assert!(!data_only.contains("args:"));
612 assert!(data_only.contains("data: {"));
613 assert!(!data_only.contains("retv:"));
614 assert!(!data_only.contains("logjp:"));
615 }
616
617 #[test]
618 fn dyntrace_print_options_verbose_sets_max_and_min() {
619 let mut trie = Trie::new();
620 trie.w_observe("x", Arc::new(1.0_f64) as Arc<dyn Any + Send + Sync>, -2.0);
621 let trace = Trace::new("args", trie, "retv", -1.5);
622
623 let max = dyntrace_to_string_with_options(&trace, &[], DynTracePrintOptions::verbose(true));
624 assert!(max.contains("args:"));
625 assert!(max.contains("data: {"));
626 assert!(max.contains("retv:"));
627 assert!(max.contains("logjp:"));
628 assert!(max.contains("[weight = -2]"));
629
630 let min =
631 dyntrace_to_string_with_options(&trace, &[], DynTracePrintOptions::verbose(false));
632 assert!(!min.contains("args:"));
633 assert!(min.contains("data: {"));
634 assert!(!min.contains("retv:"));
635 assert!(!min.contains("logjp:"));
636 assert!(!min.contains("[weight ="));
637 }
638
639 #[test]
640 fn dyntrie_auto_converts_supported_literals() {
641 let mut trie = Trie::new();
642 trie.observe("f64", Arc::new(0.25_f64) as Arc<dyn Any + Send + Sync>);
643 trie.observe(
644 "rgb",
645 Arc::new((0.3_f64, 0.5_f64, 0.2_f64)) as Arc<dyn Any + Send + Sync>,
646 );
647 trie.observe("n", Arc::new(3_u8) as Arc<dyn Any + Send + Sync>);
648
649 unsafe {
650 assert_eq!(trie.auto::<f32>("f64"), 0.25_f32);
651 assert_eq!(
652 trie.auto::<(f32, f32, f32)>("rgb"),
653 (0.3_f32, 0.5_f32, 0.2_f32)
654 );
655 assert_eq!(trie.auto::<i64>("n"), 3_i64);
656 }
657 }
658
659 #[test]
660 #[should_panic(expected = "read: failed when downcasting type at address \"f64\"")]
661 fn dyntrie_read_stays_strict_when_autocast_would_succeed() {
662 let mut trie = Trie::new();
663 trie.observe("f64", Arc::new(0.25_f64) as Arc<dyn Any + Send + Sync>);
664
665 let _ = trie.read::<f32>("f64");
666 }
667}