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}