waterui_core/components/metadata.rs
1//! Metadata components for attaching arbitrary data to views.
2//!
3//! This module provides two types of metadata components:
4//! - `Metadata<T>`: Strict metadata that must be handled by renderers
5//! - `IgnorableMetadata<T>`: Optional metadata that can be safely ignored
6//!
7//! Metadata can be used to attach arbitrary data to views that will be processed
8//! by renderers, such as accessibility attributes, transition effects, or custom
9//! rendering instructions.
10
11use alloc::boxed::Box;
12use core::any::Any;
13use core::any::type_name;
14
15use crate::{AnyView, Environment, View};
16
17/// Represents a view that carries additional metadata of type `T`.
18///
19/// This struct allows attaching arbitrary data to a view component. The metadata
20/// is expected to be handled by a renderer, and will panic if not properly caught.
21///
22/// Metadata is transparent for layout system, it is not a native view.
23#[derive(Debug)]
24#[must_use]
25pub struct Metadata<T: MetadataKey> {
26 /// The view content wrapped by this metadata.
27 pub content: AnyView,
28 /// The metadata value associated with the content.
29 pub value: T,
30}
31
32/// A marker trait for metadata keys.
33pub trait MetadataKey: 'static {}
34
35impl<T: MetadataKey> Metadata<T> {
36 /// Creates a new `Metadata` instance with the specified content and value.
37 ///
38 /// # Arguments
39 ///
40 /// * `content` - The view to be wrapped with metadata.
41 /// * `value` - The metadata value to associate with the content.
42 pub fn new(content: impl View, value: T) -> Self {
43 Self {
44 content: AnyView::new(content),
45 value,
46 }
47 }
48
49 #[cold]
50 fn panic_not_caught() {
51 panic!(
52 "The metadata `{}` is not caught by your renderer. If the metadata is not essential, use `IgnorableMetadata<T>`.",
53 type_name::<Self>()
54 );
55 }
56}
57
58impl<T: MetadataKey> View for Metadata<T> {
59 fn body(self, _env: &Environment) -> impl View {
60 Self::panic_not_caught();
61 }
62}
63
64/// A metadata wrapper that can be safely ignored by renderers if not handled explicitly.
65///
66/// Unlike `Metadata<T>`, this type won't panic if not caught by a renderer.
67#[derive(Debug)]
68pub struct IgnorableMetadata<T: MetadataKey> {
69 /// The view content wrapped by this ignorable metadata.
70 pub content: AnyView,
71 /// The metadata value associated with the content.
72 pub value: T,
73}
74
75impl<T: MetadataKey> IgnorableMetadata<T> {
76 /// Creates a new `IgnorableMetadata` instance with the specified content and value.
77 ///
78 /// # Arguments
79 ///
80 /// * `content` - The view to be wrapped with ignorable metadata.
81 /// * `value` - The metadata value to associate with the content.
82 pub fn new(content: impl View, value: T) -> Self {
83 Self {
84 content: AnyView::new(content),
85 value,
86 }
87 }
88}
89
90impl<T: MetadataKey> View for IgnorableMetadata<T> {
91 fn body(self, _env: &Environment) -> impl View {
92 self.content
93 }
94}
95
96/// A metadata key that retains a value for its lifetime.
97///
98/// This is useful for keeping watcher guards, subscriptions, or other values
99/// alive as long as the view exists. The retained value is dropped when the
100/// view is dropped.
101///
102/// This type implements `MetadataKey` and is used with `Metadata`,
103/// so renderers must handle it (by extracting content and keeping the value alive).
104#[derive(Debug)]
105pub struct Retain {
106 /// The retained value (not read, only kept alive).
107 _value: Box<dyn Any>,
108}
109
110impl MetadataKey for Retain {}
111
112impl Retain {
113 /// Creates a new `Retain` from a value.
114 pub fn new<T: 'static>(value: T) -> Self {
115 Self {
116 _value: Box::new(value),
117 }
118 }
119}