From 5c835327197960d3469c1f6f57b6f2546f44c9ed Mon Sep 17 00:00:00 2001 From: qwjyh Date: Tue, 27 Feb 2024 13:07:49 +0900 Subject: [PATCH] Revert "refactor: separate subcommands" This reverts commit 4502113e41d9daf88c3ff8482d1a74a16cfa805f. --- src/cmd_init.rs | 80 ------------- src/cmd_options.rs | 68 ----------- src/cmd_storage.rs | 136 --------------------- src/main.rs | 289 ++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 275 insertions(+), 298 deletions(-) delete mode 100644 src/cmd_init.rs delete mode 100644 src/cmd_options.rs delete mode 100644 src/cmd_storage.rs diff --git a/src/cmd_init.rs b/src/cmd_init.rs deleted file mode 100644 index b037c64..0000000 --- a/src/cmd_init.rs +++ /dev/null @@ -1,80 +0,0 @@ -use std::{path::{self, Path}, fs::File, io::{BufWriter, Write}}; -use git2::Repository; - -use anyhow::{Result, Context, anyhow}; - -use crate::{add_and_commit, full_status, set_device_name, devices::{Device, get_devices, write_devices, DEVICESFILE}}; - -pub fn cmd_init(repo_url: Option, config_dir: &path::PathBuf) -> Result<()> { - let is_first_device: bool; - // get repo or initialize it - let repo = match repo_url { - Some(repo_url) => { - trace!("repo: {}", repo_url); - let repo = Repository::clone(&repo_url, &config_dir)?; - is_first_device = false; - repo - } - None => { - trace!("No repo provided"); - println!("Initializing for the first device..."); - - // create repository - let repo = Repository::init(&config_dir)?; - - // set up gitignore - { - let f = File::create(&config_dir.join(".gitignore"))?; - { - let mut buf = BufWriter::new(f); - buf.write("devname".as_bytes())?; - } - add_and_commit(&repo, Path::new(".gitignore"), "Add devname to gitignore.")?; - full_status(&repo)?; - } - is_first_device = true; - repo - } - }; - full_status(&repo)?; - - // set device name - let device = set_device_name()?; - - // save devname - let devname_path = &config_dir.join("devname"); - { - let f = File::create(devname_path) - .context("Failed to create a file to store local device name")?; - let writer = BufWriter::new(f); - serde_yaml::to_writer(writer, &device.name()).unwrap(); - }; - full_status(&repo)?; - - // Add new device to devices.yml - { - let mut devices: Vec = if is_first_device { - vec![] - } else { - get_devices(&config_dir)? - }; - trace!("devices: {:?}", devices); - if devices.iter().any(|x| x.name() == device.name()) { - return Err(anyhow!("device name is already used.")); - } - devices.push(device.clone()); - trace!("Devices: {:?}", devices); - write_devices(&config_dir, devices)?; - } - full_status(&repo)?; - - // commit - add_and_commit( - &repo, - &Path::new(DEVICESFILE), - &format!("Add new devname: {}", &device.name()), - )?; - println!("Device added"); - full_status(&repo)?; - Ok(()) -} diff --git a/src/cmd_options.rs b/src/cmd_options.rs deleted file mode 100644 index 6f274ad..0000000 --- a/src/cmd_options.rs +++ /dev/null @@ -1,68 +0,0 @@ -/// CLI arguments definition - -use std::path::PathBuf; - -use clap::{Parser, Subcommand}; -use clap_verbosity_flag::Verbosity; - -use crate::storages::StorageType; - -#[derive(Parser)] -#[command(author, version, about, long_about = None)] -pub(crate) struct Cli { - #[command(subcommand)] - pub command: Commands, - - #[command(flatten)] - pub verbose: Verbosity, -} - -#[derive(Subcommand)] -pub(crate) enum Commands { - /// Initialize for this device. - /// Provide `repo_url` to use existing repository, otherwise this device will be configured as the - /// first device. - Init { - repo_url: Option, // url? - }, - - /// Manage storages. - Storage(StorageArgs), - - /// Print config dir. - Path {}, - - /// Sync with git repo. - Sync {}, -} - -#[derive(clap::Args)] -#[command(args_conflicts_with_subcommands = true)] -pub(crate) struct StorageArgs { - #[command(subcommand)] - pub(crate) command: StorageCommands, -} - -#[derive(Subcommand)] -pub(crate) enum StorageCommands { - /// Add new storage. - Add { - #[arg(value_enum)] - storage_type: StorageType, - - // TODO: set this require and select matching disk for physical - #[arg(short, long, value_name = "PATH")] - path: Option, - }, - /// List all storages. - List {}, - /// Add new device-specific name to existing storage. - /// For physical disk, the name is taken from system info automatically. - Bind { storage: String }, -} - -#[test] -fn verify_cli() { - use clap::CommandFactory; - Cli::command().debug_assert() -} diff --git a/src/cmd_storage.rs b/src/cmd_storage.rs deleted file mode 100644 index 1dfa339..0000000 --- a/src/cmd_storage.rs +++ /dev/null @@ -1,136 +0,0 @@ -use std::{path::{self, Path}, collections::HashMap, io::ErrorKind}; - -use anyhow::{Result, Context, Ok}; -use clap::{CommandFactory, error}; -use git2::Repository; -use inquire::Text; -use sysinfo::{SystemExt, DiskExt}; - -use crate::{anyhow, StorageCommands, cmd_options::{StorageArgs, Cli}, storages::{Storage, get_storages, StorageType, physical_drive_partition::{select_physical_storage, select_sysinfo_disk}, self, write_storages, STORAGESFILE, StorageExt}, devices::get_device, ask_unique_name, add_and_commit}; - -pub fn cmd_storage(storage: StorageArgs, config_dir: &path::PathBuf) -> Result<()> { - let repo = Repository::open(&config_dir) - .context("Repository doesn't exist. Please run init to initialize the repository.")?; - trace!("repo state: {:?}", repo.state()); - match storage.command { - StorageCommands::Add { storage_type, path } => { - trace!("Storage Add {:?}, {:?}", storage_type, path); - // Get storages - // let mut storages: Vec = get_storages(&config_dir)?; - let mut storages: HashMap = get_storages(&config_dir)?; - trace!("found storages: {:?}", storages); - - let device = get_device(&config_dir)?; - let (key, storage) = match storage_type { - StorageType::Physical => { - // select storage - let (key, storage) = select_physical_storage(device, &storages)?; - println!("storage: {}: {:?}", key, storage); - (key, Storage::PhysicalStorage(storage)) - } - StorageType::SubDirectory => { - if storages.is_empty() { - return Err(anyhow!( - "No storages found. Please add at least 1 physical storage first." - )); - } - let path = path.unwrap_or_else(|| { - let mut cmd = Cli::command(); - cmd.error( - error::ErrorKind::MissingRequiredArgument, - " is required with sub-directory", - ) - .exit(); - }); - trace!("SubDirectory arguments: path: {:?}", path); - // Nightly feature std::path::absolute - let path = path.canonicalize()?; - trace!("canonicalized: path: {:?}", path); - - let key_name = ask_unique_name(&storages, "sub-directory".to_string())?; - let notes = Text::new("Notes for this sub-directory:").prompt()?; - let storage = storages::directory::Directory::try_from_device_path( - key_name.clone(), - path, - notes, - &device, - &storages, - )?; - (key_name, Storage::SubDirectory(storage)) - } - StorageType::Online => todo!(), - }; - - // add to storages - storages.insert(key.clone(), storage); - trace!("updated storages: {:?}", storages); - - // write to file - write_storages(&config_dir, storages)?; - - // commit - add_and_commit( - &repo, - &Path::new(STORAGESFILE), - &format!("Add new storage(physical drive): {}", key), - )?; - - println!("Added new storage."); - trace!("Finished adding storage"); - Ok(()) - } - StorageCommands::List {} => { - // Get storages - let storages: HashMap = get_storages(&config_dir)?; - trace!("found storages: {:?}", storages); - let device = get_device(&config_dir)?; - for (k, storage) in &storages { - println!("{}: {}", k, storage); - println!(" {}", storage.mount_path(&device, &storages)?.display()); - } - Ok(()) - } - StorageCommands::Bind { - storage: storage_name, - } => { - // get storages - let mut storages: HashMap = get_storages(&config_dir)?; - let commit_comment = { - // find matching storage - let storage = storages - .get_mut(&storage_name) - .context(format!("No storage has name {}", storage_name))?; - // get disk from sysinfo - let mut sysinfo = sysinfo::System::new_all(); - sysinfo.refresh_disks(); - let disk = select_sysinfo_disk(&sysinfo)?; - let system_name = disk - .name() - .to_str() - .context("Failed to convert disk name to valid string")?; - // add to storages - storage.bind_device(disk, &config_dir)?; - trace!("storage: {}", storage); - format!("{} to {}", system_name, storage.name()) - }; - trace!("bound new system name to the storage"); - trace!("storages: {:#?}", storages); - - write_storages(&config_dir, storages)?; - // commit - add_and_commit( - &repo, - &Path::new(STORAGESFILE), - &format!( - "Bound new storage name to physical drive ({})", - commit_comment - ), - )?; - println!( - "Bound new storage name to physical drive ({})", - commit_comment - ); - Ok(()) - } - } -} diff --git a/src/main.rs b/src/main.rs index f38b406..b868d2a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,24 +12,83 @@ extern crate log; extern crate dirs; use anyhow::{anyhow, Context, Result}; -use clap::{CommandFactory, Parser}; +use clap::error::ErrorKind; +use clap::{CommandFactory, Parser, Subcommand}; +use clap_verbosity_flag::Verbosity; use git2::{Commit, Oid, Repository}; use inquire::{validator::Validation, Text}; use serde_yaml; use std::collections::HashMap; -use std::path::Path; - -use crate::cmd_options::{Cli, Commands, StorageCommands}; -use crate::devices::get_device; -use crate::storages::{ - get_storages, physical_drive_partition::*, write_storages, Storage, StorageExt, StorageType, - STORAGESFILE, +use std::path::PathBuf; +use std::{env, io::BufReader, path::Path}; +use std::{ + ffi::OsString, + io::{self, BufWriter}, }; -use devices::Device; +use std::{fmt::Debug, fs::File}; +use std::{fs, io::prelude::*}; +use sysinfo::{Disk, DiskExt, SystemExt}; + +use crate::storages::{ + directory::Directory, get_storages, local_info, online_storage, physical_drive_partition::*, + write_storages, Storage, StorageExt, StorageType, STORAGESFILE, +}; +use devices::{Device, DEVICESFILE, *}; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + #[command(subcommand)] + command: Commands, + + #[command(flatten)] + verbose: Verbosity, +} + +#[derive(Subcommand)] +enum Commands { + /// Initialize for this device. + /// Provide `repo_url` to use existing repository, otherwise this device will be configured as the + /// first device. + Init { + repo_url: Option, // url? + }, + + /// Manage storages. + Storage(StorageArgs), + + /// Print config dir. + Path {}, + + /// Sync with git repo. + Sync {}, +} + +#[derive(clap::Args)] +#[command(args_conflicts_with_subcommands = true)] +struct StorageArgs { + #[command(subcommand)] + command: StorageCommands, +} + +#[derive(Subcommand)] +enum StorageCommands { + /// Add new storage. + Add { + #[arg(value_enum)] + storage_type: StorageType, + + // TODO: set this require and select matching disk for physical + #[arg(short, long, value_name = "PATH")] + path: Option, + }, + /// List all storages. + List {}, + /// Add new device-specific name to existing storage. + /// For physical disk, the name is taken from system info automatically. + Bind { storage: String }, +} -mod cmd_init; -mod cmd_options; -mod cmd_storage; mod devices; mod storages; @@ -49,8 +108,204 @@ fn main() -> Result<()> { trace!("Config dir: {:?}", config_dir); match cli.command { - Commands::Init { repo_url } => cmd_init::cmd_init(repo_url, &config_dir)?, - Commands::Storage(storage) => cmd_storage::cmd_storage(storage, &config_dir)?, + Commands::Init { repo_url } => { + let is_first_device: bool; + // get repo or initialize it + let repo = match repo_url { + Some(repo_url) => { + trace!("repo: {}", repo_url); + let repo = Repository::clone(&repo_url, &config_dir)?; + is_first_device = false; + repo + } + None => { + trace!("No repo provided"); + println!("Initializing for the first device..."); + + // create repository + let repo = Repository::init(&config_dir)?; + + // set up gitignore + { + let f = File::create(&config_dir.join(".gitignore"))?; + { + let mut buf = BufWriter::new(f); + buf.write("devname".as_bytes())?; + } + add_and_commit( + &repo, + Path::new(".gitignore"), + "Add devname to gitignore.", + )?; + full_status(&repo)?; + } + is_first_device = true; + repo + } + }; + full_status(&repo)?; + + // set device name + let device = set_device_name()?; + + // save devname + let devname_path = &config_dir.join("devname"); + { + let f = File::create(devname_path) + .context("Failed to create a file to store local device name")?; + let writer = BufWriter::new(f); + serde_yaml::to_writer(writer, &device.name()).unwrap(); + }; + full_status(&repo)?; + + // Add new device to devices.yml + { + let mut devices: Vec = if is_first_device { + vec![] + } else { + get_devices(&config_dir)? + }; + trace!("devices: {:?}", devices); + if devices.iter().any(|x| x.name() == device.name()) { + return Err(anyhow!("device name is already used.")); + } + devices.push(device.clone()); + trace!("Devices: {:?}", devices); + write_devices(&config_dir, devices)?; + } + full_status(&repo)?; + + // commit + add_and_commit( + &repo, + &Path::new(DEVICESFILE), + &format!("Add new devname: {}", &device.name()), + )?; + println!("Device added"); + full_status(&repo)?; + } + Commands::Storage(storage) => { + let repo = Repository::open(&config_dir).context( + "Repository doesn't exist. Please run init to initialize the repository.", + )?; + trace!("repo state: {:?}", repo.state()); + match storage.command { + StorageCommands::Add { storage_type, path } => { + trace!("Storage Add {:?}, {:?}", storage_type, path); + // Get storages + // let mut storages: Vec = get_storages(&config_dir)?; + let mut storages: HashMap = get_storages(&config_dir)?; + trace!("found storages: {:?}", storages); + + let device = get_device(&config_dir)?; + let (key, storage) = match storage_type { + StorageType::Physical => { + // select storage + let (key, storage) = select_physical_storage(device, &storages)?; + println!("storage: {}: {:?}", key, storage); + (key, Storage::PhysicalStorage(storage)) + } + StorageType::SubDirectory => { + if storages.is_empty() { + return Err(anyhow!("No storages found. Please add at least 1 physical storage first.")); + } + let path = path.unwrap_or_else(|| { + let mut cmd = Cli::command(); + cmd.error( + ErrorKind::MissingRequiredArgument, + " is required with sub-directory", + ) + .exit(); + }); + trace!("SubDirectory arguments: path: {:?}", path); + // Nightly feature std::path::absolute + let path = path.canonicalize()?; + trace!("canonicalized: path: {:?}", path); + + let key_name = ask_unique_name(&storages, "sub-directory".to_string())?; + let notes = Text::new("Notes for this sub-directory:").prompt()?; + let storage = storages::directory::Directory::try_from_device_path( + key_name.clone(), + path, + notes, + &device, + &storages, + )?; + (key_name, Storage::SubDirectory(storage)) + } + StorageType::Online => todo!(), + }; + + // add to storages + storages.insert(key.clone(), storage); + trace!("updated storages: {:?}", storages); + + // write to file + write_storages(&config_dir, storages)?; + + // commit + add_and_commit( + &repo, + &Path::new(STORAGESFILE), + &format!("Add new storage(physical drive): {}", key), + )?; + + println!("Added new storage."); + trace!("Finished adding storage"); + } + StorageCommands::List {} => { + // Get storages + let storages: HashMap = get_storages(&config_dir)?; + trace!("found storages: {:?}", storages); + let device = get_device(&config_dir)?; + for (k, storage) in &storages { + println!("{}: {}", k, storage); + println!(" {}", storage.mount_path(&device, &storages)?.display()); + } + } + StorageCommands::Bind { + storage: storage_name, + } => { + // get storages + let mut storages: HashMap = get_storages(&config_dir)?; + let commit_comment = { + // find matching storage + let storage = storages + .get_mut(&storage_name) + .context(format!("No storage has name {}", storage_name))?; + // get disk from sysinfo + let mut sysinfo = sysinfo::System::new_all(); + sysinfo.refresh_disks(); + let disk = select_sysinfo_disk(&sysinfo)?; + let system_name = disk + .name() + .to_str() + .context("Failed to convert disk name to valid string")?; + // add to storages + storage.bind_device(disk, &config_dir)?; + trace!("storage: {}", storage); + format!("{} to {}", system_name, storage.name()) + }; + trace!("bound new system name to the storage"); + trace!("storages: {:#?}", storages); + + write_storages(&config_dir, storages)?; + // commit + add_and_commit( + &repo, + &Path::new(STORAGESFILE), + &format!( + "Bound new storage name to physical drive ({})", + commit_comment + ), + )?; + println!( + "Bound new storage name to physical drive ({})", + commit_comment + ); + } + } + } Commands::Path {} => { println!("{}", &config_dir.display()); } @@ -163,3 +418,9 @@ fn full_status(repo: &Repository) -> Result<()> { } Ok(()) } + +#[test] +fn verify_cli() { + use clap::CommandFactory; + Cli::command().debug_assert() +}