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