stanza_matcher.rs

  1use bevy_app::{App, Plugin, Startup};
  2use bevy_ecs::message::Message;
  3use bevy_ecs::prelude::Messages;
  4use bevy_ecs::resource::Resource;
  5use bevy_ecs::world::World;
  6use tokio_xmpp::Stanza;
  7
  8use crate::component::system::spawn_stanza_workers;
  9
 10pub trait StanzaMatcher: Send + Sync + 'static {
 11    type Message: Message;
 12    fn matches(&mut self, candidate: &Stanza) -> Option<Self::Message>;
 13}
 14
 15// =============================================================================
 16// HList Structure
 17// =============================================================================
 18
 19pub struct HNil;
 20
 21pub struct HCons<Head, Tail> {
 22    pub head: Head,
 23    pub tail: Tail,
 24}
 25
 26pub struct Matcher<M>(pub M);
 27
 28// =============================================================================
 29// Dispatch Trait
 30// =============================================================================
 31
 32pub trait StanzaDispatch: Send + Sync + 'static {
 33    fn dispatch(&mut self, stanza: &Stanza, world: &mut World);
 34    fn register(app: &mut App);
 35}
 36
 37impl StanzaDispatch for HNil {
 38    fn dispatch(&mut self, _stanza: &Stanza, _world: &mut World) {}
 39    fn register(_app: &mut App) {}
 40}
 41
 42impl<M, Tail> StanzaDispatch for HCons<Matcher<M>, Tail>
 43where
 44    M: StanzaMatcher,
 45    Tail: StanzaDispatch,
 46{
 47    fn dispatch(&mut self, stanza: &Stanza, world: &mut World) {
 48        if let Some(msg) = self.head.0.matches(stanza) {
 49            world.resource_mut::<Messages<M::Message>>().write(msg);
 50            return;
 51        }
 52        self.tail.dispatch(stanza, world)
 53    }
 54
 55    fn register(app: &mut App) {
 56        app.add_message::<M::Message>();
 57        Tail::register(app);
 58    }
 59}
 60
 61// =============================================================================
 62// Builder API
 63// =============================================================================
 64
 65impl HNil {
 66    pub fn matcher<M: StanzaMatcher>(self, m: M) -> HCons<Matcher<M>, HNil> {
 67        HCons {
 68            head: Matcher(m),
 69            tail: HNil,
 70        }
 71    }
 72}
 73
 74impl<Head, Tail> HCons<Head, Tail> {
 75    pub fn matcher<M: StanzaMatcher>(self, m: M) -> HCons<Matcher<M>, Self> {
 76        HCons {
 77            head: Matcher(m),
 78            tail: self,
 79        }
 80    }
 81}
 82
 83// =============================================================================
 84// Resource Wrapper
 85// =============================================================================
 86
 87#[derive(Resource)]
 88pub struct MatcherRegistry<D: StanzaDispatch> {
 89    pub dispatchers: D,
 90}
 91
 92// =============================================================================
 93// Plugin
 94// =============================================================================
 95
 96pub struct StanzaMatcherPlugin<D> {
 97    dispatchers: D,
 98}
 99
100impl StanzaMatcherPlugin<HNil> {
101    pub fn new() -> Self {
102        StanzaMatcherPlugin { dispatchers: HNil }
103    }
104}
105
106impl Default for StanzaMatcherPlugin<HNil> {
107    fn default() -> Self {
108        Self::new()
109    }
110}
111
112impl<D> StanzaMatcherPlugin<D> {
113    pub fn matcher<M: StanzaMatcher>(self, m: M) -> StanzaMatcherPlugin<HCons<Matcher<M>, D>> {
114        StanzaMatcherPlugin {
115            dispatchers: HCons {
116                head: Matcher(m),
117                tail: self.dispatchers,
118            },
119        }
120    }
121}
122
123impl<D: StanzaDispatch + Clone> Plugin for StanzaMatcherPlugin<D> {
124    fn build(&self, app: &mut App) {
125        D::register(app);
126        app.insert_resource(MatcherRegistry {
127            dispatchers: self.dispatchers.clone(),
128        });
129        app.add_systems(Startup, spawn_stanza_workers::<D>);
130    }
131}
132
133// =============================================================================
134// Clone Impls
135// =============================================================================
136
137impl Clone for HNil {
138    fn clone(&self) -> Self {
139        HNil
140    }
141}
142
143impl<M: Clone> Clone for Matcher<M> {
144    fn clone(&self) -> Self {
145        Matcher(self.0.clone())
146    }
147}
148
149impl<Head: Clone, Tail: Clone> Clone for HCons<Head, Tail> {
150    fn clone(&self) -> Self {
151        HCons {
152            head: self.head.clone(),
153            tail: self.tail.clone(),
154        }
155    }
156}