fx_callback/
lib.rs

1/*!
2![Build](https://github.com/yoep/fx-callback/workflows/Build/badge.svg)
3![Version](https://img.shields.io/github/v/tag/yoep/fx-callback?label=version)
4[![Crates](https://img.shields.io/crates/v/fx-callback)](https://crates.io/crates/fx-callback)
5[![License: Apache-2.0](https://img.shields.io/github/license/yoep/fx-callback)](./LICENSE)
6[![codecov](https://codecov.io/gh/yoep/fx-callback/branch/master/graph/badge.svg?token=A801IOOZAH)](https://codecov.io/gh/yoep/fx-callback)
7
8A subscription based callback for data events that might occur within one or more structs.
9It is mainly used within the FX landscape to allow events to be published between multiple structs.
10
11## Example
12
13```rust
14use fx_callback::{Callback, MultiThreadedCallback, Subscriber, Subscription};
15
16/// The events of the struct that informs subscribers about changes to the data within the struct.
17#[derive(Debug, Clone, PartialEq)]
18enum MyEvent {
19    Foo,
20}
21
22/// The struct to which an interested subscriber can subscribe to.
23#[derive(Debug)]
24struct Example {
25    callbacks: MultiThreadedCallback<MyEvent>,
26}
27
28impl Example {
29    fn invoke_event(&self) {
30        self.callbacks.invoke(MyEvent::Foo);
31    }
32}
33
34impl Callback<MyEvent> for Example {
35    fn subscribe(&self) -> Subscription<MyEvent> {
36        self.callbacks.subscribe()
37    }
38
39    fn subscribe_with(&self, subscriber: Subscriber<MyEvent>) {
40        self.callbacks.subscribe_with(subscriber)
41    }
42}
43```
44
45## Usage
46
47### Subscription/event holder
48
49To get started with adding callbacks to your structs, add one of the implementations of the `Callback` trait.
50Make sure that the struct implements the `Debug` trait.
51
52```rust
53use fx_callback::{Callback, MultiThreadedCallback};
54
55#[derive(Debug)]
56pub struct MyStruct {
57    callbacks: MultiThreadedCallback<MyEvent>,
58}
59```
60
61Add the `Callback` trait implementation to your struct to allow adding callbacks.
62
63```rust
64impl Callback<MyEvent> for MyStruct {
65    fn subscribe(&self) -> Subscription<MyEvent> {
66        self.callbacks.subscribe()
67    }
68
69    fn subscribe_with(&self, subscriber: Subscriber<MyEvent>) {
70        self.callbacks.subscribe_with(subscriber)
71    }
72}
73```
74
75When you want to inform subscribers about a certain event, call the `invoke` method.
76
77```rust
78impl MyStruct {
79    pub fn invoke_event(&self) {
80        self.callbacks.invoke(MyEvent::Foo);
81    }
82}
83```
84
85### Subscriber
86
87The interested subscriber can subscribe to the interested event of a struct that implements the `Callback` trait.
88
89```rust
90use fx_callback::{Callback, MultiThreadedCallback, Subscriber, Subscription};
91use tokio::runtime::Runtime;
92
93fn main() {
94    let runtime = Runtime::new().unwrap();
95    let struct_with_callback = MyStruct::new();
96
97    let mut receiver = struct_with_callback.subscribe();
98    runtime.spawn(async move {
99       loop {
100           if let Some(event) = receiver.recv().await {
101               println!("Received event: {}", event);
102           } else {
103               break;
104           }
105       }
106    });
107
108    struct_with_callback.invoke_event();
109}
110```
111*/
112
113#[doc(inline)]
114pub use callback::*;
115
116mod callback;
117
118#[cfg(test)]
119pub(crate) mod tests {
120    use log::LevelFilter;
121    use log4rs::append::console::ConsoleAppender;
122    use log4rs::config::{Appender, Root};
123    use log4rs::encode::pattern::PatternEncoder;
124    use log4rs::Config;
125    use std::sync::Once;
126
127    static INIT: Once = Once::new();
128
129    /// Initializes the logger with the specified log level.
130    #[macro_export]
131    macro_rules! init_logger {
132        ($level:expr) => {
133            crate::tests::init_logger_level($level)
134        };
135        () => {
136            crate::tests::init_logger_level(log::LevelFilter::Trace)
137        };
138    }
139
140    /// Initializes the logger with the specified log level.
141    pub(crate) fn init_logger_level(level: LevelFilter) {
142        INIT.call_once(|| {
143            log4rs::init_config(Config::builder()
144                .appender(Appender::builder().build("stdout", Box::new(ConsoleAppender::builder()
145                    .encoder(Box::new(PatternEncoder::new("\x1B[37m{d(%Y-%m-%d %H:%M:%S%.3f)}\x1B[0m {h({l:>5.5})} \x1B[35m{I:>6.6}\x1B[0m \x1B[37m---\x1B[0m \x1B[37m[{T:>15.15}]\x1B[0m \x1B[36m{t:<60.60}\x1B[0m \x1B[37m:\x1B[0m {m}{n}")))
146                    .build())))
147                .build(Root::builder().appender("stdout").build(level))
148                .unwrap())
149                .unwrap();
150        })
151    }
152}