1use core::{
31 any::{Any, TypeId},
32 fmt::Debug,
33 marker::PhantomData,
34};
35
36use alloc::{collections::BTreeMap, rc::Rc, vec::Vec};
37
38#[derive(Debug, Clone)]
59pub struct Environment {
60 state: Rc<EnvironmentState>,
61}
62
63#[derive(Debug, Clone)]
64enum EnvironmentState {
65 Map(BTreeMap<TypeId, Rc<dyn Any>>),
66 Overlay {
67 parent: Rc<Self>,
68 key: TypeId,
69 entry: EnvironmentEntry,
70 },
71}
72
73#[derive(Debug, Clone)]
74enum EnvironmentEntry {
75 Present(Rc<dyn Any>),
76 Removed,
77}
78
79impl MetadataKey for Environment {}
80
81impl Default for Environment {
82 fn default() -> Self {
83 Self::new()
84 }
85}
86
87use crate::{
88 View,
89 components::Metadata,
90 extract::Extractor,
91 metadata::MetadataKey,
92 plugin::Plugin,
93 view::{Hook, ViewConfiguration},
94};
95
96#[derive(Debug)]
100pub struct Store<K, V> {
101 key: PhantomData<K>,
102 value: V,
103}
104
105impl<K, V> Store<K, V> {
106 #[must_use]
108 pub const fn new(value: V) -> Self {
109 Self {
110 key: PhantomData,
111 value,
112 }
113 }
114
115 #[must_use]
117 pub const fn value(&self) -> &V {
118 &self.value
119 }
120}
121
122impl Environment {
123 #[must_use]
125 pub fn identity(&self) -> usize {
126 Rc::as_ptr(&self.state) as usize
127 }
128
129 fn insert_any(&mut self, key: TypeId, value: Rc<dyn Any>) {
130 match Rc::get_mut(&mut self.state) {
131 Some(EnvironmentState::Map(map)) => {
132 map.insert(key, value);
133 }
134 Some(EnvironmentState::Overlay {
135 key: overlay_key,
136 entry,
137 ..
138 }) if *overlay_key == key => {
139 *entry = EnvironmentEntry::Present(value);
140 }
141 _ => self.push_overlay(key, EnvironmentEntry::Present(value)),
142 }
143 }
144
145 fn lookup_any_in_state(state: &EnvironmentState, key: TypeId) -> Option<&Rc<dyn Any>> {
146 match state {
147 EnvironmentState::Map(map) => map.get(&key),
148 EnvironmentState::Overlay {
149 parent,
150 key: overlay_key,
151 entry,
152 } => {
153 if *overlay_key == key {
154 match entry {
155 EnvironmentEntry::Present(value) => Some(value),
156 EnvironmentEntry::Removed => None,
157 }
158 } else {
159 Self::lookup_any_in_state(parent.as_ref(), key)
160 }
161 }
162 }
163 }
164
165 fn lookup_any(&self, key: TypeId) -> Option<&Rc<dyn Any>> {
166 Self::lookup_any_in_state(self.state.as_ref(), key)
167 }
168
169 fn push_overlay(&mut self, key: TypeId, entry: EnvironmentEntry) {
170 self.state = Rc::new(EnvironmentState::Overlay {
171 parent: self.state.clone(),
172 key,
173 entry,
174 });
175 }
176
177 fn extend_from_state(&mut self, state: &EnvironmentState) {
178 match state {
179 EnvironmentState::Map(map) => {
180 for (key, value) in map {
181 self.insert_any(*key, value.clone());
182 }
183 }
184 EnvironmentState::Overlay { parent, key, entry } => {
185 self.extend_from_state(parent.as_ref());
186 self.push_overlay(*key, entry.clone());
187 }
188 }
189 }
190
191 fn collect_matches_in_state<'a, T: 'static>(
192 state: &'a EnvironmentState,
193 matches: &mut Vec<&'a T>,
194 ) {
195 match state {
196 EnvironmentState::Map(map) => {
197 if let Some(value) = map.get(&TypeId::of::<T>()) {
198 matches.push(
199 value
200 .downcast_ref::<T>()
201 .expect("failed to downcast value while collecting environment state"),
202 );
203 }
204 }
205 EnvironmentState::Overlay { parent, key, entry } => {
206 Self::collect_matches_in_state(parent.as_ref(), matches);
207 if *key != TypeId::of::<T>() {
208 return;
209 }
210 match entry {
211 EnvironmentEntry::Present(value) => matches.push(
212 value
213 .downcast_ref::<T>()
214 .expect("failed to downcast value while collecting environment state"),
215 ),
216 EnvironmentEntry::Removed => matches.clear(),
217 }
218 }
219 }
220 }
221
222 #[must_use]
224 pub fn new() -> Self {
225 Self {
226 state: Rc::new(EnvironmentState::Map(BTreeMap::new())),
227 }
228 }
229
230 #[must_use]
235 pub fn store<K: 'static, V: 'static>(mut self, value: V) -> Self {
236 self.insert(Store {
237 key: PhantomData::<K>,
238 value,
239 });
240 self
241 }
242
243 #[must_use]
248 pub fn query<K: 'static, V: 'static>(&self) -> Option<&V> {
249 self.get::<Store<K, V>>().map(|s| &s.value)
250 }
251
252 pub fn install(&mut self, plugin: impl Plugin) -> &mut Self {
256 plugin.install(self);
257 self
258 }
259
260 pub fn insert<T: 'static>(&mut self, value: T) {
264 let key = TypeId::of::<T>();
265 let value = Rc::new(value) as Rc<dyn Any>;
266 self.insert_any(key, value);
267 }
268
269 pub fn insert_hook<T: ViewConfiguration, V: View>(
273 &mut self,
274 hook: impl Fn(&Self, T) -> V + 'static,
275 ) {
276 self.insert(Hook::new(hook));
277 }
278
279 pub fn remove<T: 'static>(&mut self) {
281 let key = TypeId::of::<T>();
282 match Rc::get_mut(&mut self.state) {
283 Some(EnvironmentState::Map(map)) => {
284 map.remove(&key);
285 }
286 Some(EnvironmentState::Overlay {
287 key: overlay_key,
288 entry,
289 ..
290 }) if *overlay_key == key => {
291 *entry = EnvironmentEntry::Removed;
292 }
293 _ => self.push_overlay(key, EnvironmentEntry::Removed),
294 }
295 }
296
297 pub fn with<T: 'static>(&mut self, value: T) -> &mut Self {
301 self.insert(value);
302 self
303 }
304
305 #[must_use]
310 pub fn extending<T: 'static>(&self, value: T) -> Self {
311 Self {
312 state: Rc::new(EnvironmentState::Overlay {
313 parent: self.state.clone(),
314 key: TypeId::of::<T>(),
315 entry: EnvironmentEntry::Present(Rc::new(value) as Rc<dyn Any>),
316 }),
317 }
318 }
319
320 #[must_use]
330 #[allow(clippy::coerce_container_to_any)]
331 pub fn get<T: 'static>(&self) -> Option<&T> {
332 self.lookup_any(TypeId::of::<T>())
333 .map(|v| v.downcast_ref::<T>().expect("failed to downcast value"))
334 }
335
336 #[must_use]
348 pub fn get_nth<T: 'static>(&self, index: usize) -> Option<&T> {
349 let mut matches = Vec::new();
350 Self::collect_matches_in_state(self.state.as_ref(), &mut matches);
351 matches.into_iter().nth_back(index)
352 }
353
354 #[must_use]
364 pub fn get_or_insert_with<T: 'static, F: FnOnce() -> T>(&mut self, f: F) -> &T {
365 if self.lookup_any(TypeId::of::<T>()).is_none() {
366 self.insert(f());
367 }
368 self.get::<T>()
369 .expect("value missing from environment after insertion")
370 }
371
372 pub fn extract<T: Extractor>(&self) -> Result<T, anyhow::Error> {
380 T::extract(self)
381 }
382
383 #[must_use]
388 pub fn layered_on(&self, parent: &Self) -> Self {
389 let mut layered = parent.clone();
390 layered.extend_from_state(self.state.as_ref());
391 layered
392 }
393}
394
395#[derive(Debug, Clone)]
400pub struct UseEnv<F> {
401 handler: F,
402}
403
404impl<F> UseEnv<F> {
405 #[must_use]
407 pub const fn new(handler: F) -> Self {
408 Self { handler }
409 }
410}
411
412#[must_use]
442pub fn use_env<E, V, F>(f: F) -> UseEnv<impl FnOnce(&Environment) -> V>
443where
444 E: Extractor,
445 V: View,
446 F: FnOnce(E) -> V + 'static,
447{
448 UseEnv::new(move |env: &Environment| {
449 let extracted = E::extract(env).expect("failed to extract value from environment");
450 f(extracted)
451 })
452}
453
454impl<V, F> View for UseEnv<F>
455where
456 V: View,
457 F: FnOnce(&Environment) -> V + 'static,
458{
459 fn body(self, env: &Environment) -> impl View {
460 (self.handler)(env)
461 }
462}
463
464#[derive(Debug, Clone)]
469pub struct With<V, T> {
470 content: V,
471 value: T,
472}
473
474impl<V: View, T: 'static> With<V, T> {
475 pub const fn new(content: V, value: T) -> Self {
478 Self { content, value }
479 }
480}
481
482pub const fn with<V: View, T: 'static>(view: V, value: T) -> With<V, T> {
484 With::new(view, value)
485}
486
487impl<V: View, T: 'static> View for With<V, T> {
488 fn body(self, env: &Environment) -> impl View {
489 let env = env.extending(self.value);
490 Metadata::new(self.content, env)
491 }
492}
493
494#[cfg(test)]
495mod tests {
496 use alloc::string::String;
497
498 use super::*;
499
500 #[test]
501 fn extending_reuses_parent_state_via_overlay() {
502 let mut base = Environment::new();
503 base.insert(7_u32);
504 let parent_state = base.state.clone();
505
506 let extended = base.extending(11_u64);
507 match extended.state.as_ref() {
508 EnvironmentState::Overlay { parent, key, entry } => {
509 assert!(Rc::ptr_eq(parent, &parent_state));
510 assert_eq!(*key, TypeId::of::<u64>());
511 match entry {
512 EnvironmentEntry::Present(value) => {
513 assert_eq!(value.downcast_ref::<u64>(), Some(&11_u64));
514 }
515 EnvironmentEntry::Removed => {
516 panic!("overlay entry unexpectedly removed");
517 }
518 }
519 }
520 EnvironmentState::Map(_) => panic!("extending must create overlay state"),
521 }
522 }
523
524 #[test]
525 fn get_nth_counts_from_the_nearest_overlay_outwards() {
526 let env = Environment::new()
527 .extending(1_i32)
528 .extending(2_i32)
529 .extending(3_i32);
530
531 assert_eq!(env.get_nth::<i32>(0), Some(&3_i32));
532 assert_eq!(env.get_nth::<i32>(1), Some(&2_i32));
533 assert_eq!(env.get_nth::<i32>(2), Some(&1_i32));
534 assert_eq!(env.get_nth::<i32>(3), None);
535 }
536
537 #[test]
538 fn get_nth_zero_agrees_with_get() {
539 let env = Environment::new().extending(1_i32).extending(2_i32);
540
541 assert_eq!(env.get_nth::<i32>(0), env.get::<i32>());
542 }
543
544 #[test]
545 fn deep_overlay_chain_preserves_parent_visibility() {
546 let mut env = Environment::new();
547 env.insert(String::from("root"));
548 let env = env.extending(3_i32).extending(true).extending(9_u8);
549
550 assert_eq!(env.get::<String>(), Some(&String::from("root")));
551 assert_eq!(env.get::<i32>(), Some(&3_i32));
552 assert_eq!(env.get::<bool>(), Some(&true));
553 assert_eq!(env.get::<u8>(), Some(&9_u8));
554 }
555}