1use anyhow::anyhow;
2use async_trait::async_trait;
3use serde::{Deserialize, Serialize};
4use std::{str::FromStr, sync::Arc};
5use thiserror::Error;
6
7use crate::entities::Epoch;
8use crate::{StdError, StdResult};
9
10use super::SupportedEra;
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14pub struct EraMarker {
15 pub name: String,
17
18 pub epoch: Option<Epoch>,
20}
21
22impl EraMarker {
23 pub fn new(name: &str, epoch: Option<Epoch>) -> Self {
25 let name = name.to_string();
26
27 Self { name, epoch }
28 }
29}
30
31#[async_trait]
34pub trait EraReaderAdapter: Sync + Send {
35 async fn read(&self) -> StdResult<Vec<EraMarker>>;
37}
38
39#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct EraEpochToken {
43 current_epoch: Epoch,
44 current_era: EraMarker,
45 next_era: Option<EraMarker>,
46}
47
48impl EraEpochToken {
49 pub fn new(current_epoch: Epoch, current_era: EraMarker, next_era: Option<EraMarker>) -> Self {
51 Self {
52 current_epoch,
53 current_era,
54 next_era,
55 }
56 }
57
58 pub fn get_current_supported_era(&self) -> StdResult<SupportedEra> {
62 SupportedEra::from_str(&self.current_era.name)
63 .map_err(|_| anyhow!(format!("Unsupported era '{}'.", &self.current_era.name)))
64 }
65
66 pub fn get_current_era_marker(&self) -> &EraMarker {
68 &self.current_era
69 }
70
71 pub fn get_current_epoch(&self) -> Epoch {
73 self.current_epoch
74 }
75
76 pub fn get_next_supported_era(&self) -> StdResult<Option<SupportedEra>> {
81 match self.next_era.as_ref() {
82 Some(marker) => Ok(Some(
83 SupportedEra::from_str(&marker.name)
84 .map_err(|_| anyhow!(format!("Unsupported era '{}'.", &marker.name)))?,
85 )),
86 None => Ok(None),
87 }
88 }
89
90 pub fn get_next_era_marker(&self) -> Option<&EraMarker> {
92 self.next_era.as_ref()
93 }
94}
95
96pub struct EraReader {
99 adapter: Arc<dyn EraReaderAdapter>,
100}
101
102#[derive(Debug, Error)]
104pub enum EraReaderError {
105 #[error("Adapter Error message: «{message}»")]
107 AdapterFailure {
108 message: String,
110
111 #[source]
113 error: StdError,
114 },
115
116 #[error(
118 "Cannot determine the Era we are currently at epoch {epoch} using the adapter informations: {eras:?}"
119 )]
120 CurrentEraNotFound {
121 epoch: Epoch,
123
124 eras: Vec<EraMarker>,
126 },
127}
128
129impl EraReader {
130 pub fn new(adapter: Arc<dyn EraReaderAdapter>) -> Self {
132 Self { adapter }
133 }
134
135 pub async fn read_era_epoch_token(
139 &self,
140 current_epoch: Epoch,
141 ) -> Result<EraEpochToken, EraReaderError> {
142 let eras = self
143 .adapter
144 .read()
145 .await
146 .map_err(|e| EraReaderError::AdapterFailure {
147 message: format!("Reading from EraReader adapter raised an error: '{}'.", &e),
148 error: e,
149 })?;
150
151 let current_marker = eras.iter().filter(|&f| f.epoch.is_some()).fold(
152 None,
153 |acc: Option<&EraMarker>, marker| {
154 if marker.epoch.unwrap() <= current_epoch
155 && (acc.is_none() || marker.epoch.unwrap() > acc.unwrap().epoch.unwrap())
156 {
157 Some(marker)
158 } else {
159 acc
160 }
161 },
162 );
163 let current_era_marker =
164 current_marker.ok_or_else(|| EraReaderError::CurrentEraNotFound {
165 epoch: current_epoch,
166 eras: eras.clone(),
167 })?;
168
169 let next_era_marker = eras.last().filter(|&marker| marker != current_era_marker);
170
171 Ok(EraEpochToken::new(
172 current_epoch,
173 current_era_marker.to_owned(),
174 next_era_marker.cloned(),
175 ))
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::super::adapters::EraReaderDummyAdapter as DummyAdapter;
182 use super::*;
183
184 fn get_basic_marker_sample() -> Vec<EraMarker> {
185 vec![
186 EraMarker {
187 name: "one".to_string(),
188 epoch: Some(Epoch(1)),
189 },
190 EraMarker {
191 name: SupportedEra::dummy().to_string(),
192 epoch: None,
193 },
194 EraMarker {
195 name: SupportedEra::dummy().to_string(),
196 epoch: Some(Epoch(10)),
197 },
198 ]
199 }
200
201 #[tokio::test]
202 async fn current_era_is_supported() {
203 let markers: Vec<EraMarker> = get_basic_marker_sample();
204 let adapter = DummyAdapter::default();
205 adapter.set_markers(markers);
206
207 let reader = EraReader::new(Arc::new(adapter));
208 let token = reader.read_era_epoch_token(Epoch(10)).await.unwrap();
209
210 assert_eq!(
211 EraEpochToken {
212 current_epoch: Epoch(10),
213 current_era: EraMarker {
214 name: SupportedEra::dummy().to_string(),
215 epoch: Some(Epoch(10))
216 },
217 next_era: None,
218 },
219 token
220 );
221 }
222
223 #[tokio::test]
224 async fn era_epoch_token() {
225 let markers: Vec<EraMarker> = get_basic_marker_sample();
226 let adapter = DummyAdapter::default();
227 adapter.set_markers(markers);
228
229 let reader = EraReader::new(Arc::new(adapter));
230 let token = reader.read_era_epoch_token(Epoch(10)).await.unwrap();
231 assert_eq!(
232 SupportedEra::dummy(),
233 token
234 .get_current_supported_era()
235 .expect("the given era is supported")
236 );
237 assert!(token.get_next_era_marker().is_none());
238 assert!(token
239 .get_next_supported_era()
240 .expect("None era shall not fail when asked.")
241 .is_none());
242 }
243
244 #[tokio::test]
245 async fn previous_era_is_not_supported() {
246 let markers: Vec<EraMarker> = get_basic_marker_sample();
247 let adapter = DummyAdapter::default();
248 adapter.set_markers(markers);
249
250 let reader = EraReader::new(Arc::new(adapter));
251 let token = reader.read_era_epoch_token(Epoch(9)).await.unwrap();
252
253 assert_eq!(
254 EraEpochToken {
255 current_epoch: Epoch(9),
256 current_era: EraMarker {
257 name: "one".to_string(),
258 epoch: Some(Epoch(1))
259 },
260 next_era: Some(EraMarker {
261 name: SupportedEra::dummy().to_string(),
262 epoch: Some(Epoch(10))
263 }),
264 },
265 token
266 );
267 }
268
269 #[tokio::test]
270 async fn error_when_no_current_era() {
271 let markers = vec![
272 EraMarker {
273 name: "one".to_string(),
274 epoch: None,
275 },
276 EraMarker {
277 name: "two".to_string(),
278 epoch: None,
279 },
280 EraMarker {
281 name: "three".to_string(),
282 epoch: Some(Epoch(100)),
283 },
284 ];
285
286 let adapter = DummyAdapter::default();
287 adapter.set_markers(markers);
288
289 let reader = EraReader::new(Arc::new(adapter));
290 let _ = reader
291 .read_era_epoch_token(Epoch(9))
292 .await
293 .expect_err("No current era must make the reader to fail.");
294 }
295
296 #[tokio::test]
297 async fn error_when_no_era() {
298 let adapter = DummyAdapter::default();
299
300 let reader = EraReader::new(Arc::new(adapter));
301 let _ = reader
302 .read_era_epoch_token(Epoch(9))
303 .await
304 .expect_err("The adapter gave no result hence the reader should fail.");
305 }
306
307 #[tokio::test]
308 async fn current_era_is_not_supported() {
309 let markers: Vec<EraMarker> = get_basic_marker_sample();
310 let adapter = DummyAdapter::default();
311 adapter.set_markers(markers);
312
313 let reader = EraReader::new(Arc::new(adapter));
314 let token = reader.read_era_epoch_token(Epoch(9)).await.unwrap();
315
316 token
317 .get_current_supported_era()
318 .expect_err("The era 'one' is not supported hence the token must issue an error.");
319
320 assert_eq!(
321 &EraMarker {
322 name: "one".to_string(),
323 epoch: Some(Epoch(1))
324 },
325 token.get_current_era_marker()
326 );
327 token
328 .get_next_supported_era()
329 .expect("The next era is supported hence this shall not fail.");
330 }
331
332 #[tokio::test]
333 async fn epoch_0_should_work() {
334 let markers = vec![EraMarker::new(
335 &SupportedEra::dummy().to_string(),
336 Some(Epoch(0)),
337 )];
338 let adapter = DummyAdapter::default();
339 adapter.set_markers(markers);
340 let reader = EraReader::new(Arc::new(adapter));
341 let token = reader.read_era_epoch_token(Epoch(9)).await.unwrap();
342
343 assert_eq!(
344 &EraMarker {
345 name: SupportedEra::dummy().to_string(),
346 epoch: Some(Epoch(0))
347 },
348 token.get_current_era_marker()
349 );
350 }
351}