coc_rs/
events.rs

1use std::time::{Duration, Instant};
2
3use async_trait::async_trait;
4
5use crate::{
6    api::Client,
7    error::APIError,
8    models::{clan, player},
9    war::War,
10};
11
12#[async_trait]
13#[allow(unused_variables)]
14pub trait EventHandler {
15    async fn player(&self, old_player: Option<player::Player>, new_player: player::Player) {}
16    async fn clan(&self, old_clan: Option<clan::Clan>, new_clan: clan::Clan) {}
17    async fn war(&self, old_war: Option<War>, new_war: War) {}
18    async fn on_error(&self, error: APIError, tag: String, event_type: EventType);
19}
20
21#[derive(Debug)]
22pub struct EventsListenerBuilder {
23    event_type: Vec<EventType>,
24    client: Client,
25}
26
27#[derive(Debug, Clone)]
28pub enum EventType {
29    Player(String, Instant, Option<player::Player>),
30    Clan(String, Instant, Option<clan::Clan>),
31    War(String, Instant, Option<War>),
32}
33
34impl std::fmt::Display for EventType {
35    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        match self {
37            Self::Player(tag, _, _) => write!(f, "PlayerEvent({tag})"),
38            Self::Clan(tag, _, _) => write!(f, "ClanEvent({tag})"),
39            Self::War(tag, _, _) => write!(f, "WarEvent({tag})"),
40        }
41    }
42}
43
44impl EventsListenerBuilder {
45    #[must_use]
46    pub const fn new(client: Client) -> Self {
47        Self { event_type: vec![], client }
48    }
49
50    #[must_use]
51    pub fn add_clan(mut self, tag: &str) -> Self {
52        self.event_type.push(EventType::Clan(tag.to_string(), Instant::now(), None));
53        self
54    }
55
56    #[must_use]
57    pub fn add_player(mut self, tag: &str) -> Self {
58        self.event_type.push(EventType::Player(tag.to_string(), Instant::now(), None));
59        self
60    }
61
62    #[must_use]
63    pub fn add_war(mut self, tag: &str) -> Self {
64        self.event_type.push(EventType::War(tag.to_string(), Instant::now(), None));
65        self
66    }
67
68    #[must_use]
69    pub fn add_clans(mut self, tags: Vec<impl ToString>) -> Self {
70        // since add_clan takes self by value, we have to use a for loop
71        for tag in tags {
72            self.event_type.push(EventType::Clan(tag.to_string(), Instant::now(), None));
73        }
74        self
75    }
76
77    #[must_use]
78    pub fn add_players(mut self, tags: Vec<impl ToString>) -> Self {
79        // since add_player takes self by value, we have to use a for loop
80        for tag in tags {
81            self.event_type.push(EventType::Player(tag.to_string(), Instant::now(), None));
82        }
83
84        self
85    }
86
87    #[must_use]
88    pub fn add_wars(&mut self, tags: Vec<impl ToString>) -> &mut Self {
89        // since add_war takes self by value, we have to use a for loop
90        for tag in tags {
91            self.event_type.push(EventType::War(tag.to_string(), Instant::now(), None));
92        }
93        self
94    }
95
96    pub fn build<T>(self, handler: T) -> EventsListener<T>
97    where
98        T: EventHandler + Sync + Send,
99    {
100        EventsListener { event_type: self.event_type, client: self.client, handler }
101    }
102}
103
104pub struct EventsListener<T>
105where
106    T: EventHandler + Sync + Send,
107{
108    event_type: Vec<EventType>,
109    client: Client,
110    handler: T,
111}
112
113pub struct EventsError {
114    api_error: APIError,
115    tag: String,
116    event_type: EventType,
117    index: usize,
118}
119
120impl std::fmt::Display for EventsError {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        write!(f, "Error while handling event `{}`: [{}]", self.event_type, self.api_error)
123    }
124}
125
126impl std::fmt::Debug for EventsError {
127    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128        write!(f, "Error while handling event `{}`: [{}]", self.event_type, self.api_error)
129    }
130}
131
132impl std::error::Error for EventsError {}
133
134impl<T> EventsListener<T>
135where
136    T: EventHandler + Sync + Send,
137{
138    /// Start the events listener, note that if duration is None, it will run forever
139    ///
140    /// # Errors
141    ///
142    /// This function will return an error if [`Client::get_player`], [`Client::get_clan`], or [`Client::get_current_war`] fails
143    pub async fn start(mut self, duration: Option<Duration>) -> Result<(), EventsError> {
144        if let Some(duration) = duration {
145            let start = Instant::now();
146            while start.elapsed() < duration {
147                if let Err(e) = self.fire_events().await {
148                    self.event_type.remove(e.index);
149                    self.handler.on_error(e.api_error, e.tag, e.event_type).await;
150                };
151            }
152        } else {
153            loop {
154                if let Err(e) = self.fire_events().await {
155                    self.event_type.remove(e.index);
156                    self.handler.on_error(e.api_error, e.tag, e.event_type).await;
157                };
158            }
159        }
160
161        Ok(())
162    }
163
164    #[inline(always)]
165    const fn should_fire_again(duration: Duration, seconds: u64) -> bool {
166        duration.as_secs() >= seconds
167    }
168
169    /// Returns true if a new event was fired for any [`EventType`]
170    ///
171    /// # Errors
172    ///
173    /// This function will return an error if [`crate::api::Client::get_player`] [`crate::api::Client::get_clan`], or [`crate::api::Client::get_current_war`] fails
174    async fn fire_events(&mut self) -> Result<bool, EventsError> {
175        for (i, event) in self.event_type.iter().enumerate() {
176            match event {
177                EventType::Player(tag, last_fired, old) => {
178                    if let Some(duration) = Instant::now().checked_duration_since(*last_fired) {
179                        if Self::should_fire_again(duration, 10) {
180                            return match self.client.get_player(tag).await {
181                                Ok(new) => {
182                                    self.handler.player(old.clone(), new.clone()).await; // invoking the handler function the user defined
183                                    self.event_type[i] =
184                                        EventType::Player(tag.clone(), Instant::now(), Some(new));
185                                    Ok(true)
186                                }
187                                Err(err) => Err(EventsError {
188                                    api_error: err,
189                                    tag: tag.clone(),
190                                    event_type: event.clone(),
191                                    index: i,
192                                }),
193                            };
194                        }
195                    };
196                }
197                EventType::Clan(tag, last_fired, old) => {
198                    if let Some(duration) = Instant::now().checked_duration_since(*last_fired) {
199                        if Self::should_fire_again(duration, 10) {
200                            return match self.client.get_clan(tag).await {
201                                Ok(new) => {
202                                    self.handler.clan(old.clone(), new.clone()).await; // invoking the handler function the user defined
203                                    self.event_type[i] =
204                                        EventType::Clan(tag.clone(), Instant::now(), Some(new));
205                                    Ok(true)
206                                }
207                                Err(err) => Err(EventsError {
208                                    api_error: err,
209                                    tag: tag.clone(),
210                                    event_type: event.clone(),
211                                    index: i,
212                                }),
213                            };
214                        }
215                    };
216                }
217                EventType::War(tag, last_fired, old) => {
218                    if let Some(duration) = Instant::now().checked_duration_since(*last_fired) {
219                        if Self::should_fire_again(duration, 60 * 10) {
220                            return match self.client.get_current_war(tag).await {
221                                Ok(new) => {
222                                    self.handler.war(old.clone(), new.clone()).await; // invoking the handler function the user defined
223                                    self.event_type[i] =
224                                        EventType::War(tag.clone(), Instant::now(), Some(new));
225                                    Ok(true)
226                                }
227                                Err(err) => Err(EventsError {
228                                    api_error: err,
229                                    tag: tag.clone(),
230                                    event_type: event.clone(),
231                                    index: i,
232                                }),
233                            };
234                        }
235                    };
236                }
237            }
238        }
239
240        Ok(false)
241    }
242}