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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
mod activity_container;
mod lifecycle;

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

use crate::nut::iac::{filter::SubscriptionFilter, managed_state::DomainId};
use crate::*;
use core::any::Any;
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.
///
/// It is important to understand that Activities are uniquely defined by their type.
/// You cannot create two activities from the same type. (But you can, for example, create a wrapper type around it.)
/// This allows activities to be referenced by their type, which mus be know at run-time. reference by their type
// @ 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 {
    pub(crate) 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.
    /// Multiple handlers can be registered.
    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.
    /// Multiple handlers can be registered.
    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 that is called when an activity is deleted.
    /// Only one handler can be registered because it takes ownership of the data.
    /// A second registration will overwrite the first handler.
    pub fn on_delete<F>(&self, f: F)
    where
        F: FnOnce(A) + 'static,
    {
        crate::nut::register_on_delete(*self, f);
    }
    /// Same as `on_delete` but with domain access in closure
    pub fn on_delete_domained<F>(&self, f: F)
    where
        F: FnOnce(A, &mut DomainState) + 'static,
    {
        crate::nut::register_domained_on_delete(*self, f);
    }

    /// 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 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.
    ///
    /// Make sure to use the correct signature for the function, the Rust compiler may give strange error messages otherwise.
    /// For example, the message must be borrowed by the subscription handler.
    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.
    /// 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.
    ///
    /// Make sure to use the correct signature for the function, the Rust compiler may give strange error messages otherwise.
    /// For example, the message must be borrowed by the subscription handler.
    ///
    /// # 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.
    /// Messages sent with `nuts::publish()` are NOT received, only messages sent with `nuts::send_to()`.
    ///
    /// Attention! The handler takes ownership of the message.
    /// It will compile if it is borrowed instead, but then it will also expect a reference to be published. (Which usually doesn't work due to lifetimes)
    /// Then, it will not react to normally sent messages and can be difficult to debug.
    ///
    /// Since the listener takes ownership, it is not possible to have more than one private channel active for the same activity at the same time.
    /// If multiple private channels are added to an activity, only the last listener is retained. (Older ones are replaced and deleted)
    pub fn private_channel<F, MSG>(&self, f: F)
    where
        F: Fn(&mut A, MSG) + 'static,
        MSG: Any,
    {
        crate::nut::register_owned(*self, f, Default::default())
    }

    /// Variant of `private_channel` with access to the domain state.
    ///
    /// # Panics
    /// Panics if the activity has not been registered with a domain.   
    pub fn private_domained_channel<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
    ///
    /// # Panics
    /// If status is set to something other than Deleted after it has been Deleted
    pub fn set_status(&self, status: LifecycleStatus) {
        crate::nut::set_status((*self).into(), status);
    }

    /// Publish a message to a specific activity.
    ///
    /// If you lack access to an `ActivityId`, use `nuts::send_to()` or `UncheckedActivityId::private_message`.
    /// Both are equivalent.
    pub fn private_message<MSG: Any>(&self, msg: MSG) {
        let id: UncheckedActivityId = (*self).into();
        id.private_message(msg);
    }
}

impl UncheckedActivityId {
    /// Changes the lifecycle status of the activity
    ///
    /// # Panics
    /// If status is set to something other than Deleted after it has been Deleted
    pub fn set_status(&self, status: LifecycleStatus) {
        crate::nut::set_status(*self, status);
    }
    /// Publish a message to a specific activity.
    ///
    /// If you lack access to an `UncheckedActivityId`, use `nuts::send_to()`, it is equivalent.
    pub fn private_message<A: Any>(&self, msg: A) {
        nut::send_custom_by_id(msg, *self)
    }
    /// A unique number for the activity.
    /// Can be used for serialization in combination with `forge_from_usize`
    pub fn as_usize(&self) -> usize {
        self.index
    }
    /// Can be used for deserialization in combination with `as_usize`
    ///
    /// If used in any other way, you might experience panics.
    /// Right now, there should still be no UB but that might change in future versions.
    pub fn forge_from_usize(index: usize) -> Self {
        Self { index }
    }
}

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
    }
}