// SPDX-License-Identifier: Apache-2.0 use crate::config::Config; use tonic::{ metadata::{errors::InvalidMetadataValue, AsciiMetadataValue}, Request, }; #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] ConfigError(#[from] crate::config::Error), #[error(transparent)] InternalError(#[from] InvalidMetadataValue), } pub fn block_on(future: F) -> F::Output where F: std::future::Future, { tokio::runtime::Runtime::new().unwrap().block_on(future) } pub fn sign_request(req: T) -> Result, Error> { let pubkey = Config::get_detee_wallet()?; let timestamp = chrono::Utc::now().to_rfc3339(); let signature = Config::try_sign_message(&format!("{timestamp}{req:?}"))?; let timestamp: AsciiMetadataValue = timestamp.parse()?; let pubkey: AsciiMetadataValue = pubkey.parse()?; let signature: AsciiMetadataValue = signature.parse()?; let mut req = Request::new(req); req.metadata_mut().insert("timestamp", timestamp); req.metadata_mut().insert("pubkey", pubkey); req.metadata_mut().insert("request-signature", signature); Ok(req) } pub fn shorten_string(my_string: &String) -> String { if my_string.len() <= 8 { my_string.to_string() } else { let first_part = &my_string[..8]; // let last_part = &my_string[my_string.len() - 4..]; format!("{}", first_part) } } pub fn display_mib_or_gib(value: &u64) -> String { if *value >= 1024 { if *value < 102400 { let value = (value / 102) as f64; format!("{}G", value / 10_f64) } else { format!("{}G", value / 1024) } } else { format!("{}M", value) } } #[macro_export] macro_rules! call_with_follow_redirect { ( $client:expr, $req_data:expr, $method:ident ) => { async { let mut client = $client; for attempt in 0..crate::constants::MAX_REDIRECTS { log::debug!( "Attempt #{}: Calling method '{}'...", attempt + 1, stringify!($method) ); let req_data_clone = $req_data.clone(); let signed_req = crate::utils::sign_request(req_data_clone)?; match client.$method(signed_req).await { Ok(resp) => return Ok(resp), Err(status) if status.code() == tonic::Code::Unavailable && status.message() == "moved" => { let redirect_url = status .metadata() .get("location") .and_then(|v| v.to_str().ok()) .ok_or_else(|| { Error::RedirectError( "Server indicated a move but provided no location".into(), ) })?; log::info!("Server moved. Redirecting to {}...", redirect_url); client = client_from_endpoint(format!("https://{}", redirect_url)).await?; continue; } Err(e) => return Err(Error::ResponseStatus(e)), } } Err(Error::MaxRedirectsExceeded(crate::constants::MAX_REDIRECTS.to_string())) } }; }