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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
mod activity_container;
mod lifecycle;

pub(crate) use activity_container::*;
pub use lifecycle::*;

use crate::nut::iac::{filter::SubscriptionFilter, managed_state::DomainId};
use crate::nut::Handler;
use crate::*;
use core::any::Any;
use std::collections::HashMap;
use std::ops::{Index, IndexMut};

// @ START-DOC ACTIVITY
/// Activities are at the core of Nuts.
/// From the globally managed data, they represent the active part, i.e. they can have event listeners.
/// The passive counter-part is defined by `DomainState`.
///
/// Every struct that has a type with static lifetime (anything that has no lifetime parameter that is determined only at runtime) can be used as an Activity.
/// You don't have to implement the `Activity` trait yourself, it will always be automatically derived if possible.
///
/// To create an activity, simply register the object that should be used as activity, using `nuts::new_activity` or one of its variants.
// @ END-DOC ACTIVITY
pub trait Activity: Any {}
impl<T: Any> Activity for T {}

#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
/// Handle to an `Activity` that has been registered, with a type parameter to track the activity's type.
/// Can be used to add type-checked closures to the activity, which will be used as event listeners.
///
/// Implements `Copy` and `Clone`
pub struct ActivityId<A> {
    pub(crate) id: UncheckedActivityId,
    pub(crate) domain_index: DomainId,
    phantom: std::marker::PhantomData<A>,
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
/// Pointer to an activity that has been registered.
/// Can be used to set the lifecycle stats of activities.
///
/// The information about the activity's type is lost at this point.
/// Therefore, this id cannot be used to register closures.
pub struct UncheckedActivityId {
    index: usize,
}

impl<A: Activity> ActivityId<A> {
    pub(crate) fn new(index: usize, domain_index: DomainId) -> Self {
        Self {
            id: UncheckedActivityId { index },
            domain_index,
            phantom: Default::default(),
        }
    }
    /// Registers a callback closure that is called when an activity changes from inactive to active.
    pub fn on_enter<F>(&self, f: F)
    where
        F: Fn(&mut A) + 'static,
    {
        crate::nut::register_no_payload(*self, f, Topic::enter(), SubscriptionFilter::no_filter())
    }
    /// Same as `on_enter` but with domain access in closure
    pub fn on_enter_domained<F>(&self, f: F)
    where
        F: Fn(&mut A, &mut DomainState) + 'static,
    {
        crate::nut::register_domained_no_payload(
            *self,
            f,
            Topic::enter(),
            SubscriptionFilter::no_filter(),
        )
    }
    /// Registers a callback closure that is called when an activity changes from active to inactive.
    pub fn on_leave<F>(&self, f: F)
    where
        F: Fn(&mut A) + 'static,
    {
        crate::nut::register_no_payload(*self, f, Topic::leave(), SubscriptionFilter::no_filter())
    }
    /// Same as `on_leave` but with domain access in closure
    pub fn on_leave_domained<F>(&self, f: F)
    where
        F: Fn(&mut A, &mut DomainState) + 'static,
    {
        crate::nut::register_domained_no_payload(
            *self,
            f,
            Topic::leave(),
            SubscriptionFilter::no_filter(),
        )
    }
    /// Registers a callback closure on an activity with a specific topic to listen to.
    ///
    /// By default, the activity will only receive calls when it is active.
    /// Use `subscribe_masked` for more control over this behavior.
    ///
    /// ### Example
    // @ START-DOC SUBSCRIBE_EXAMPLE
    /// ```rust
    /// struct MyActivity { id: usize };
    /// struct MyMessage { text: String };
    ///
    /// pub fn main() {
    ///     let activity = nuts::new_activity(MyActivity { id: 0 } );
    ///     activity.subscribe(
    ///         |activity: &mut MyActivity, message: &MyMessage|
    ///         println!("Subscriber with ID {} received text: {}", activity.id, message.text)
    ///     );
    /// }
    /// ```
    /// In the example above, a subscription is created that waits for messages of type `MyMessage` to be published.
    /// So far, the code inside the closure is not executed and nothing is printed to the console.
    ///
    /// Note that the first argument of the closure is a mutable reference to the activity object.
    /// The second argument is a read-only reference to the published message.
    /// Both types must match exactly or otherwise the closure will either not be accepted by the compiler.
    ///
    /// A function with the correct argument types can also be used to subscribe.
    /// ```rust
    /// struct MyActivity { id: usize };
    /// struct MyMessage { text: String };
    ///
    /// pub fn main() {
    ///     let activity = nuts::new_activity(MyActivity { id: 0 } );
    ///     activity.subscribe(MyActivity::print_text);
    /// }
    ///
    /// impl MyActivity {
    ///     fn print_text(&mut self, message: &MyMessage) {
    ///         println!("Subscriber with ID {} received text: {}", self.id, message.text)
    ///     }
    /// }
    /// ```
    // @ END-DOC SUBSCRIBE_EXAMPLE
    pub fn subscribe<F, MSG>(&self, f: F)
    where
        F: Fn(&mut A, &MSG) + 'static,
        MSG: Any,
    {
        crate::nut::register(*self, f, Default::default())
    }
    /// Same as [subscribe](#method.subscribe) but gives mutable access to the message object.
    pub fn subscribe_mut<F, MSG>(&self, f: F)
    where
        F: Fn(&mut A, &mut MSG) + 'static,
        MSG: Any,
    {
        crate::nut::register_mut(*self, f, Default::default())
    }
    /// Registers a callback closure on an activity with a specific topic to listen to.
    /// This variant takes ownership of the message.
    /// Only subscription per type is allowed. Othwerise, a pnic will occur when publishing.
    pub fn subscribe_owned<F, MSG>(&self, f: F)
    where
        F: Fn(&mut A, MSG) + 'static,
        MSG: Any,
    {
        crate::nut::register_owned(*self, f, Default::default())
    }

    /// Registers a callback closure on an activity with a specific topic to listen to.
    /// Has mutable access to the `DomainState` object.
    ///
    /// By default, the activity will only receive calls when it is active.
    /// Use `subscribe_domained_masked` for more control over this behavior.
    ///
    /// # Panics
    /// Panics if the activity has not been registered with a domain.    
    pub fn subscribe_domained<F, MSG>(&self, f: F)
    where
        F: Fn(&mut A, &mut DomainState, &MSG) + 'static,
        MSG: Any,
    {
        crate::nut::register_domained(*self, f, Default::default())
    }
    /// Same as [`subscribe_domained`](#method.subscribe_domained) but gives mutable access to the message object.
    pub fn subscribe_domained_mut<F, MSG>(&self, f: F)
    where
        F: Fn(&mut A, &mut DomainState, &mut MSG) + 'static,
        MSG: Any,
    {
        crate::nut::register_domained_mut(*self, f, Default::default())
    }
    /// Registers a callback closure on an activity with a specific topic to listen to and access to the domain.
    /// This variant takes ownership of the message.
    /// Only subscription per type is allowed. Otherwise, a panic will occur when publishing.
    pub fn subscribe_domained_owned<F, MSG>(&self, f: F)
    where
        F: Fn(&mut A, &mut DomainState, MSG) + 'static,
        MSG: Any,
    {
        crate::nut::register_domained_owned(*self, f, Default::default())
    }

    /// Registers a callback closure on an activity with a specific topic to listen to with filtering options.
    pub fn subscribe_masked<F, MSG>(&self, mask: SubscriptionFilter, f: F)
    where
        F: Fn(&mut A, &MSG) + 'static,
        MSG: Any,
    {
        crate::nut::register(*self, f, mask)
    }
    /// Same as [`subscribe_masked`](#method.subscribe_masked) but gives mutable access to the message object.
    pub fn subscribe_masked_mut<F, MSG>(&self, mask: SubscriptionFilter, f: F)
    where
        F: Fn(&mut A, &mut MSG) + 'static,
        MSG: Any,
    {
        crate::nut::register_mut(*self, f, mask)
    }

    /// Registers a callback closure on an activity with a specific topic to listen to with filtering options.
    /// Has mutable access to the `DomainState` object.
    ///
    /// # Panics
    /// Panics if the activity has not been registered with a domain.
    pub fn subscribe_domained_masked<F, MSG>(&self, mask: SubscriptionFilter, f: F)
    where
        F: Fn(&mut A, &mut DomainState, &MSG) + 'static,
        MSG: Any,
    {
        crate::nut::register_domained(*self, f, mask)
    }
    /// Same as [`subscribe_domained_masked`](#method.subscribe_domained_masked) but gives mutable access to the message object.
    pub fn subscribe_domained_masked_mut<F, MSG>(&self, mask: SubscriptionFilter, f: F)
    where
        F: Fn(&mut A, &mut DomainState, &mut MSG) + 'static,
        MSG: Any,
    {
        crate::nut::register_domained_mut(*self, f, mask)
    }

    /// Changes the lifecycle status of the activity
    pub fn set_status(&self, status: LifecycleStatus) {
        crate::nut::set_status((*self).into(), status);
    }
}

impl UncheckedActivityId {
    /// Changes the lifecycle status of the activity
    pub fn set_status(&self, status: LifecycleStatus) {
        crate::nut::set_status(*self, status);
    }
}

impl<A> Copy for ActivityId<A> {}
impl<A> Clone for ActivityId<A> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<A> Into<UncheckedActivityId> for ActivityId<A> {
    fn into(self) -> UncheckedActivityId {
        self.id
    }
}