Files
mdrs-client-rust/src/main.rs
T
orrisroot 769a5a68e2 refactor: unify error handling with anyhow and add From conversions
Phase 5: Replace all Box<dyn Error> return types with anyhow::Result<T>
throughout the codebase. Replace string-based Err("msg".into()) and
format!().into() patterns with bail!() and anyhow!() macros. Fix
dirs::home_dir().unwrap() in settings.rs to use a fallback path instead
of panicking when HOME is unset. Remove stray use std::error::Error
imports no longer needed.

Phase 6: Add From<&User> for CacheUser in models/user.rs and
From<&Laboratory>/From<&Laboratories> for CacheLaboratory/CacheLabsWrapper
in models/laboratory.rs. Simplify commands/login.rs to use .into()
conversions, removing the redundant to_cache_user() and to_cache_labs()
helper functions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-20 14:19:10 +09:00

221 lines
6.4 KiB
Rust

mod api;
mod cache;
mod cli;
mod commands;
mod connection;
mod error;
mod models;
mod settings;
mod token;
use clap::Parser;
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()))
};
match cli.command {
Commands::Config(subcmd) => {
use commands::config_subcommand::ConfigSubcommand;
match subcmd {
ConfigSubcommand::Create(args) => {
if let Err(e) = commands::config::config_create(&args.remote, &args.url) {
handle_error(e);
}
}
ConfigSubcommand::Update(args) => {
if let Err(e) = commands::config::config_update(&args.remote, &args.url) {
handle_error(e);
}
}
ConfigSubcommand::List(_) => {
if let Err(e) = commands::config::config_list() {
handle_error(e);
}
}
ConfigSubcommand::Delete(args) => {
if let Err(e) = commands::config::config_delete(&args.remote) {
handle_error(e);
}
}
}
}
Commands::Login {
username,
password,
remote,
} => {
let remote = remote.trim_end_matches(':').to_string();
if let Err(e) = build_rt().block_on(commands::login::run_login(
username.as_deref(),
password.as_deref(),
&remote,
)) {
handle_error(e);
}
}
Commands::Logout { remote } => {
let remote = remote.trim_end_matches(':').to_string();
if let Err(e) = commands::logout::logout(&remote) {
handle_error(e);
}
}
Commands::Upload {
local_path,
remote_path,
recursive,
skip_if_exists,
} => {
if let Err(e) = build_rt().block_on(commands::upload::upload(
&local_path,
&remote_path,
recursive,
skip_if_exists,
)) {
handle_error(e);
}
}
Commands::Download {
remote_path,
local_path,
recursive,
skip_if_exists,
password,
exclude,
} => {
let excludes: Vec<String> = exclude
.iter()
.map(|e| e.trim_end_matches('/').to_lowercase())
.collect();
if let Err(e) = build_rt().block_on(commands::download::download(
&remote_path,
&local_path,
recursive,
skip_if_exists,
password.as_deref(),
excludes,
)) {
handle_error(e);
}
}
Commands::Ls {
remote_path,
password,
json,
recursive,
quiet,
} => {
if let Err(e) = build_rt().block_on(commands::ls::ls(
&remote_path,
password.as_deref(),
json,
recursive,
quiet,
)) {
handle_error(e);
}
}
Commands::Whoami { remote } => {
let remote = remote.trim_end_matches(':').to_string();
if let Err(e) = build_rt().block_on(commands::whoami::whoami(&remote)) {
handle_error(e);
}
}
Commands::Labs { remote } => {
let remote = remote.trim_end_matches(':').to_string();
if let Err(e) = build_rt().block_on(commands::labs::labs(&remote)) {
handle_error(e);
}
}
Commands::Chacl {
access_level_key,
recursive,
password,
remote_path,
} => {
if let Err(e) = build_rt().block_on(commands::chacl::chacl(
&remote_path,
&access_level_key,
recursive,
password.as_deref(),
)) {
handle_error(e);
}
}
Commands::Metadata {
remote_path,
password,
} => {
if let Err(e) = build_rt()
.block_on(commands::metadata::metadata(&remote_path, password.as_deref()))
{
handle_error(e);
}
}
Commands::Mkdir { remote_path } => {
if let Err(e) = build_rt().block_on(commands::mkdir::mkdir(&remote_path)) {
handle_error(e);
}
}
Commands::Rm {
recursive,
remote_path,
} => {
if let Err(e) = build_rt().block_on(commands::rm::rm(&remote_path, recursive)) {
handle_error(e);
}
}
Commands::Mv { src_path, dest_path } => {
if let Err(e) =
build_rt().block_on(commands::mv::mv(&src_path, &dest_path))
{
handle_error(e);
}
}
Commands::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);
}
}
Commands::FileMetadata {
remote_path,
password,
} => {
if let Err(e) = build_rt().block_on(commands::file_metadata::file_metadata(
&remote_path,
password.as_deref(),
)) {
handle_error(e);
}
}
Commands::Version => {
commands::version::version();
}
Commands::SelfUpdate { yes } => {
if let Err(e) = build_rt().block_on(commands::selfupdate::selfupdate(yes)) {
handle_error(e.into());
}
}
}
}
fn main() {
// Load .env file from the current directory (silently ignore if not present).
dotenvy::dotenv().ok();
// Exit with code 130 on Ctrl+C, matching Python's KeyboardInterrupt handling.
ctrlc::set_handler(|| std::process::exit(130)).ok();
run(Cli::parse());
}