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}