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