Skip to main content
This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.

yew_agent/oneshot/
provider.rs

1use core::fmt;
2use std::any::type_name;
3use std::cell::RefCell;
4use std::rc::Rc;
5
6use gloo_worker::{Bincode, Codec};
7use serde::{Deserialize, Serialize};
8use yew::prelude::*;
9
10use super::{Oneshot, OneshotBridge, OneshotSpawner};
11use crate::Reach;
12use crate::utils::get_next_id;
13use crate::worker::WorkerProviderProps;
14
15pub(crate) struct OneshotProviderState<T>
16where
17    T: Oneshot + 'static,
18{
19    id: usize,
20    spawn_bridge_fn: Rc<dyn Fn() -> OneshotBridge<T>>,
21    reach: Reach,
22    held_bridge: Rc<RefCell<Option<OneshotBridge<T>>>>,
23}
24
25impl<T> fmt::Debug for OneshotProviderState<T>
26where
27    T: Oneshot,
28{
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        f.debug_struct(type_name::<Self>()).finish_non_exhaustive()
31    }
32}
33
34impl<T> OneshotProviderState<T>
35where
36    T: Oneshot,
37{
38    fn get_held_bridge(&self) -> OneshotBridge<T> {
39        let mut held_bridge = self.held_bridge.borrow_mut();
40
41        match held_bridge.as_mut() {
42            Some(m) => m.fork(),
43            None => {
44                let bridge = (self.spawn_bridge_fn)();
45                *held_bridge = Some(bridge.fork());
46                bridge
47            }
48        }
49    }
50
51    /// Creates a bridge, uses "fork" for public agents.
52    pub fn create_bridge(&self) -> OneshotBridge<T> {
53        match self.reach {
54            Reach::Public => {
55                let held_bridge = self.get_held_bridge();
56                held_bridge.fork()
57            }
58            Reach::Private => (self.spawn_bridge_fn)(),
59        }
60    }
61}
62
63impl<T> Clone for OneshotProviderState<T>
64where
65    T: Oneshot,
66{
67    fn clone(&self) -> Self {
68        Self {
69            id: self.id,
70            spawn_bridge_fn: self.spawn_bridge_fn.clone(),
71            reach: self.reach,
72            held_bridge: self.held_bridge.clone(),
73        }
74    }
75}
76
77impl<T> PartialEq for OneshotProviderState<T>
78where
79    T: Oneshot,
80{
81    fn eq(&self, rhs: &Self) -> bool {
82        self.id == rhs.id
83    }
84}
85
86/// The Oneshot Agent Provider.
87///
88/// This component provides its children access to an oneshot agent.
89#[component]
90pub fn OneshotProvider<T, C = Bincode>(props: &WorkerProviderProps) -> Html
91where
92    T: Oneshot<Input: Serialize + for<'de> Deserialize<'de> + 'static>
93        + Future<Output: Serialize + for<'de> Deserialize<'de> + 'static>
94        + 'static,
95    C: Codec + 'static,
96{
97    let WorkerProviderProps {
98        children,
99        path,
100        lazy,
101        module,
102        reach,
103    } = props.clone();
104
105    // Creates a spawning function so Codec is can be erased from contexts.
106    let spawn_bridge_fn: Rc<dyn Fn() -> OneshotBridge<T>> = {
107        let path = path.clone();
108        Rc::new(move || {
109            OneshotSpawner::<T>::new()
110                .as_module(module)
111                .encoding::<C>()
112                .spawn(&path)
113        })
114    };
115
116    let state = {
117        use_memo((path, lazy, reach), move |(_path, lazy, reach)| {
118            let state = OneshotProviderState::<T> {
119                id: get_next_id(),
120                spawn_bridge_fn,
121                reach: *reach,
122                held_bridge: Rc::default(),
123            };
124
125            if *reach == Reach::Public && !*lazy {
126                state.get_held_bridge();
127            }
128            state
129        })
130    };
131
132    html! {
133        <ContextProvider<OneshotProviderState<T>> context={(*state).clone()}>
134            {children}
135        </ContextProvider<OneshotProviderState<T>>>
136    }
137}