waterui_core/
env.rs

1//! Environment management module for sharing data across views.
2//!
3//! This module provides functionality for creating and managing environment contexts
4//! that can be passed through the view hierarchy. The environment is a type-based
5//! key-value store where types serve as unique keys.
6//!
7//! The main components are:
8//! - `Environment`: A store for typed values that can be passed between views
9//! - `UseEnv`: A view that allows consuming environment values
10//! - `With`: A view that extends the environment with additional values
11//!
12//! # Example
13//!
14//! ```rust
15//! use waterui_core::{Environment,env::use_env};
16//!
17//! // Create an environment with a string value
18//! let env = Environment::new().with(String::from("Hello, world!"));
19//!
20//! // Access the value in a child view
21//! // let view = use_env(|env: &Environment| {
22//! //     if let Some(message) = env.get::<String>() {
23//! //         message
24//! //     } else {
25//! //         "No message found".to_string()
26//! //     }
27//! // });
28//! ```
29
30use core::{
31    any::{Any, TypeId},
32    fmt::Debug,
33    marker::PhantomData,
34};
35
36use alloc::{collections::BTreeMap, rc::Rc};
37
38/// An `Environment` stores a map of types to values.
39///
40/// Each type can have at most one value in the environment. The environment
41/// is used to pass contextual information from parent views to child views.
42///
43/// # Examples
44///
45/// ```
46/// use waterui_core::Environment;
47///
48/// let mut env = Environment::new();
49/// env.insert(String::from("hello"));
50///
51/// // Get the value back
52/// assert_eq!(env.get::<String>(), Some(&String::from("hello")));
53///
54/// // Remove the value
55/// env.remove::<String>();
56/// assert_eq!(env.get::<String>(), None);
57/// ```
58#[derive(Debug, Clone, Default)]
59pub struct Environment {
60    map: BTreeMap<TypeId, Rc<dyn Any>>,
61}
62
63impl MetadataKey for Environment {}
64
65use crate::{
66    View,
67    components::Metadata,
68    handler::{HandlerFnOnce, HandlerOnce, IntoHandlerOnce},
69    metadata::MetadataKey,
70    plugin::Plugin,
71    view::{Hook, ViewConfiguration},
72};
73
74/// A type-indexed storage container for values in an environment.
75///
76/// This struct allows storing values of any type `V` indexed by a type key `K`.
77#[derive(Debug)]
78pub struct Store<K, V> {
79    key: PhantomData<K>,
80    value: V,
81}
82
83impl<K, V> Store<K, V> {
84    /// Creates a new store with the given value.
85    #[must_use]
86    pub const fn new(value: V) -> Self {
87        Self {
88            key: PhantomData,
89            value,
90        }
91    }
92
93    /// Returns a reference to the stored value.
94    #[must_use]
95    pub const fn value(&self) -> &V {
96        &self.value
97    }
98}
99
100impl Environment {
101    /// Creates a new empty environment.
102    #[must_use]
103    pub const fn new() -> Self {
104        Self {
105            map: BTreeMap::new(),
106        }
107    }
108
109    /// Stores a value in the environment indexed by type `K`.
110    ///
111    /// # Arguments
112    /// * `value` - The value to store
113    #[must_use]
114    pub fn store<K: 'static, V: 'static>(mut self, value: V) -> Self {
115        self.insert(Store {
116            key: PhantomData::<V>,
117            value,
118        });
119        self
120    }
121
122    /// Queries for a value in the environment indexed by type `K`.
123    ///
124    /// # Returns
125    /// An optional reference to the stored value
126    #[must_use]
127    pub fn query<K: 'static, V: 'static>(&self) -> Option<&V> {
128        self.get::<Store<K, V>>().map(|s| &s.value)
129    }
130
131    /// Installs a plugin into the environment.
132    ///
133    /// Plugins can register values or modifiers that will be available to all views.
134    pub fn install(&mut self, plugin: impl Plugin) -> &mut Self {
135        plugin.install(self);
136        self
137    }
138
139    /// Inserts a value into the environment.
140    ///
141    /// If a value of the same type already exists, it will be replaced.
142    pub fn insert<T: 'static>(&mut self, value: T) {
143        self.map.insert(TypeId::of::<T>(), Rc::new(value));
144    }
145
146    /// Inserts a view configuration hook into the environment.
147    ///
148    /// Hooks allow you to intercept and modify view configurations globally.
149    pub fn insert_hook<T: ViewConfiguration, V: View>(
150        &mut self,
151        hook: impl Fn(&Self, T) -> V + 'static,
152    ) {
153        self.insert(Hook::new(hook));
154    }
155
156    /// Removes a value from the environment by its type.
157    pub fn remove<T: 'static>(&mut self) {
158        self.map.remove(&TypeId::of::<T>());
159    }
160
161    /// Adds a value to the environment and returns the modified environment.
162    ///
163    /// This is a fluent interface for chaining multiple additions.
164    pub fn with<T: 'static>(&mut self, value: T) -> &mut Self {
165        self.insert(value);
166        self
167    }
168
169    /// Retrieves a reference to a value from the environment by its type.
170    ///
171    /// Returns `None` if no value of the requested type exists.
172    ///
173    /// # Panics
174    ///
175    /// This function will panic if a value of the requested type exists in the environment,
176    /// but the stored value cannot be downcast to the requested type. This should never happen
177    /// if only `insert` and `with` are used to add values.
178    #[must_use]
179    #[allow(clippy::coerce_container_to_any)]
180    pub fn get<T: 'static>(&self) -> Option<&T> {
181        self.map
182            .get(&TypeId::of::<T>())
183            .map(|v| v.downcast_ref::<T>().expect("failed to downcast value"))
184    }
185}
186
187/// A view that provides access to the environment.
188///
189/// `UseEnv` allows child views to access values stored in the environment
190/// through a handler function.
191#[derive(Debug, Clone)]
192pub struct UseEnv<V, H> {
193    handler: H,
194    _marker: PhantomData<V>,
195}
196
197impl<V, H> UseEnv<V, H> {
198    /// Creates a new `UseEnv` with the provided handler.
199    #[must_use]
200    pub const fn new(handler: H) -> Self {
201        Self {
202            handler,
203            _marker: PhantomData,
204        }
205    }
206}
207
208/// Creates a view that can access the environment.
209///
210/// This function takes a closure that receives a reference to the environment
211/// and returns a view. It's a convenience wrapper around `UseEnv`.
212#[must_use]
213pub const fn use_env<P, V, F>(f: F) -> UseEnv<V, IntoHandlerOnce<F, P, V>>
214where
215    V: View,
216    F: HandlerFnOnce<P, V>,
217{
218    UseEnv::new(IntoHandlerOnce::new(f))
219}
220
221impl<V, H> View for UseEnv<V, H>
222where
223    V: View,
224    H: HandlerOnce<V>,
225{
226    fn body(self, env: &Environment) -> impl View {
227        self.handler.handle(env)
228    }
229}
230
231/// A view that extends the environment with an additional value.
232///
233/// `With` wraps a child view and provides an extended environment that
234/// includes a new value of type `T`.
235#[derive(Debug, Clone)]
236pub struct With<V, T> {
237    content: V,
238    value: T,
239}
240
241impl<V: View, T: 'static> With<V, T> {
242    /// Creates a new `With` view that wraps the provided content and adds
243    /// the given value to the environment for all child views.
244    pub const fn new(content: V, value: T) -> Self {
245        Self { content, value }
246    }
247}
248
249/// Wraps a view and provides an extended environment value.
250pub const fn with<V: View, T: 'static>(view: V, value: T) -> With<V, T> {
251    With::new(view, value)
252}
253
254impl<V: View, T: 'static> View for With<V, T> {
255    fn body(self, env: &Environment) -> impl View {
256        let mut env = env.clone();
257        env.insert(self.value);
258        Metadata::new(self.content, env)
259    }
260}