From d05bd8a08d003be57400488b7971ff65d6bb840f Mon Sep 17 00:00:00 2001 From: Yoshihiro OKUMURA Date: Mon, 20 Apr 2026 15:59:28 +0900 Subject: [PATCH] chore(rust): update lockfile and format sources Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Cargo.lock | 223 +++++++++++++++++++++++++++++++--- src/api/files.rs | 20 +-- src/api/folders.rs | 9 +- src/api/laboratories.rs | 15 +-- src/api/users.rs | 7 +- src/cache/mod.rs | 32 +++-- src/commands/chacl.rs | 11 +- src/commands/config.rs | 2 +- src/commands/cp.rs | 30 +++-- src/commands/download.rs | 31 ++--- src/commands/file_metadata.rs | 5 +- src/commands/labs.rs | 17 ++- src/commands/login.rs | 10 +- src/commands/ls.rs | 16 +-- src/commands/metadata.rs | 4 +- src/commands/mkdir.rs | 4 +- src/commands/mod.rs | 2 +- src/commands/mv.rs | 24 +++- src/commands/rm.rs | 3 +- src/commands/selfupdate.rs | 20 +-- src/commands/shared.rs | 17 ++- src/commands/upload.rs | 38 ++++-- src/connection.rs | 1 - src/main.rs | 24 ++-- src/token.rs | 2 +- 25 files changed, 390 insertions(+), 177 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb314f1..8f28704 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -654,6 +654,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -799,11 +805,24 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + [[package]] name = "hash32" version = "0.2.1" @@ -822,6 +841,15 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + [[package]] name = "hashbrown" version = "0.17.0" @@ -1037,6 +1065,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "idna" version = "0.5.0" @@ -1076,6 +1110,8 @@ checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown 0.17.0", + "serde", + "serde_core", ] [[package]] @@ -1137,6 +1173,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" version = "0.2.185" @@ -1664,6 +1706,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -1781,6 +1833,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "radium" version = "0.7.0" @@ -1789,9 +1847,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -2047,9 +2105,9 @@ dependencies = [ [[package]] name = "rtoolbox" -version = "0.0.4" +version = "0.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327b72899159dfae8060c51a1f6aebe955245bcd9cc4997eed0f623caea022e4" +checksum = "50a0e551c1e27e1731aba276dbeaeac73f53c7cd34d1bda485d02bd1e0f36844" dependencies = [ "libc", "windows-sys 0.59.0", @@ -2065,7 +2123,7 @@ dependencies = [ "borsh", "bytes", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "rkyv", "serde", "serde_json", @@ -2444,7 +2502,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.2", "once_cell", "rustix", "windows-sys 0.61.2", @@ -2536,9 +2594,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.0" +version = "1.52.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91135f59b1cbf38c91e73cf3386fca9bb77915c45ce2771460c9d92f0f3d776" +checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" dependencies = [ "bytes", "libc", @@ -2674,9 +2732,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "unicase" @@ -2705,6 +2763,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "untrusted" version = "0.9.0" @@ -2809,11 +2873,20 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", ] [[package]] @@ -2871,6 +2944,40 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "web-sys" version = "0.3.95" @@ -3173,6 +3280,94 @@ name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "writeable" diff --git a/src/api/files.rs b/src/api/files.rs index af5c7ef..7c67e81 100644 --- a/src/api/files.rs +++ b/src/api/files.rs @@ -10,10 +10,7 @@ struct FileListResponse { impl MDRSConnection { /// List all files in a folder, following pagination automatically. - pub async fn list_all_files( - &self, - folder_id: &str, - ) -> Result, anyhow::Error> { + pub async fn list_all_files(&self, folder_id: &str) -> Result, anyhow::Error> { let mut all_files = Vec::new(); let mut page: u32 = 1; loop { @@ -36,13 +33,9 @@ impl MDRSConnection { } /// Upload a local file into the given remote folder. - pub async fn upload_file( - &self, - folder_id: &str, - file_path: &str, - ) -> Result<(), anyhow::Error> { + pub async fn upload_file(&self, folder_id: &str, file_path: &str) -> Result<(), anyhow::Error> { + use anyhow::{anyhow, bail}; use reqwest::multipart; -use anyhow::{anyhow, bail}; let file_name: String = std::path::Path::new(file_path) .file_name() .ok_or_else(|| anyhow!("Invalid file path: `{}`", file_path))? @@ -62,11 +55,7 @@ use anyhow::{anyhow, bail}; } /// Download a file from `url` and write it to `dest`. - pub async fn download_file( - &self, - url: &str, - dest: &str, - ) -> Result<(), anyhow::Error> { + pub async fn download_file(&self, url: &str, dest: &str) -> Result<(), anyhow::Error> { let resp = self .client .get(url) @@ -78,4 +67,3 @@ use anyhow::{anyhow, bail}; Ok(()) } } - diff --git a/src/api/folders.rs b/src/api/folders.rs index b813f2c..5d23214 100644 --- a/src/api/folders.rs +++ b/src/api/folders.rs @@ -1,6 +1,6 @@ use crate::connection::MDRSConnection; -use anyhow::{bail}; pub use crate::models::folder::{FolderDetail, FolderSimple}; +use anyhow::bail; impl MDRSConnection { /// List folders matching the given path under a laboratory (GET v3/folders/?path=...&laboratory_id=...) @@ -50,11 +50,7 @@ impl MDRSConnection { /// Authenticate against a password-locked folder (POST v3/folders/{id}/auth/). /// Returns `Err` if the password is incorrect or the request fails. - pub async fn folder_auth( - &self, - folder_id: &str, - password: &str, - ) -> Result<(), anyhow::Error> { + pub async fn folder_auth(&self, folder_id: &str, password: &str) -> Result<(), anyhow::Error> { let resp = self .client .post(self.build_url(&format!("v3/folders/{}/auth/", folder_id))) @@ -71,4 +67,3 @@ impl MDRSConnection { Ok(()) } } - diff --git a/src/api/laboratories.rs b/src/api/laboratories.rs index f2f8060..a0de2dc 100644 --- a/src/api/laboratories.rs +++ b/src/api/laboratories.rs @@ -13,13 +13,14 @@ impl MDRSConnection { let resp = self.get("v3/laboratories/").await?; // The API may return a paginated object or a direct array let text = resp.text().await?; - let items: Vec = if let Ok(list) = serde_json::from_str::>(&text) { - list - } else if let Ok(paged) = serde_json::from_str::(&text) { - paged.results.unwrap_or_default() - } else { - vec![] - }; + let items: Vec = + if let Ok(list) = serde_json::from_str::>(&text) { + list + } else if let Ok(paged) = serde_json::from_str::(&text) { + paged.results.unwrap_or_default() + } else { + vec![] + }; Ok(Laboratories { items }) } } diff --git a/src/api/users.rs b/src/api/users.rs index 5c71c4d..d083647 100644 --- a/src/api/users.rs +++ b/src/api/users.rs @@ -1,7 +1,7 @@ use crate::connection::MDRSConnection; use crate::models::user::User as ModelUser; +use anyhow::bail; use serde::Deserialize; -use anyhow::{bail}; /// Full API response shape from GET v3/users/current/ #[derive(Debug, Deserialize)] @@ -38,10 +38,7 @@ impl MDRSConnection { /// Refresh the access token using the refresh token. /// POST v3/users/token/refresh/ {refresh: ...} -> {access: new_access} - pub async fn token_refresh( - &self, - refresh_token: &str, - ) -> Result { + pub async fn token_refresh(&self, refresh_token: &str) -> Result { let body = serde_json::json!({ "refresh": refresh_token }); let resp = self .client diff --git a/src/cache/mod.rs b/src/cache/mod.rs index 82e7795..89fc32b 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -4,8 +4,8 @@ pub mod types; pub use digest::compute_digest; pub use types::{Cache, CacheLaboratory, CacheLabsWrapper, CacheUser}; -use anyhow::{anyhow, bail}; use crate::connection::MDRSConnection; +use anyhow::{anyhow, bail}; use std::collections::HashMap; use std::fs; use std::sync::{Arc, LazyLock, Mutex}; @@ -43,10 +43,21 @@ fn cache_file_path(remote: &str) -> std::path::PathBuf { pub fn load_cache(remote: &str) -> Result { let cache_path = cache_file_path(remote); if !cache_path.exists() { - bail!("Not logged in to `{}`. Run `mdrs login {}` first.", remote, remote); + bail!( + "Not logged in to `{}`. Run `mdrs login {}` first.", + remote, + remote + ); } let data = fs::read_to_string(&cache_path)?; - serde_json::from_str::(&data).map_err(|e| anyhow!("Cache for `{}` is invalid or outdated ({}). Run `mdrs login {}` to refresh it.", remote, e, remote)) + serde_json::from_str::(&data).map_err(|e| { + anyhow!( + "Cache for `{}` is invalid or outdated ({}). Run `mdrs login {}` to refresh it.", + remote, + e, + remote + ) + }) } // --------------------------------------------------------------------------- @@ -61,9 +72,7 @@ pub fn load_cache(remote: &str) -> Result { /// - `flock(LOCK_EX)` on a dedicated `cache/{remote}.lock` file serializes /// the entire read-check-refresh-write cycle across separate processes on /// the same host. -pub async fn load_cache_with_token_refresh( - remote: &str, -) -> Result { +pub async fn load_cache_with_token_refresh(remote: &str) -> Result { let lock = get_remote_lock(remote); let _guard = lock.lock().await; @@ -81,7 +90,11 @@ pub async fn load_cache_with_token_refresh( let mut cache = load_cache(remote)?; if crate::token::is_expired(&cache.token.refresh) { - bail!("Session for `{}` has expired. Please run `mdrs login {}` again.", remote, remote); + bail!( + "Session for `{}` has expired. Please run `mdrs login {}` again.", + remote, + remote + ); } if crate::token::is_refresh_required(&cache.token.access, &cache.token.refresh) { @@ -99,10 +112,7 @@ pub async fn load_cache_with_token_refresh( /// Call the token-refresh endpoint and write the new access token back to the /// cache file. The caller must already hold the per-remote async mutex. -async fn refresh_and_persist( - remote: &str, - cache: &Cache, -) -> Result { +async fn refresh_and_persist(remote: &str, cache: &Cache) -> Result { let url = crate::commands::config::get_remote_url(remote)? .ok_or_else(|| anyhow!("Remote `{}` is not configured.", remote))?; let conn = MDRSConnection::new(&url); diff --git a/src/commands/chacl.rs b/src/commands/chacl.rs index 24fef97..88cdebb 100644 --- a/src/commands/chacl.rs +++ b/src/commands/chacl.rs @@ -1,8 +1,6 @@ use crate::cache::{create_authenticated_conn, load_cache_with_token_refresh}; -use crate::commands::shared::{ - find_folder, find_lab_in_cache, parse_remote_path, -}; -use anyhow::{bail}; +use crate::commands::shared::{find_folder, find_lab_in_cache, parse_remote_path}; +use anyhow::bail; pub async fn chacl( remote_path: &str, @@ -29,7 +27,10 @@ pub async fn chacl( let folder = find_folder(&conn, lab.id, &folder_path, None).await?; let mut data = serde_json::Map::new(); - data.insert("access_level".to_string(), serde_json::json!(access_level_id)); + data.insert( + "access_level".to_string(), + serde_json::json!(access_level_id), + ); if recursive { data.insert("lower".to_string(), serde_json::json!(1)); } diff --git a/src/commands/config.rs b/src/commands/config.rs index fa76e08..e39b1d9 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -1,7 +1,7 @@ +use anyhow::bail; use configparser::ini::Ini; use std::fs; use std::path::PathBuf; -use anyhow::{bail}; fn config_path() -> PathBuf { crate::settings::SETTINGS.config_dirname.join("config.ini") diff --git a/src/commands/cp.rs b/src/commands/cp.rs index f68541d..8fa7775 100644 --- a/src/commands/cp.rs +++ b/src/commands/cp.rs @@ -1,15 +1,11 @@ use crate::cache::{create_authenticated_conn, load_cache_with_token_refresh}; use crate::commands::shared::{ - find_file_by_name, find_folder, find_lab_in_cache, - find_subfolder_by_name, nfc, parse_remote_path, + find_file_by_name, find_folder, find_lab_in_cache, find_subfolder_by_name, nfc, + parse_remote_path, }; -use anyhow::{bail}; +use anyhow::bail; -pub async fn cp( - src_path: &str, - dest_path: &str, - recursive: bool, -) -> Result<(), anyhow::Error> { +pub async fn cp(src_path: &str, dest_path: &str, recursive: bool) -> Result<(), anyhow::Error> { let (s_remote, s_lab, s_path) = parse_remote_path(src_path)?; let dest_ends_with_slash = dest_path.ends_with('/'); let (d_remote, d_lab, d_path) = parse_remote_path(dest_path)?; @@ -51,7 +47,11 @@ pub async fn cp( bail!("File `{}` already exists.", d_basename); } if find_subfolder_by_name(&d_parent_folder.sub_folders, &d_basename).is_some() { - bail!("Cannot overwrite non-folder `{}` with folder `{}`.", d_basename, d_path); + bail!( + "Cannot overwrite non-folder `{}` with folder `{}`.", + d_basename, + d_path + ); } // No-op if source and destination are identical if s_parent_folder.id == d_parent_folder.id && d_basename == s_basename { @@ -81,13 +81,21 @@ pub async fn cp( } let src_folder_id = src_folder.id.clone(); if find_file_by_name(&d_parent_files, &d_basename).is_some() { - bail!("Cannot overwrite non-folder `{}` with folder `{}`.", d_basename, s_path); + bail!( + "Cannot overwrite non-folder `{}` with folder `{}`.", + d_basename, + s_path + ); } if let Some(d_folder) = find_subfolder_by_name(&d_parent_folder.sub_folders, &d_basename) { if d_folder.id == src_folder_id { bail!("`{}` and `{}` are the same folder.", s_path, s_path); } - bail!("Cannot move `{}` to `{}`: Folder not empty.", s_path, d_path); + bail!( + "Cannot move `{}` to `{}`: Folder not empty.", + s_path, + d_path + ); } // No-op if source and destination are identical if s_parent_folder.id == d_parent_folder.id && s_basename == d_basename { diff --git a/src/commands/download.rs b/src/commands/download.rs index 86f05ac..e1802f9 100644 --- a/src/commands/download.rs +++ b/src/commands/download.rs @@ -1,13 +1,12 @@ use crate::cache::{create_authenticated_conn, load_cache_with_token_refresh}; use crate::commands::shared::{ - find_file_by_name, find_folder, find_lab_in_cache, - find_subfolder_by_name, parse_remote_path, + find_file_by_name, find_folder, find_lab_in_cache, find_subfolder_by_name, parse_remote_path, }; use crate::connection::MDRSConnection; +use anyhow::{anyhow, bail}; use futures::stream::{FuturesUnordered, StreamExt}; use std::path::PathBuf; use std::sync::Arc; -use anyhow::{anyhow, bail}; pub async fn download( remote_path: &str, @@ -93,8 +92,7 @@ pub async fn download( let dir_files = conn.list_all_files(&folder_id).await?; // Download files in this folder (up to 10 concurrent). - let mut futs: FuturesUnordered> = - FuturesUnordered::new(); + let mut futs: FuturesUnordered> = FuturesUnordered::new(); for f in &dir_files { if is_excluded(&excludes, &lab.name, &folder.path, Some(&f.name)) { continue; @@ -119,7 +117,10 @@ pub async fn download( // (connection pool) while supplying a fresh Bearer token. let task_conn = match load_cache_with_token_refresh(&remote).await { Ok(c) => conn.with_token(c.token.access), - Err(e) => { eprintln!("Error: {}", e); return; } + Err(e) => { + eprintln!("Error: {}", e); + return; + } }; let dest_str = dest_path.to_string_lossy().to_string(); match task_conn.download_file(&url, &dest_str).await { @@ -163,18 +164,18 @@ pub async fn download( /// Return true if the given lab/folder/file path matches any exclude pattern. /// Constructs: `/{lab_name}{folder_path}{file_name}` lowercased, trailing slash stripped. /// `folder_path` is expected to already start (and end) with "/". -fn is_excluded(excludes: &[String], lab_name: &str, folder_path: &str, file_name: Option<&str>) -> bool { +fn is_excluded( + excludes: &[String], + lab_name: &str, + folder_path: &str, + file_name: Option<&str>, +) -> bool { if excludes.is_empty() { return false; } - let path = format!( - "/{}{}{}", - lab_name, - folder_path, - file_name.unwrap_or("") - ) - .trim_end_matches('/') - .to_lowercase(); + let path = format!("/{}{}{}", lab_name, folder_path, file_name.unwrap_or("")) + .trim_end_matches('/') + .to_lowercase(); excludes.iter().any(|e| e == &path) } diff --git a/src/commands/file_metadata.rs b/src/commands/file_metadata.rs index 64e8bb8..d661ba9 100644 --- a/src/commands/file_metadata.rs +++ b/src/commands/file_metadata.rs @@ -1,9 +1,8 @@ use crate::cache::{create_authenticated_conn, load_cache_with_token_refresh}; -use anyhow::{anyhow}; use crate::commands::shared::{ - find_file_by_name, find_folder, find_lab_in_cache, - parse_remote_path, + find_file_by_name, find_folder, find_lab_in_cache, parse_remote_path, }; +use anyhow::anyhow; pub async fn file_metadata(remote_path: &str, password: Option<&str>) -> Result<(), anyhow::Error> { let (remote, labname, r_path) = parse_remote_path(remote_path)?; diff --git a/src/commands/labs.rs b/src/commands/labs.rs index 95ebfdf..3b7759a 100644 --- a/src/commands/labs.rs +++ b/src/commands/labs.rs @@ -18,8 +18,12 @@ pub async fn labs(remote: &str) -> Result<(), anyhow::Error> { println!( "{: Result<(), anyhow::Error> { for lab in &labs.items { println!( "{: Result<(), anyhow::Error> { +pub async fn login(username: &str, password: &str, remote: &str) -> Result<(), anyhow::Error> { // resolve remote label to URL from config let url_opt = crate::commands::config::get_remote_url(remote)?; let base_url = url_opt.ok_or_else(|| anyhow!("Remote host `{}` is not configured", remote))?; @@ -141,4 +136,3 @@ pub async fn login( println!("Login Successful"); Ok(()) } - diff --git a/src/commands/ls.rs b/src/commands/ls.rs index fa1813e..8272efe 100644 --- a/src/commands/ls.rs +++ b/src/commands/ls.rs @@ -1,11 +1,9 @@ +use crate::cache::{create_authenticated_conn, load_cache_with_token_refresh}; +use crate::commands::shared::{find_folder, find_lab_in_cache, fmt_datetime, parse_remote_path}; +use crate::connection::MDRSConnection; use crate::models::file::File; use crate::models::folder::{FolderDetail, FolderSimple}; -use crate::cache::{create_authenticated_conn, load_cache_with_token_refresh}; -use crate::commands::shared::{ - find_folder, find_lab_in_cache, fmt_datetime, parse_remote_path, -}; -use crate::connection::MDRSConnection; -use serde_json::{json, Value}; +use serde_json::{Value, json}; use std::future::Future; use std::pin::Pin; @@ -214,11 +212,7 @@ fn file_to_json(f: &File, base_url: &str) -> Value { let download_url = if f.download_url.starts_with("http") { f.download_url.clone() } else { - format!( - "{}{}", - base_url.trim_end_matches('/'), - f.download_url - ) + format!("{}{}", base_url.trim_end_matches('/'), f.download_url) }; json!({ "id": f.id, diff --git a/src/commands/metadata.rs b/src/commands/metadata.rs index 8be4088..98bf01a 100644 --- a/src/commands/metadata.rs +++ b/src/commands/metadata.rs @@ -1,7 +1,5 @@ use crate::cache::{create_authenticated_conn, load_cache_with_token_refresh}; -use crate::commands::shared::{ - find_folder, find_lab_in_cache, parse_remote_path, -}; +use crate::commands::shared::{find_folder, find_lab_in_cache, parse_remote_path}; pub async fn metadata(remote_path: &str, password: Option<&str>) -> Result<(), anyhow::Error> { let (remote, labname, folder_path) = parse_remote_path(remote_path)?; diff --git a/src/commands/mkdir.rs b/src/commands/mkdir.rs index d977791..83c0a8d 100644 --- a/src/commands/mkdir.rs +++ b/src/commands/mkdir.rs @@ -1,7 +1,7 @@ use crate::cache::{create_authenticated_conn, load_cache_with_token_refresh}; use crate::commands::shared::{ - find_file_by_name, find_folder, find_lab_in_cache, - find_subfolder_by_name, nfc, parse_remote_path, + find_file_by_name, find_folder, find_lab_in_cache, find_subfolder_by_name, nfc, + parse_remote_path, }; use anyhow::{anyhow, bail}; diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 9a0a45a..4844e5a 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -14,8 +14,8 @@ pub mod metadata; pub mod mkdir; pub mod mv; pub mod rm; +pub mod selfupdate; pub mod shared; pub mod upload; pub mod version; pub mod whoami; -pub mod selfupdate; diff --git a/src/commands/mv.rs b/src/commands/mv.rs index 790554e..75165ad 100644 --- a/src/commands/mv.rs +++ b/src/commands/mv.rs @@ -1,9 +1,9 @@ use crate::cache::{create_authenticated_conn, load_cache_with_token_refresh}; use crate::commands::shared::{ - find_file_by_name, find_folder, find_lab_in_cache, - find_subfolder_by_name, nfc, parse_remote_path, + find_file_by_name, find_folder, find_lab_in_cache, find_subfolder_by_name, nfc, + parse_remote_path, }; -use anyhow::{bail}; +use anyhow::bail; pub async fn mv(src_path: &str, dest_path: &str) -> Result<(), anyhow::Error> { let (s_remote, s_lab, s_path) = parse_remote_path(src_path)?; @@ -47,7 +47,11 @@ pub async fn mv(src_path: &str, dest_path: &str) -> Result<(), anyhow::Error> { bail!("File `{}` already exists.", d_basename); } if find_subfolder_by_name(&d_parent_folder.sub_folders, &d_basename).is_some() { - bail!("Cannot overwrite non-folder `{}` with folder `{}`.", d_basename, d_path); + bail!( + "Cannot overwrite non-folder `{}` with folder `{}`.", + d_basename, + d_path + ); } // No-op if source and destination are identical if s_parent_folder.id == d_parent_folder.id && d_basename == s_basename { @@ -74,13 +78,21 @@ pub async fn mv(src_path: &str, dest_path: &str) -> Result<(), anyhow::Error> { }; let src_folder_id = src_folder.id.clone(); if find_file_by_name(&d_parent_files, &d_basename).is_some() { - bail!("Cannot overwrite non-folder `{}` with folder `{}`.", d_basename, s_path); + bail!( + "Cannot overwrite non-folder `{}` with folder `{}`.", + d_basename, + s_path + ); } if let Some(d_folder) = find_subfolder_by_name(&d_parent_folder.sub_folders, &d_basename) { if d_folder.id == src_folder_id { bail!("`{}` and `{}` are the same folder.", s_path, s_path); } - bail!("Cannot move `{}` to `{}`: Folder not empty.", s_path, d_path); + bail!( + "Cannot move `{}` to `{}`: Folder not empty.", + s_path, + d_path + ); } // No-op if source and destination are identical if s_parent_folder.id == d_parent_folder.id && s_basename == d_basename { diff --git a/src/commands/rm.rs b/src/commands/rm.rs index 0ea4c5c..d592d65 100644 --- a/src/commands/rm.rs +++ b/src/commands/rm.rs @@ -1,7 +1,6 @@ use crate::cache::{create_authenticated_conn, load_cache_with_token_refresh}; use crate::commands::shared::{ - find_file_by_name, find_folder, find_lab_in_cache, - find_subfolder_by_name, parse_remote_path, + find_file_by_name, find_folder, find_lab_in_cache, find_subfolder_by_name, parse_remote_path, }; use anyhow::{anyhow, bail}; diff --git a/src/commands/selfupdate.rs b/src/commands/selfupdate.rs index ae87ba8..1e41135 100644 --- a/src/commands/selfupdate.rs +++ b/src/commands/selfupdate.rs @@ -50,7 +50,11 @@ fn is_newer(current: &str, latest: &str) -> bool { /// Extract the binary named `bin_name` from a `.tar.gz` archive at `archive_path` /// and write it to `dest_path`. -fn extract_from_tar_gz(archive_path: &Path, bin_name: &str, dest_path: &Path) -> anyhow::Result<()> { +fn extract_from_tar_gz( + archive_path: &Path, + bin_name: &str, + dest_path: &Path, +) -> anyhow::Result<()> { use flate2::read::GzDecoder; use tar::Archive; @@ -98,12 +102,12 @@ fn extract_from_zip(archive_path: &Path, bin_name: &str, dest_path: &Path) -> an pub async fn selfupdate(yes: bool) -> anyhow::Result<()> { let current_version = env!("CARGO_PKG_VERSION"); - println!("Checking for updates (current version: {current_version}, target: {BUILD_TARGET})..."); - - let api_url = format!( - "{GITEA_HOST}/api/v1/repos/{REPO_OWNER}/{REPO_NAME}/releases?limit=1" + println!( + "Checking for updates (current version: {current_version}, target: {BUILD_TARGET})..." ); + let api_url = format!("{GITEA_HOST}/api/v1/repos/{REPO_OWNER}/{REPO_NAME}/releases?limit=1"); + let client = reqwest::Client::new(); let mut req = client .get(&api_url) @@ -178,10 +182,7 @@ pub async fn selfupdate(yes: bool) -> anyhow::Result<()> { let download_resp = download_req.send().await?; if !download_resp.status().is_success() { - bail!( - "Failed to download asset: HTTP {}", - download_resp.status() - ); + bail!("Failed to download asset: HTTP {}", download_resp.status()); } let bytes = download_resp.bytes().await?; @@ -215,4 +216,3 @@ pub async fn selfupdate(yes: bool) -> anyhow::Result<()> { println!("Successfully updated to version {latest_version}."); Ok(()) } - diff --git a/src/commands/shared.rs b/src/commands/shared.rs index 3765dee..5c14b62 100644 --- a/src/commands/shared.rs +++ b/src/commands/shared.rs @@ -2,17 +2,15 @@ use crate::cache::{Cache, CacheLaboratory}; use crate::connection::MDRSConnection; use crate::models::file::File; use crate::models::folder::{FolderDetail, FolderSimple}; -use unicode_normalization::UnicodeNormalization; use anyhow::{anyhow, bail}; +use unicode_normalization::UnicodeNormalization; // --------------------------------------------------------------------------- // Path helpers // --------------------------------------------------------------------------- /// Parse "remote:/labname/path/" into (remote, labname, folder_path). -pub fn parse_remote_path( - remote_path: &str, -) -> Result<(String, String, String), anyhow::Error> { +pub fn parse_remote_path(remote_path: &str) -> Result<(String, String, String), anyhow::Error> { let parts: Vec<&str> = remote_path.splitn(2, ':').collect(); if parts.len() != 2 { bail!("remote_path must be in the form 'remote:/labname/path/'"); @@ -75,13 +73,20 @@ pub async fn find_folder( bail!("Folder `{}` not found.", path); } if folders.len() != 1 { - bail!("Ambiguous path `{}`: {} folders matched.", path, folders.len()); + bail!( + "Ambiguous path `{}`: {} folders matched.", + path, + folders.len() + ); } let folder_simple = &folders[0]; if folder_simple.lock { match password { None => { - bail!("Folder `{}` is locked. Use -p/--password to provide a password.", path); + bail!( + "Folder `{}` is locked. Use -p/--password to provide a password.", + path + ); } Some(pw) => conn.folder_auth(&folder_simple.id, pw).await?, } diff --git a/src/commands/upload.rs b/src/commands/upload.rs index aa79d1f..2985b90 100644 --- a/src/commands/upload.rs +++ b/src/commands/upload.rs @@ -1,14 +1,13 @@ -use crate::models::folder::FolderSimple; use crate::cache::{create_authenticated_conn, load_cache_with_token_refresh}; use crate::commands::shared::{ - find_file_by_name, find_folder, find_lab_in_cache, - nfc, parse_remote_path, + find_file_by_name, find_folder, find_lab_in_cache, nfc, parse_remote_path, }; +use crate::models::folder::FolderSimple; +use anyhow::{anyhow, bail}; use futures::stream::{FuturesUnordered, StreamExt}; use std::path::PathBuf; use std::sync::Arc; use tokio::fs; -use anyhow::{anyhow, bail}; pub async fn upload( local_path: &str, @@ -40,7 +39,8 @@ pub async fn upload( } } } - conn.upload_file(&dest_folder.id, &local.to_string_lossy()).await?; + conn.upload_file(&dest_folder.id, &local.to_string_lossy()) + .await?; println!("{}{}", dest_folder.path, filename); } else if local.is_dir() { if !recursive { @@ -50,13 +50,18 @@ pub async fn upload( // remote_path. E.g. `upload ./mydir remote:/lab/path/` creates // `/lab/path/mydir/` on the remote and uploads into that folder. let local_basename = local.file_name().unwrap().to_string_lossy().to_string(); - let top_remote_id = find_or_create_folder(&conn, &dest_folder.id, &dest_folder.sub_folders, &local_basename).await?; + let top_remote_id = find_or_create_folder( + &conn, + &dest_folder.id, + &dest_folder.sub_folders, + &local_basename, + ) + .await?; let top_folder = conn.retrieve_folder(&top_remote_id).await?; println!("{}", top_folder.path.trim_end_matches('/')); // Iterative depth-first walk: each entry is (local_dir, remote_folder_id) - let mut stack: Vec<(PathBuf, String)> = - vec![(local.to_path_buf(), top_remote_id)]; + let mut stack: Vec<(PathBuf, String)> = vec![(local.to_path_buf(), top_remote_id)]; while let Some((local_dir, remote_id)) = stack.pop() { let folder_detail = conn.retrieve_folder(&remote_id).await?; @@ -77,15 +82,16 @@ pub async fn upload( // Ensure each local sub-directory exists on the remote side for subdir in subdirs { let dirname = subdir.file_name().unwrap().to_string_lossy().to_string(); - let sub_remote_id = find_or_create_folder(&conn, &remote_id, &folder_detail.sub_folders, &dirname).await?; + let sub_remote_id = + find_or_create_folder(&conn, &remote_id, &folder_detail.sub_folders, &dirname) + .await?; let sub_folder = conn.retrieve_folder(&sub_remote_id).await?; println!("{}", sub_folder.path.trim_end_matches('/')); stack.push((subdir, sub_remote_id)); } // Upload files in this directory (up to 10 concurrent) - let mut futs: FuturesUnordered> = - FuturesUnordered::new(); + let mut futs: FuturesUnordered> = FuturesUnordered::new(); for file_path in files { let filename = file_path.file_name().unwrap().to_string_lossy().to_string(); let file_path_str = file_path.to_string_lossy().to_string(); @@ -111,7 +117,10 @@ pub async fn upload( // (connection pool) while supplying a fresh Bearer token. let task_conn = match load_cache_with_token_refresh(&remote).await { Ok(c) => conn.with_token(c.token.access), - Err(e) => { eprintln!("Error: {}", e); return; } + Err(e) => { + eprintln!("Error: {}", e); + return; + } }; match task_conn.upload_file(&folder_id, &file_path_str).await { Ok(_) => println!("{}{}", remote_path_prefix, fname), @@ -138,7 +147,10 @@ async fn find_or_create_folder( existing: &[FolderSimple], name: &str, ) -> Result { - if let Some(sf) = existing.iter().find(|f| nfc(&f.name).to_lowercase() == nfc(name).to_lowercase()) { + if let Some(sf) = existing + .iter() + .find(|f| nfc(&f.name).to_lowercase() == nfc(name).to_lowercase()) + { return Ok(sf.id.clone()); } let resp = conn.create_folder(parent_id, &nfc(name)).await?; diff --git a/src/connection.rs b/src/connection.rs index cd840dd..904c694 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -100,4 +100,3 @@ impl MDRSConnection { .await } } - diff --git a/src/main.rs b/src/main.rs index a7969b9..9943ecb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,10 +13,7 @@ use cli::{Cli, Commands}; use error::handle_error; fn run(cli: Cli) { - let build_rt = || { - tokio::runtime::Runtime::new() - .unwrap_or_else(|e| handle_error(e.into())) - }; + let build_rt = || tokio::runtime::Runtime::new().unwrap_or_else(|e| handle_error(e.into())); match cli.command { Commands::Config(subcmd) => { @@ -150,9 +147,10 @@ fn run(cli: Cli) { remote_path, password, } => { - if let Err(e) = build_rt() - .block_on(commands::metadata::metadata(&remote_path, password.as_deref())) - { + if let Err(e) = build_rt().block_on(commands::metadata::metadata( + &remote_path, + password.as_deref(), + )) { handle_error(e); } } @@ -169,10 +167,11 @@ fn run(cli: Cli) { handle_error(e); } } - Commands::Mv { src_path, dest_path } => { - if let Err(e) = - build_rt().block_on(commands::mv::mv(&src_path, &dest_path)) - { + Commands::Mv { + src_path, + dest_path, + } => { + if let Err(e) = build_rt().block_on(commands::mv::mv(&src_path, &dest_path)) { handle_error(e); } } @@ -181,8 +180,7 @@ fn run(cli: Cli) { dest_path, recursive, } => { - if let Err(e) = - build_rt().block_on(commands::cp::cp(&src_path, &dest_path, recursive)) + if let Err(e) = build_rt().block_on(commands::cp::cp(&src_path, &dest_path, recursive)) { handle_error(e); } diff --git a/src/token.rs b/src/token.rs index dd4c688..152864e 100644 --- a/src/token.rs +++ b/src/token.rs @@ -1,8 +1,8 @@ // JWT utilities for token expiry checking (no signature verification required) +use anyhow::{anyhow, bail}; use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD}; use std::time::{SystemTime, UNIX_EPOCH}; -use anyhow::{anyhow, bail}; fn now_secs() -> i64 { SystemTime::now()