Skip to main content

tree_display/
context.rs

1//! Custom value transformations for tree display.
2//!
3//! Register mappers to modify how values are displayed without changing
4//! the underlying data. Useful for formatting, redaction, or debugging
5//! transformations.
6
7use super::format::Content;
8use std::{
9  any::{Any, TypeId},
10  collections::HashMap,
11};
12
13// ──── API ───────────────────────────────────────────────────────────────────────────────────────
14
15/// A function that maps one content value to another.
16pub type Mapper = dyn Fn(&dyn Content) -> Box<dyn Content>;
17
18/// Context for custom value transformations.
19///
20/// A context holds a collection of mappers keyed by the [`TypeId`] of the
21/// input type. When displaying a tree, the context is used to apply
22/// transformations to values before they are rendered.
23///
24/// ## Example
25/// ```no_run
26/// use tree_display::Context;
27///
28/// let context = Context::new()
29///     .map(|s: &String| format!("'{}'", s))
30///     .map(|n: &i32| format!("{}", n * 2));
31/// ```
32#[derive(Default)]
33pub struct Context {
34  /// Map from [`TypeId`] to a mapper function for that type.
35  pub mappers: HashMap<TypeId, Box<Mapper>>,
36}
37
38impl Context {
39  /// Creates a new empty context with no mappers.
40  pub fn new() -> Self {
41    Self {
42      mappers: HashMap::new(),
43    }
44  }
45
46  /// Registers a mapper for a specific type.
47  ///
48  /// The mapper function takes a reference to a value and returns another
49  /// value that implements `Content`. The transformed value will
50  /// be used when displaying values of type `T` in the tree.
51  ///
52  /// To apply a mapper to a specific field in a struct or enum, use the
53  /// `#[tree(map)]` attribute on that field when deriving [`TreeDisplay`](`super::TreeDisplay`).
54  ///
55  /// ## Type Parameters
56  /// - `T`: The input type to map from
57  /// - `R`: The output type that implements [`Content`]
58  ///
59  /// ## Example
60  /// ```no_run
61  /// use tree_display::context::Context;
62  ///
63  /// let context = Context::new()
64  ///     .map(|s: &String| format!("'{}'", s))
65  ///     .map(|n: &i32| format!("{}", n * 2));
66  /// ```
67  pub fn map<T: 'static, R: Content + 'static>(mut self, f: impl Fn(&T) -> R + 'static) -> Self {
68    self.mappers.insert(
69      TypeId::of::<T>(),
70      Box::new(move |content| {
71        let t = (content as &dyn Any)
72          .downcast_ref::<T>()
73          .expect("TypeId guarantees this matches");
74        Box::new(f(t))
75      }),
76    );
77    self
78  }
79}