1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use super::{Context, Msg, Widget, WidgetRuntime};
use anyhow::{anyhow, Error};
use std::collections::HashMap;
use std::hash::Hash;
use std::rc::Rc;
use yew::worker::Agent;
use yew::{Bridge, Bridged, ComponentLink};

/// This `Hook` technique is used to resolve conflicing inmplementations
/// of `OnWireEvent` and allow to have multiple implementation of that trait.
pub trait AgentHook: 'static {
    type Agent: Agent;
}

impl<T: Agent> AgentHook for T {
    // TODO: Maybe use `Output` here?
    type Agent = Self;
}

pub trait OnWireEvent<H: AgentHook>: Widget {
    fn on_wire(
        &mut self,
        _tag: &Self::Tag,
        _response: <H::Agent as Agent>::Output,
        _ctx: &mut Context<Self>,
    ) -> Result<(), Error> {
        let self_type_name = std::any::type_name::<Self>();
        let type_name = std::any::type_name::<H::Agent>();
        Err(anyhow!(
            "No implementation for incoming event from the agent: {} of {}.",
            type_name,
            self_type_name
        ))
    }
}

pub type WiredHandler<T, E, W> =
    &'static dyn Fn(&mut W, &T, E, &mut Context<W>) -> Result<(), Error>;

pub struct TagRegistry<TAG, ID> {
    // TODO: Use more effective bi-deirctional map here
    tag_to_id: HashMap<Rc<TAG>, ID>,
    id_to_tag: HashMap<ID, Rc<TAG>>,
}

impl<TAG, ID> Default for TagRegistry<TAG, ID> {
    fn default() -> Self {
        Self {
            tag_to_id: HashMap::new(),
            id_to_tag: HashMap::new(),
        }
    }
}

impl<TAG, ID> TagRegistry<TAG, ID>
where
    TAG: Eq + Hash,
    ID: Clone + Eq + Hash,
{
    pub fn insert(&mut self, tag: TAG, id: ID) {
        let tag = Rc::new(tag);
        self.tag_to_id.insert(tag.clone(), id.clone());
        self.id_to_tag.insert(id, tag);
    }

    pub fn remove(&mut self, id: &ID) {
        if let Some(tag) = self.id_to_tag.remove(id) {
            self.tag_to_id.remove(&tag);
        }
    }

    pub fn get(&self, tag: &TAG) -> Option<&ID> {
        self.tag_to_id.get(tag)
    }

    pub fn tag(&self, id: &ID) -> Option<Rc<TAG>> {
        self.id_to_tag.get(id).cloned()
    }

    pub fn all_tags(&self) -> Vec<Rc<TAG>> {
        self.tag_to_id.keys().cloned().collect()
    }

    pub fn is_empty(&self) -> bool {
        self.tag_to_id.is_empty()
    }
}

pub struct WiredBridge<ID, T: Agent, W: Widget> {
    link: Option<Box<dyn Bridge<T>>>,
    // TODO: Join `handler` and `registry` and keep both under single `Option`
    // This filled only if subscribe method called
    handler: Option<WiredHandler<W::Tag, T::Output, W>>,
    registry: TagRegistry<W::Tag, ID>,
}

impl<ID, T: Agent, W: Widget> Default for WiredBridge<ID, T, W> {
    fn default() -> Self {
        Self {
            link: None,
            handler: None,
            registry: TagRegistry::default(),
        }
    }
}

impl<ID, T: Agent, W: Widget> WiredBridge<ID, T, W> {
    pub fn is_linked(&self) -> bool {
        self.link.is_some()
    }

    pub fn handler(&self) -> Option<WiredHandler<W::Tag, T::Output, W>> {
        self.handler
    }

    pub fn registry(&mut self) -> &mut TagRegistry<W::Tag, ID> {
        &mut self.registry
    }
}

impl<ID, T: Agent, W: Widget> WiredBridge<ID, T, W> {
    pub fn activate_link(&mut self, widget_link: &ComponentLink<WidgetRuntime<W>>)
    where
        Msg<W>: From<T::Output>,
    {
        if self.link.is_none() {
            let callback = widget_link.callback(Msg::from);
            let link = T::bridge(callback);
            self.link = Some(link);
        }
    }

    pub fn activate_handler<H>(&mut self)
    where
        H: AgentHook<Agent = T>,
        W: OnWireEvent<H>,
    {
        if self.handler.is_none() {
            let handler = &<W as OnWireEvent<H>>::on_wire;
            self.handler = Some(handler);
        }
    }

    pub fn get_mut_linked<H>(
        &mut self,
        widget_link: &ComponentLink<WidgetRuntime<W>>,
    ) -> (&mut dyn Bridge<T>, &mut TagRegistry<W::Tag, ID>)
    where
        H: AgentHook<Agent = T>,
        Msg<W>: From<T::Output>,
        W: OnWireEvent<H>,
    {
        self.activate_link(widget_link);
        self.activate_handler();
        let bridge = self.link.as_deref_mut().unwrap();
        (bridge, &mut self.registry)
    }
}