use crate::{ClientError, Error}; use colored::*; use serde::{de::DeserializeOwned, Serialize}; use shared::{interface_config::ServerInfo, INNERNET_PUBKEY_HEADER}; use std::time::Duration; use ureq::{Agent, AgentBuilder}; pub fn human_duration(duration: Duration) -> String { match duration.as_secs() { n if n < 1 => "just now".cyan().to_string(), n if n < 60 => format!("{} {} ago", n, "seconds".cyan()), n if n < 60 * 60 => { let mins = n / 60; let secs = n % 60; format!( "{} {}, {} {} ago", mins, if mins == 1 { "minute" } else { "minutes" }.cyan(), secs, if secs == 1 { "second" } else { "seconds" }.cyan(), ) }, n => { let hours = n / (60 * 60); let mins = (n / 60) % 60; format!( "{} {}, {} {} ago", hours, if hours == 1 { "hour" } else { "hours" }.cyan(), mins, if mins == 1 { "minute" } else { "minutes" }.cyan(), ) }, } } pub fn human_size(bytes: u64) -> String { const KB: u64 = 1024; const MB: u64 = 1024 * KB; const GB: u64 = 1024 * MB; const TB: u64 = 1024 * GB; match bytes { n if n < 2 * KB => format!("{} {}", n, "B".cyan()), n if n < 2 * MB => format!("{:.2} {}", n as f64 / KB as f64, "KiB".cyan()), n if n < 2 * GB => format!("{:.2} {}", n as f64 / MB as f64, "MiB".cyan()), n if n < 2 * TB => format!("{:.2} {}", n as f64 / GB as f64, "GiB".cyan()), n => format!("{:.2} {}", n as f64 / TB as f64, "TiB".cyan()), } } pub struct Api<'a> { agent: Agent, server: &'a ServerInfo, } impl<'a> Api<'a> { pub fn new(server: &'a ServerInfo) -> Self { let agent = AgentBuilder::new() .timeout(Duration::from_secs(5)) .redirects(0) .build(); Self { agent, server } } pub fn http(&self, verb: &str, endpoint: &str) -> Result { self.request::<(), _>(verb, endpoint, None) } pub fn http_form( &self, verb: &str, endpoint: &str, form: S, ) -> Result { self.request(verb, endpoint, Some(form)) } fn request( &self, verb: &str, endpoint: &str, form: Option, ) -> Result { let request = self .agent .request( verb, &format!("http://{}/v1{}", self.server.internal_endpoint, endpoint), ) .set(INNERNET_PUBKEY_HEADER, &self.server.public_key); let response = if let Some(form) = form { request.send_json(serde_json::to_value(form)?)? } else { request.call()? }; let mut response = response.into_string()?; // A little trick for serde to parse an empty response as `()`. if response.is_empty() { response = "null".into(); } Ok(serde_json::from_str(&response).map_err(|e| { ClientError(format!( "failed to deserialize JSON response from the server: {}, response={}", e, &response )) })?) } }