kaspa_notify/
notification.rs

1use crate::subscription::context::SubscriptionContext;
2
3use super::{
4    events::EventType,
5    subscription::{
6        single::{OverallSubscription, UtxosChangedSubscription, VirtualChainChangedSubscription},
7        Single,
8    },
9};
10use std::fmt::{Debug, Display};
11
12/// A notification, usable throughout the full notification system via types implementing this trait
13pub trait Notification: Clone + Debug + Display + Send + Sync + 'static {
14    fn apply_overall_subscription(&self, subscription: &OverallSubscription, context: &SubscriptionContext) -> Option<Self>;
15
16    fn apply_virtual_chain_changed_subscription(
17        &self,
18        subscription: &VirtualChainChangedSubscription,
19        context: &SubscriptionContext,
20    ) -> Option<Self>;
21
22    fn apply_utxos_changed_subscription(&self, subscription: &UtxosChangedSubscription, context: &SubscriptionContext)
23        -> Option<Self>;
24
25    fn apply_subscription(&self, subscription: &dyn Single, context: &SubscriptionContext) -> Option<Self> {
26        match subscription.event_type() {
27            EventType::VirtualChainChanged => self.apply_virtual_chain_changed_subscription(
28                subscription.as_any().downcast_ref::<VirtualChainChangedSubscription>().unwrap(),
29                context,
30            ),
31            EventType::UtxosChanged => self
32                .apply_utxos_changed_subscription(subscription.as_any().downcast_ref::<UtxosChangedSubscription>().unwrap(), context),
33            _ => self.apply_overall_subscription(subscription.as_any().downcast_ref::<OverallSubscription>().unwrap(), context),
34        }
35    }
36
37    fn event_type(&self) -> EventType;
38}
39
40#[macro_export]
41macro_rules! full_featured {
42    ($(#[$meta:meta])* $vis:vis enum $name:ident {
43    $($(#[$variant_meta:meta])* $variant_name:ident($field_name:path),)*
44    }) => {
45        paste::paste! {
46        $(#[$meta])*
47        $vis enum $name {
48            $($(#[$variant_meta])* $variant_name($field_name)),*
49        }
50
51        impl std::convert::From<&$name> for kaspa_notify::events::EventType {
52            fn from(value: &$name) -> Self {
53                match value {
54                    $($name::$variant_name(_) => kaspa_notify::events::EventType::$variant_name),*
55                }
56            }
57        }
58
59        impl std::convert::From<&$name> for kaspa_notify::scope::Scope {
60            fn from(value: &$name) -> Self {
61                match value {
62                    $($name::$variant_name(_) => kaspa_notify::scope::Scope::$variant_name(kaspa_notify::scope::[<$variant_name Scope>]::default())),*
63                }
64            }
65        }
66
67        impl AsRef<$name> for $name {
68            fn as_ref(&self) -> &Self {
69                self
70            }
71        }
72    }
73    }
74}
75
76pub use full_featured;
77
78pub mod test_helpers {
79    use crate::subscription::{context::SubscriptionContext, Subscription};
80
81    use super::*;
82    use derive_more::Display;
83    use kaspa_addresses::Address;
84    use kaspa_core::trace;
85    use std::sync::Arc;
86
87    #[derive(Clone, Debug, Default, PartialEq, Eq)]
88    pub struct BlockAddedNotification {
89        pub data: u64,
90    }
91
92    #[derive(Clone, Debug, Default, PartialEq, Eq)]
93    pub struct VirtualChainChangedNotification {
94        pub data: u64,
95        pub accepted_transaction_ids: Option<u64>,
96    }
97
98    #[derive(Clone, Debug, Default, PartialEq, Eq)]
99    pub struct UtxosChangedNotification {
100        pub data: u64,
101        pub addresses: Arc<Vec<Address>>,
102    }
103
104    full_featured! {
105    #[derive(Clone, Debug, Display, PartialEq, Eq)]
106    pub enum TestNotification {
107        #[display(fmt = "BlockAdded #{}", "_0.data")]
108        BlockAdded(BlockAddedNotification),
109        #[display(fmt = "VirtualChainChanged #{}", "_0.data")]
110        VirtualChainChanged(VirtualChainChangedNotification),
111        #[display(fmt = "UtxosChanged #{}", "_0.data")]
112        UtxosChanged(UtxosChangedNotification),
113    }
114    }
115
116    impl Notification for TestNotification {
117        fn apply_overall_subscription(&self, subscription: &OverallSubscription, _: &SubscriptionContext) -> Option<Self> {
118            trace!("apply_overall_subscription: {self:?}, {subscription:?}");
119            match subscription.active() {
120                true => Some(self.clone()),
121                false => None,
122            }
123        }
124
125        fn apply_virtual_chain_changed_subscription(
126            &self,
127            subscription: &VirtualChainChangedSubscription,
128            _: &SubscriptionContext,
129        ) -> Option<Self> {
130            match subscription.active() {
131                true => {
132                    if let TestNotification::VirtualChainChanged(ref payload) = self {
133                        if !subscription.include_accepted_transaction_ids() && payload.accepted_transaction_ids.is_some() {
134                            return Some(TestNotification::VirtualChainChanged(VirtualChainChangedNotification {
135                                data: payload.data,
136                                accepted_transaction_ids: None,
137                            }));
138                        }
139                    }
140                    Some(self.clone())
141                }
142                false => None,
143            }
144        }
145
146        fn apply_utxos_changed_subscription(
147            &self,
148            subscription: &UtxosChangedSubscription,
149            context: &SubscriptionContext,
150        ) -> Option<Self> {
151            match subscription.active() {
152                true => {
153                    if let TestNotification::UtxosChanged(ref payload) = self {
154                        let subscription = subscription.data();
155                        if !subscription.to_all() {
156                            // trace!("apply_utxos_changed_subscription: Notification payload {:?}", payload);
157                            // trace!("apply_utxos_changed_subscription: Subscription content {:?}", subscription);
158                            // trace!("apply_utxos_changed_subscription: Subscription Context {}", context.address_tracker);
159                            let addresses = payload
160                                .addresses
161                                .iter()
162                                .filter(|x| subscription.contains_address(x, context))
163                                .cloned()
164                                .collect::<Vec<_>>();
165                            if !addresses.is_empty() {
166                                return Some(TestNotification::UtxosChanged(UtxosChangedNotification {
167                                    data: payload.data,
168                                    addresses: Arc::new(addresses),
169                                }));
170                            } else {
171                                return None;
172                            }
173                        }
174                    }
175                    Some(self.clone())
176                }
177                false => None,
178            }
179        }
180
181        fn event_type(&self) -> EventType {
182            self.into()
183        }
184    }
185
186    /// A trait to help tests match notification received and expected thanks to some predefined data
187    pub trait Data {
188        fn data(&self) -> u64;
189        fn data_mut(&mut self) -> &mut u64;
190    }
191    impl Data for BlockAddedNotification {
192        fn data(&self) -> u64 {
193            self.data
194        }
195
196        fn data_mut(&mut self) -> &mut u64 {
197            &mut self.data
198        }
199    }
200    impl Data for VirtualChainChangedNotification {
201        fn data(&self) -> u64 {
202            self.data
203        }
204
205        fn data_mut(&mut self) -> &mut u64 {
206            &mut self.data
207        }
208    }
209    impl Data for UtxosChangedNotification {
210        fn data(&self) -> u64 {
211            self.data
212        }
213
214        fn data_mut(&mut self) -> &mut u64 {
215            &mut self.data
216        }
217    }
218    impl Data for TestNotification {
219        fn data(&self) -> u64 {
220            match self {
221                TestNotification::BlockAdded(n) => n.data(),
222                TestNotification::VirtualChainChanged(n) => n.data(),
223                TestNotification::UtxosChanged(n) => n.data(),
224            }
225        }
226
227        fn data_mut(&mut self) -> &mut u64 {
228            match self {
229                TestNotification::BlockAdded(n) => n.data_mut(),
230                TestNotification::VirtualChainChanged(n) => n.data_mut(),
231                TestNotification::UtxosChanged(n) => n.data_mut(),
232            }
233        }
234    }
235}