mithril_client/era.rs
1//! A client to retrieve the current Mithril era.
2//!
3//! In order to do so it defines a [MithrilEraClient] which exposes the following features:
4//! - [fetch_current][MithrilEraClient::fetch_current]: fetch the current Mithril era using its [EraFetcher]
5//!
6//! This module defines the following components:
7//!
8//! - [EraFetcher]: defines an interface to retrieve the current Mithril era, an implementation over
9//! [mithril_aggregator_client::AggregatorHttpClient] is provided
10//! - [FetchedEra]: a wrapper around a raw era string that provides a conversion to [SupportedEra]
11//!
12//! # Retrieve the current Mithril era
13//!
14//! To get [SupportedEra] using the [ClientBuilder][crate::client::ClientBuilder].
15//!
16//! ```no_run
17//! # async fn run() -> mithril_client::MithrilResult<()> {
18//! use mithril_client::ClientBuilder;
19//!
20//! let client = ClientBuilder::aggregator("YOUR_AGGREGATOR_ENDPOINT", "YOUR_GENESIS_VERIFICATION_KEY").build()?;
21//!
22//! let fetched_era = client.mithril_era_client().fetch_current().await?;
23//! match fetched_era.to_supported_era() {
24//! Ok(supported_era) => println!("Current Mithril era: {supported_era}"),
25//! Err(era) => println!(
26//! "Warning: Unsupported era '{}', the aggregator might not be compatible with this client version. Consider upgrading.\nFetched era: {era}",
27//! fetched_era.era
28//! ),
29//! }
30//! # Ok(())
31//! # }
32//! ```
33
34use std::sync::Arc;
35
36use anyhow::Context;
37use async_trait::async_trait;
38use serde::{Deserialize, Serialize};
39
40use mithril_common::entities::SupportedEra;
41
42use crate::MithrilResult;
43
44/// Client for retrieving the current Mithril era.
45pub struct MithrilEraClient {
46 era_fetcher: Arc<dyn EraFetcher>,
47}
48
49impl MithrilEraClient {
50 /// Constructs a new [MithrilEraClient].
51 pub fn new(era_fetcher: Arc<dyn EraFetcher>) -> Self {
52 Self { era_fetcher }
53 }
54
55 /// Fetches the current Mithril era.
56 pub async fn fetch_current(&self) -> MithrilResult<FetchedEra> {
57 self.era_fetcher.fetch_current_era().await
58 }
59}
60
61/// Wrapper around a raw Mithril era string.
62#[derive(Debug, PartialEq, Serialize, Deserialize)]
63pub struct FetchedEra {
64 /// Mithril era.
65 pub era: String,
66}
67
68impl FetchedEra {
69 /// Attempts to convert the internal Mithril era string to a [SupportedEra] enum variant.
70 pub fn to_supported_era(&self) -> MithrilResult<SupportedEra> {
71 self.era
72 .parse::<SupportedEra>()
73 .with_context(|| format!("Unknown supported era: {}", self.era))
74 }
75}
76
77/// Trait for retrieving the current Mithril era.
78#[cfg_attr(target_family = "wasm", async_trait(?Send))]
79#[cfg_attr(not(target_family = "wasm"), async_trait)]
80pub trait EraFetcher: Send + Sync {
81 /// Fetch the current Mithril era.
82 async fn fetch_current_era(&self) -> MithrilResult<FetchedEra>;
83}
84
85#[cfg(test)]
86mod tests {
87 use mithril_common::entities::SupportedEra;
88
89 use super::*;
90
91 #[test]
92 fn to_supported_era_should_return_enum_variant_for_known_era() {
93 let fetched_era = FetchedEra {
94 era: SupportedEra::Pythagoras.to_string(),
95 };
96
97 let supported_era = fetched_era.to_supported_era().unwrap();
98
99 assert_eq!(SupportedEra::Pythagoras, supported_era);
100 }
101
102 #[test]
103 fn to_supported_era_returns_error_for_unsupported_era() {
104 let fetched_era = FetchedEra {
105 era: "unsupported_era".to_string(),
106 };
107
108 fetched_era
109 .to_supported_era()
110 .expect_err("Expected an error when converting an unsupported era to 'SupportedEra'");
111 }
112}