media_remote/high_level/
subscription.rs

1use std::{
2    collections::HashMap,
3    sync::{
4        atomic::{AtomicU64, Ordering},
5        Arc, Mutex, RwLockReadGuard,
6    },
7};
8
9use crate::NowPlayingInfo;
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash)]
12pub struct ListenerToken(u64);
13
14pub trait Subscription {
15    fn get_info(&self) -> RwLockReadGuard<'_, Option<NowPlayingInfo>>;
16
17    fn get_token_counter(&self) -> Arc<AtomicU64>;
18
19    fn get_listeners(
20        &self,
21    ) -> Arc<
22        Mutex<
23            HashMap<
24                ListenerToken,
25                Box<dyn Fn(RwLockReadGuard<'_, Option<NowPlayingInfo>>) + Send + Sync>,
26            >,
27        >,
28    >;
29
30    /// Subscribes a listener to receive updates when the "Now Playing" information changes.
31    ///
32    /// # Arguments
33    /// - `listener`: A function or closure that accepts a `RwLockReadGuard<'_, Option<NowPlayingInfo>>`.
34    ///   The function will be invoked with the current "Now Playing" info whenever the data is updated.
35    ///
36    /// # Returns
37    /// - `ListenerToken`: A token representing the listener, which can later be used to unsubscribe.
38    ///
39    /// # Example
40    /// ```rust
41    /// use media_remote::prelude::*;
42    ///
43    /// let now_playing: NowPlaying = NowPlaying::new();
44    ///
45    /// now_playing.subscribe(|guard| {
46    ///     let info = guard.as_ref();
47    ///     if let Some(info) = info {
48    ///         println!("Currently playing: {:?}", info.title);
49    ///     }    
50    /// });
51    /// ```
52    fn subscribe<F: Fn(RwLockReadGuard<'_, Option<NowPlayingInfo>>) + Send + Sync + 'static>(
53        &self,
54        listener: F,
55    ) -> ListenerToken {
56        listener(self.get_info());
57
58        let token = ListenerToken(self.get_token_counter().fetch_add(1, Ordering::Relaxed));
59
60        self.get_listeners()
61            .lock()
62            .unwrap()
63            .insert(token.clone(), Box::new(listener));
64
65        token
66    }
67
68    /// Unsubscribes a previously registered listener using the provided `ListenerToken`.
69    ///
70    ///
71    /// # Arguments
72    /// - `token`: The `ListenerToken` returned when the listener was subscribed. It is used to identify
73    ///   and remove the listener.
74    ///
75    /// # Example
76    /// ```rust
77    /// use media_remote::prelude::*;
78    ///
79    /// let now_playing: NowPlaying = NowPlaying::new();
80    ///
81    /// let token = now_playing.subscribe(|guard| {
82    ///     let info = guard.as_ref();
83    ///     if let Some(info) = info {
84    ///         println!("Currently playing: {:?}", info.title);
85    ///     }    
86    /// });
87    ///
88    /// now_playing.unsubscribe(token);
89    /// ```
90    fn unsubscribe(&self, token: ListenerToken) {
91        let binding = self.get_listeners();
92        let mut listeners = binding.lock().unwrap();
93        listeners.remove(&token);
94    }
95}