midgard_rs/midgard/
mod.rs

1use chrono::Duration;
2use chrono::{DateTime, Utc};
3pub use config::*;
4use serde::{Deserialize, Serialize};
5
6mod config;
7mod endpoints;
8
9#[derive(Debug, Serialize, Deserialize, Clone, Default)]
10pub struct Midgard {
11	config: Configuration,
12	last_call: DateTime<Utc>,
13}
14
15impl Midgard {
16        #[must_use]
17	pub fn new() -> Self {
18		Self { config: Configuration::default(), last_call: Utc::now() }
19	}
20
21        #[must_use]
22	pub fn with_config(config: Configuration) -> Self {
23		Self { config, last_call: Utc::now() }
24	}
25
26        #[must_use]
27	pub const fn get_config(&self) -> &Configuration {
28		&self.config
29	}
30
31	pub fn set_config(&mut self, config: Configuration) {
32		self.config = config;
33	}
34
35        #[must_use]
36	pub const fn get_last_call(&self) -> DateTime<Utc> {
37		self.last_call
38	}
39
40	fn set_last_call(&mut self, last_call: DateTime<Utc>) {
41		self.last_call = last_call;
42	}
43
44	/// Returns a future timestamp of when it is ok to call the Midgard API again
45	fn ok_to_call_at(&self) -> DateTime<Utc> {
46		let rate_limit: i64 = i64::try_from(self.config.get_rate_limit_ms()).map_or(1000, |rate_limit| rate_limit);
47		let rate_limit = Duration::try_milliseconds(rate_limit).map_or_else(Duration::zero, |rate_limit| rate_limit);
48		self.last_call.checked_add_signed(rate_limit).map_or_else(Utc::now, |res| res)
49	}
50
51	/// Sleeps until it is ok to call the Midgard API again
52	async fn sleep_until_ok_to_call(&mut self) {
53		let now = Utc::now();
54		let ok_to_call_at = self.ok_to_call_at();
55		if now < ok_to_call_at {
56			let sleep_duration = ok_to_call_at - now;
57			tokio::time::sleep(sleep_duration.to_std().unwrap()).await;
58		}
59		self.set_last_call(Utc::now());
60	}
61}
62
63
64#[cfg(test)]
65mod tests {
66        use rand::prelude::*;
67	use serde_json::json;
68        use crate::GetActionList;
69
70	use super::*;
71
72        #[tokio::test]
73        async fn endpoints() {
74                let mut midgard = Midgard::new();
75
76
77                // actions
78                let params = GetActionList::new(vec!["BTC.BTC".to_string()], 10);
79		let actions = midgard.get_actions(params).await.unwrap();
80                assert!(!actions.get_actions().get_actions().is_empty());
81
82                
83                // action pagination
84                let pool_list = midgard.get_pool_list(None, None).await.unwrap();
85		let random_usize = thread_rng().gen_range(0..pool_list.get_assets().len());
86		let pool = pool_list.get_assets()[random_usize].clone();
87
88		let mut params = GetActionList::new(vec![pool.clone()], 5);
89		let actions = midgard.get_actions(params.clone()).await.unwrap();
90		assert!(!actions.get_actions().get_actions().is_empty());
91
92		let next_page_token = actions.get_meta().get_next_page_token();
93		if let Some(next_page_token) = next_page_token {
94			params.set_next_page_token(next_page_token);
95			let next_actions = midgard.get_actions(params).await.unwrap();
96
97			assert!(!next_actions.get_actions().get_actions().is_empty());
98		}
99
100                // savers pools
101                let pool_list = midgard.get_pool_list(None, None).await.unwrap();
102                let savers_pools = pool_list.get_savers_pools();
103                assert!(!savers_pools.is_empty());
104
105                println!("savers pools: {}", json!(savers_pools));
106        }
107
108}