From e24659e376e13f78e027cb67d2a93c8b0c2dd293 Mon Sep 17 00:00:00 2001 From: Shantanu Raj Date: Tue, 12 Dec 2023 22:17:25 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=8D=B4=20Fetch=20entities=20in=20parallel?= =?UTF-8?q?=20for=20real=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 🍴 Fetch entities in parallel for real * Futures are cold, JS brain gaslight me * Use ResultWithDefaultError where applicable --- Co-authored-by: William Barbosa --- Cargo.lock | 32 +++++++++++++-------------- src/api/client.rs | 25 ++++++++++++---------- src/commands/auth.rs | 2 +- src/config/init.rs | 6 +++--- src/config/locate.rs | 16 +++++++------- src/config/manage.rs | 17 +++++++++------ src/config/model.rs | 51 ++++++++++++++++++++++++++------------------ src/config/parser.rs | 10 ++++----- src/credentials.rs | 5 ++++- src/main.rs | 2 +- src/models.rs | 2 +- src/picker/fzf.rs | 7 ++++-- src/utilities.rs | 4 ++-- 13 files changed, 100 insertions(+), 79 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 59b2789..43053dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -833,24 +833,24 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-lite" @@ -869,9 +869,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", @@ -880,21 +880,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-core", "futures-io", @@ -1707,9 +1707,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] diff --git a/src/api/client.rs b/src/api/client.rs index a0a43c2..0f8e446 100644 --- a/src/api/client.rs +++ b/src/api/client.rs @@ -66,18 +66,18 @@ impl V9ApiClient { let header_content = "Basic ".to_string() + general_purpose::STANDARD.encode(auth_string).as_str(); let mut headers = header::HeaderMap::new(); - let auth_header = header::HeaderValue::from_str(header_content.as_str())?; + let auth_header = header::HeaderValue::from_str(header_content.as_str()).expect("Invalid header value"); headers.insert(header::AUTHORIZATION, auth_header); let base_client = Client::builder().default_headers(headers); let http_client = { if let Some(proxy) = proxy { - base_client.proxy(reqwest::Proxy::all(proxy)?) + base_client.proxy(reqwest::Proxy::all(proxy).expect("Invalid proxy")) } else { base_client } } - .build()?; + .build().expect("Couldn't build a http client"); let api_client = Self { http_client, base_url: "https://track.toggl.com/api/v9".to_string(), @@ -140,13 +140,16 @@ impl ApiClient for V9ApiClient { } async fn get_entities(&self) -> ResultWithDefaultError { - let network_time_entries = self.get_time_entries(); - let network_projects = self.get_projects(); - let network_tasks = self.get_tasks(); - let network_clients = self.get_clients(); + let (network_time_entries, network_projects, network_tasks, network_clients) = + tokio::join!( + self.get_time_entries(), + self.get_projects(), + self.get_tasks(), + self.get_clients(), + ); let clients: HashMap = network_clients - .await? + .unwrap_or_default() .iter() .map(|c| { ( @@ -161,7 +164,7 @@ impl ApiClient for V9ApiClient { .collect(); let projects: HashMap = network_projects - .await? + .unwrap_or_default() .iter() .map(|p| { ( @@ -183,7 +186,7 @@ impl ApiClient for V9ApiClient { .collect(); let tasks: HashMap = network_tasks - .await? + .unwrap_or_default() .iter() .map(|t| { ( @@ -199,7 +202,7 @@ impl ApiClient for V9ApiClient { .collect(); let time_entries = network_time_entries - .await? + .unwrap_or_default() .iter() .map(|te| TimeEntry { id: te.id, diff --git a/src/commands/auth.rs b/src/commands/auth.rs index 97a3083..4f8ad62 100644 --- a/src/commands/auth.rs +++ b/src/commands/auth.rs @@ -23,7 +23,7 @@ impl AuthenticationCommand { "{} {}", AUTH_SUCCEEDED_MESSAGE.green(), user.email.green().bold(), - )?; + ).expect("failed to write to stdout"); Ok(()) } diff --git a/src/config/init.rs b/src/config/init.rs index 0039202..3cd874d 100644 --- a/src/config/init.rs +++ b/src/config/init.rs @@ -9,7 +9,7 @@ impl ConfigInitCommand { match config_path { Ok(path) => { if edit { - utilities::open_path_in_editor(path)? + utilities::open_path_in_editor(path)?; } else { let display_path = utilities::simplify_config_path_for_display(path.as_path()); println!( @@ -30,8 +30,8 @@ impl ConfigInitCommand { "Created config at".green().bold(), display_config_path ); - std::fs::create_dir_all(config_dir)?; - std::fs::write(config_path, default_config)?; + std::fs::create_dir_all(config_dir).expect("failed to create config directory"); + std::fs::write(config_path, default_config).expect("failed to write config"); println!("{}", msg); } } diff --git a/src/config/locate.rs b/src/config/locate.rs index cd15c81..0cd8b00 100644 --- a/src/config/locate.rs +++ b/src/config/locate.rs @@ -3,16 +3,16 @@ use std::path::{Path, PathBuf}; use base64::{engine::general_purpose, Engine as _}; use lazy_static::lazy_static; -use crate::error::ConfigError; +use crate::{error::ConfigError, models::ResultWithDefaultError}; lazy_static! { pub static ref TRACKED_PATH: Option = locate_tracked_path().ok(); } -pub fn locate_config_path() -> Result> { +pub fn locate_config_path() -> ResultWithDefaultError { let config_root = get_config_root(); - let mut config_path = std::env::current_dir()?; + let mut config_path = std::env::current_dir().expect("failed to get current directory"); let mut config_filename = get_encoded_config_path(&config_root, &config_path); while !config_filename.exists() { @@ -24,24 +24,24 @@ pub fn locate_config_path() -> Result> { Ok(config_filename) } -fn locate_tracked_path() -> Result> { +fn locate_tracked_path() -> ResultWithDefaultError { let config_root = get_config_root(); - let mut config_path = std::env::current_dir()?; + let mut config_path = std::env::current_dir().expect("failed to get current directory"); let mut config_filename = get_encoded_config_path(&config_root, &config_path); while !config_filename.exists() { if !config_path.pop() { - return Err("No config file found".into()); + panic!("No config file found"); } config_filename = get_encoded_config_path(&config_root, &config_path); } Ok(config_path) } -pub fn get_config_path_for_current_dir() -> Result> { +pub fn get_config_path_for_current_dir() -> ResultWithDefaultError { let config_root = get_config_root(); - let path = std::env::current_dir()?; + let path = std::env::current_dir().expect("failed to get current directory"); Ok(get_encoded_config_path(&config_root, &path)) } diff --git a/src/config/manage.rs b/src/config/manage.rs index 717518d..17daf6b 100644 --- a/src/config/manage.rs +++ b/src/config/manage.rs @@ -12,13 +12,16 @@ impl ConfigManageCommand { let display_path = utilities::simplify_config_path_for_display(path.as_path()); if delete { - return fs::remove_file(path).map_err(|e| e.into()).map(|_| { - println!( - "{} {}", - "Config file deleted from".red().bold(), - display_path - ); - }); + return { + fs::remove_file(path).map(|_| { + println!( + "{} {}", + "Config file deleted from".red().bold(), + display_path + ); + }).expect("failed to delete config"); + Ok(()) + }; } if edit { return utilities::open_path_in_editor(path); diff --git a/src/config/model.rs b/src/config/model.rs index 1f270b8..310ef5c 100644 --- a/src/config/model.rs +++ b/src/config/model.rs @@ -293,18 +293,19 @@ fn resolve_token_to_macro(token: &str) -> Option { } } -fn resolve_macro( - base_dir: &Path, - instruction: Macro, -) -> Result> { +fn resolve_macro(base_dir: &Path, instruction: Macro) -> ResultWithDefaultError { match instruction { Macro::Branch => { let output = std::process::Command::new("git") .arg("rev-parse") .arg("--abbrev-ref") .arg("HEAD") - .output()?; - Ok(String::from_utf8(output.stdout)?.trim().to_string()) + .output() + .expect("Failed to resolve branch"); + Ok(String::from_utf8(output.stdout) + .expect("Failed to convert branch name to string. This should never happen.") + .trim() + .to_string()) } Macro::BaseDir => Ok(base_dir .file_name() @@ -325,11 +326,11 @@ fn resolve_macro( Ok(parent_dir) } Macro::CurrentDir => { - let output = env::current_dir()?; + let output = env::current_dir().expect("Failed to get current directory"); Ok(output.file_name().unwrap().to_str().unwrap().to_string()) } Macro::ParentDir => { - let current_dir = env::current_dir()?; + let current_dir = env::current_dir().expect("Failed to get current directory"); let parent_dir_path = current_dir.parent().unwrap().to_path_buf(); Ok(parent_dir_path .file_name() @@ -353,24 +354,24 @@ fn resolve_macro( ))); } let git_root = PathBuf::from( - String::from_utf8(output.stdout).map(|s| s.trim().to_string())?, + String::from_utf8(output.stdout) + .map(|s| s.trim().to_string()) + .expect( + "Failed to convert git root to string. This should never happen.", + ), ); // Check if we are in a git worktree // If so, we need to get the root of the main repository let git_dir = git_root.join(".git"); if git_dir.is_file() { - let git_dir = std::fs::read_to_string(git_dir)?; + let git_dir = std::fs::read_to_string(git_dir) + .expect("Failed to read git directory. No git root maybe?"); let git_dir = git_dir .split(':') .nth(1) - .ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::Other, - "Failed to resolve git worktree root", - ) - })? + .expect("Failed to resolve git worktree root") .trim(); let git_root = PathBuf::from(git_dir); @@ -417,7 +418,10 @@ fn resolve_macro( "Failed to resolve git root", ))); } - let git_root = PathBuf::from(String::from_utf8(output.stdout)?); + let git_root = PathBuf::from( + String::from_utf8(output.stdout) + .expect("Failed to convert git root to string. Not in a git root?"), + ); Ok(git_root .parent() .unwrap() @@ -438,10 +442,15 @@ fn resolve_macro( if !output.status.success() { return Err(Box::new(ConfigError::ShellResolution( command, - String::from_utf8(output.stderr)?, + String::from_utf8(output.stderr).unwrap(), ))); } - Ok(String::from_utf8(output.stdout)?.trim().to_string()) + Ok(String::from_utf8(output.stdout) + .expect( + "Failed to convert shell output to string. This should never happen.", + ) + .trim() + .to_string()) } Err(e) => Err(Box::new(ConfigError::ShellResolution( command, @@ -452,7 +461,7 @@ fn resolve_macro( } } -fn resolve_token(base_dir: &Path, token: &str) -> Result> { +fn resolve_token(base_dir: &Path, token: &str) -> ResultWithDefaultError { match resolve_token_to_macro(token) { Some(macro_) => resolve_macro(base_dir, macro_), None => Err(Box::new(ConfigError::UnrecognizedMarco(token.to_string()))), @@ -510,7 +519,7 @@ impl TrackConfig { self.get_branch_config(branch.as_deref()) } pub fn get_active_config(&self) -> ResultWithDefaultError<&BranchConfig> { - let current_dir = std::env::current_dir()?; + let current_dir = std::env::current_dir().expect("Failed to get current directory"); return Ok(self.get_branch_config_for_dir(¤t_dir)); } pub fn get_default_entry(&self, entities: Entities) -> ResultWithDefaultError { diff --git a/src/config/parser.rs b/src/config/parser.rs index 01d9a62..c21b04a 100644 --- a/src/config/parser.rs +++ b/src/config/parser.rs @@ -1,13 +1,13 @@ use std::path::Path; use toml; +use crate::models::ResultWithDefaultError; + use super::model::TrackConfig; -pub fn get_config_from_file>( - path: P, -) -> Result> { - let contents = std::fs::read_to_string(path)?; - let config: TrackConfig = toml::from_str(&contents)?; +pub fn get_config_from_file>(path: P) -> ResultWithDefaultError { + let contents = std::fs::read_to_string(path).expect("failed to read config file"); + let config: TrackConfig = toml::from_str(&contents).expect("failed to parse config"); Ok(config) } diff --git a/src/credentials.rs b/src/credentials.rs index ba6bb45..0888e67 100644 --- a/src/credentials.rs +++ b/src/credentials.rs @@ -30,7 +30,10 @@ impl KeyringStorage { impl CredentialsStorage for KeyringStorage { fn read(&self) -> ResultWithDefaultError { - let api_token = self.keyring.get_password()?; + let api_token = self + .keyring + .get_password() + .expect("failed to read from keyring"); Ok(Credentials { api_token }) } diff --git a/src/main.rs b/src/main.rs index 1561f97..4bd6db1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,7 +60,7 @@ async fn execute_subcommand(args: CommandLineArguments) -> ResultWithDefaultErro if !directory.is_dir() { return Err(Box::new(error::ArgumentError::NotADirectory(directory))); } - std::env::set_current_dir(directory)?; + std::env::set_current_dir(directory).expect("Couldn't set current directory"); } match command { None => RunningTimeEntryCommand::execute(get_default_api_client()?).await?, diff --git a/src/models.rs b/src/models.rs index 27fa0a9..491a049 100644 --- a/src/models.rs +++ b/src/models.rs @@ -9,7 +9,7 @@ use colors_transform::{Color, Rgb}; use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; -pub type ResultWithDefaultError = Result>; +pub type ResultWithDefaultError = Result>; #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Entities { diff --git a/src/picker/fzf.rs b/src/picker/fzf.rs index 2bd24a1..117be95 100644 --- a/src/picker/fzf.rs +++ b/src/picker/fzf.rs @@ -42,13 +42,16 @@ impl ItemPicker for FzfPicker { let fzf_input = format_as_fzf_input(&items); let possible_elements = create_element_hash_map(&items); - writeln!(child.stdin.as_mut().unwrap(), "{}", fzf_input)?; + writeln!(child.stdin.as_mut().unwrap(), "{}", fzf_input) + .expect("Failed to write to fzf stdin"); match child.wait_with_output() { Err(_) => Err(Box::new(PickerError::Generic)), Ok(output) => match output.status.code() { Some(0) => { - let user_selected_string = String::from_utf8(output.stdout)?; + let user_selected_string = String::from_utf8(output.stdout).expect( + "Failed to convert fzf output to string. This should never happen.", + ); let selected_item_index = utilities::remove_trailing_newline(user_selected_string); let selected_item = diff --git a/src/utilities.rs b/src/utilities.rs index fd4b131..6f906f4 100644 --- a/src/utilities.rs +++ b/src/utilities.rs @@ -6,7 +6,7 @@ use std::{ use colored::Colorize; use directories::BaseDirs; -use crate::constants; +use crate::{constants, models::ResultWithDefaultError}; pub fn remove_trailing_newline(value: String) -> String { value.trim_end().to_string() @@ -50,7 +50,7 @@ pub fn read_from_stdin_with_constraints(text: &str, valid_values: &[String]) -> } } -pub fn open_path_in_editor

(path: P) -> Result<(), Box> +pub fn open_path_in_editor

(path: P) -> ResultWithDefaultError<()> where P: AsRef + std::convert::AsRef, {