Skip to main content

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}