add manual type for physical drive add

This commit is contained in:
qwjyh 2023-12-07 04:23:24 +09:00
parent bf1a5e23fe
commit 017e34d392
4 changed files with 225 additions and 23 deletions

32
Cargo.lock generated
View file

@ -33,30 +33,30 @@ checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]] [[package]]
name = "anstyle-parse" name = "anstyle-parse"
version = "0.2.2" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
dependencies = [ dependencies = [
"utf8parse", "utf8parse",
] ]
[[package]] [[package]]
name = "anstyle-query" name = "anstyle-query"
version = "1.0.0" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" checksum = "a3a318f1f38d2418400f8209655bfd825785afd25aa30bb7ba6cc792e4596748"
dependencies = [ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
name = "anstyle-wincon" name = "anstyle-wincon"
version = "3.0.1" version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"windows-sys 0.48.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -111,9 +111,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.4.10" version = "4.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -131,9 +131,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.4.9" version = "4.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -514,9 +514,9 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.8.9" version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
dependencies = [ dependencies = [
"libc", "libc",
"log", "log",
@ -556,9 +556,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.96" version = "0.9.97"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",

View file

@ -0,0 +1,116 @@
use std::io::ErrorKind;
use inquire::{autocompletion::{Autocomplete, Replacement}, CustomUserError};
#[derive(Clone, Default)]
pub struct FilePathCompleter {
input: String,
paths: Vec<String>,
lcp: String,
}
impl FilePathCompleter {
fn update_input(&mut self, input: &str) -> Result<(), CustomUserError> {
if input == self.input {
return Ok(());
}
self.input = input.to_owned();
self.paths.clear();
let input_path = std::path::PathBuf::from(input);
let fallback_parent = input_path
.parent()
.map(|p| {
if p.to_string_lossy() == "" {
std::path::PathBuf::from(".")
} else {
p.to_owned()
}
})
.unwrap_or_else(|| std::path::PathBuf::from("."));
let scan_dir = if input.ends_with('/') {
input_path
} else {
fallback_parent.clone()
};
let entries = match std::fs::read_dir(scan_dir) {
Ok(read_dir) => Ok(read_dir),
Err(err) if err.kind() == ErrorKind::NotFound => std::fs::read_dir(fallback_parent),
Err(err) => Err(err),
}?
.collect::<Result<Vec<_>, _>>()?;
let mut idx = 0;
let limit = 15;
while idx < entries.len() && self.paths.len() < limit {
let entry = entries.get(idx).unwrap();
let path = entry.path();
let path_str = if path.is_dir() {
format!("{}/", path.to_string_lossy())
} else {
path.to_string_lossy().to_string()
};
if path_str.starts_with(&self.input) && path_str.len() != self.input.len() {
self.paths.push(path_str);
}
idx = idx.saturating_add(1);
}
self.lcp = self.longest_common_prefix();
Ok(())
}
fn longest_common_prefix(&self) -> String {
let mut ret: String = String::new();
let mut sorted = self.paths.clone();
sorted.sort();
if sorted.is_empty() {
return ret;
}
let mut first_word = sorted.first().unwrap().chars();
let mut last_word = sorted.last().unwrap().chars();
loop {
match (first_word.next(), last_word.next()) {
(Some(c1), Some(c2)) if c1 == c2 => {
ret.push(c1);
}
_ => return ret,
}
}
}
}
impl Autocomplete for FilePathCompleter {
fn get_suggestions(&mut self, input: &str) -> Result<Vec<String>, CustomUserError> {
self.update_input(input)?;
Ok(self.paths.clone())
}
fn get_completion(
&mut self,
input: &str,
highlighted_suggestion: Option<String>,
) -> Result<Replacement, CustomUserError> {
self.update_input(input)?;
Ok(match highlighted_suggestion {
Some(suggestion) => Replacement::Some(suggestion),
None => match self.lcp.is_empty() {
true => Replacement::None,
false => Replacement::Some(self.lcp.clone()),
},
})
}
}

View file

@ -16,10 +16,11 @@ use clap::error::ErrorKind;
use clap::{CommandFactory, Parser, Subcommand}; use clap::{CommandFactory, Parser, Subcommand};
use clap_verbosity_flag::Verbosity; use clap_verbosity_flag::Verbosity;
use git2::{Commit, Oid, Repository}; use git2::{Commit, Oid, Repository};
use inquire::{min_length, Confirm, CustomType, Select};
use inquire::{validator::Validation, Text}; use inquire::{validator::Validation, Text};
use serde_yaml; use serde_yaml;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::PathBuf; use std::path::{self, PathBuf};
use std::{env, io::BufReader, path::Path}; use std::{env, io::BufReader, path::Path};
use std::{ use std::{
ffi::OsString, ffi::OsString,
@ -29,6 +30,7 @@ use std::{fmt::Debug, fs::File};
use std::{fs, io::prelude::*}; use std::{fs, io::prelude::*};
use sysinfo::{Disk, DiskExt, SystemExt}; use sysinfo::{Disk, DiskExt, SystemExt};
use crate::inquire_filepath_completer::FilePathCompleter;
use crate::storages::{ use crate::storages::{
directory::Directory, get_storages, local_info, online_storage, physical_drive_partition::*, directory::Directory, get_storages, local_info, online_storage, physical_drive_partition::*,
write_storages, Storage, StorageExt, StorageType, STORAGESFILE, write_storages, Storage, StorageExt, StorageType, STORAGESFILE,
@ -62,6 +64,9 @@ enum Commands {
/// Sync with git repo. /// Sync with git repo.
Sync {}, Sync {},
/// Check config files.
Check {},
} }
#[derive(clap::Args)] #[derive(clap::Args)]
@ -90,11 +95,11 @@ enum StorageCommands {
} }
mod devices; mod devices;
mod inquire_filepath_completer;
mod storages; mod storages;
struct BackupLog {} struct BackupLog {}
#[feature(absolute_path)]
fn main() -> Result<()> { fn main() -> Result<()> {
let cli = Cli::parse(); let cli = Cli::parse();
env_logger::Builder::new() env_logger::Builder::new()
@ -200,8 +205,66 @@ fn main() -> Result<()> {
let device = get_device(&config_dir)?; let device = get_device(&config_dir)?;
let (key, storage) = match storage_type { let (key, storage) = match storage_type {
StorageType::Physical => { StorageType::Physical => {
// select storage let use_sysinfo = {
let (key, storage) = select_physical_storage(device, &storages)?; let options = vec![
"Fetch disk information automatically.",
"Type disk information manually.",
];
let ans = Select::new("Do you fetch disk information automatically? (it may take a few minutes)", options)
.prompt().context("Failed to get response. Please try again.")?;
match ans {
"Fetch disk information automatically." => true,
_ => false,
}
};
let (key, storage) = if use_sysinfo {
// select storage
select_physical_storage(device, &storages)?
} else {
let mut name = String::new();
loop {
name = Text::new("Name for the storage:")
.with_validator(min_length!(0, "At least 1 character"))
.prompt()
.context("Failed to get Name")?;
if storages.iter().all(|(k, _v)| k != &name) {
break;
}
println!("The name {} is already used.", name);
}
let kind = Text::new("Kind of storage (ex. SSD):")
.prompt()
.context("Failed to get kind.")?;
let capacity: u64 = CustomType::<u64>::new("Capacity (byte):")
.with_error_message("Please type number.")
.prompt()
.context("Failed to get capacity.")?;
let fs = Text::new("filesystem:")
.prompt()
.context("Failed to get fs.")?;
let is_removable = Confirm::new("Is removable")
.prompt()
.context("Failed to get is_removable")?;
let mount_path: path::PathBuf = PathBuf::from(
Text::new("mount path:")
.with_autocomplete(FilePathCompleter::default())
.prompt()?,
);
let local_info =
local_info::LocalInfo::new("".to_string(), mount_path);
(
name.clone(),
PhysicalDrivePartition::new(
name,
kind,
capacity,
fs,
is_removable,
local_info,
&device,
),
)
};
println!("storage: {}: {:?}", key, storage); println!("storage: {}: {:?}", key, storage);
(key, Storage::PhysicalStorage(storage)) (key, Storage::PhysicalStorage(storage))
} }
@ -312,6 +375,7 @@ fn main() -> Result<()> {
Commands::Sync {} => { Commands::Sync {} => {
unimplemented!("Sync is not implemented") unimplemented!("Sync is not implemented")
} }
Commands::Check {} => todo!(),
} }
full_status(&Repository::open(&config_dir)?)?; full_status(&Repository::open(&config_dir)?)?;
Ok(()) Ok(())

View file

@ -14,7 +14,7 @@ use std::{
}; };
use sysinfo::{Disk, DiskExt, SystemExt}; use sysinfo::{Disk, DiskExt, SystemExt};
use super::local_info::LocalInfo; use super::local_info::{self, LocalInfo};
/// Partitoin of physical (on-premises) drive. /// Partitoin of physical (on-premises) drive.
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -29,6 +29,25 @@ pub struct PhysicalDrivePartition {
} }
impl PhysicalDrivePartition { impl PhysicalDrivePartition {
pub fn new(
name: String,
kind: String,
capacity: u64,
fs: String,
is_removable: bool,
local_info: LocalInfo,
device: &Device
) -> PhysicalDrivePartition {
PhysicalDrivePartition {
name,
kind,
capacity,
fs,
is_removable,
local_info: HashMap::from([(device.name(), local_info)]),
}
}
/// Try to get Physical drive info from sysinfo. /// Try to get Physical drive info from sysinfo.
pub fn try_from_sysinfo_disk( pub fn try_from_sysinfo_disk(
disk: &sysinfo::Disk, disk: &sysinfo::Disk,
@ -122,8 +141,11 @@ pub fn select_physical_storage(
) -> Result<(String, PhysicalDrivePartition)> { ) -> Result<(String, PhysicalDrivePartition)> {
trace!("select_physical_storage"); trace!("select_physical_storage");
// get disk info fron sysinfo // get disk info fron sysinfo
let mut sys_disks = sysinfo::System::new_all(); let mut sys_disks =
sys_disks.refresh_disks(); sysinfo::System::new_with_specifics(sysinfo::RefreshKind::new().with_disks_list());
trace!("refresh");
// sys_disks.refresh_disks_list();
// sys_disks.refresh_disks();
trace!("Available disks"); trace!("Available disks");
for disk in sys_disks.disks() { for disk in sys_disks.disks() {
trace!("{:?}", disk) trace!("{:?}", disk)