mirror of
https://github.com/qwjyh/xdbm
synced 2024-11-22 14:50:12 +09:00
Merge branch 'main' of github.com:qwjyh/xdbm
This commit is contained in:
commit
01dd0ad77a
9 changed files with 423 additions and 73 deletions
32
Cargo.lock
generated
32
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
4
README.md
Normal file
4
README.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# TODO:
|
||||||
|
- [ ] split subcommands to functions
|
||||||
|
- [ ] reorganize cmd option for storage
|
||||||
|
- [ ] use subcommand
|
116
src/inquire_filepath_completer.rs
Normal file
116
src/inquire_filepath_completer.rs
Normal 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()),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
156
src/main.rs
156
src/main.rs
|
@ -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,8 @@ 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::online_storage::OnlineStorage;
|
||||||
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 +65,9 @@ enum Commands {
|
||||||
|
|
||||||
/// Sync with git repo.
|
/// Sync with git repo.
|
||||||
Sync {},
|
Sync {},
|
||||||
|
|
||||||
|
/// Check config files.
|
||||||
|
Check {},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(clap::Args)]
|
#[derive(clap::Args)]
|
||||||
|
@ -84,17 +90,26 @@ enum StorageCommands {
|
||||||
},
|
},
|
||||||
/// List all storages.
|
/// List all storages.
|
||||||
List {},
|
List {},
|
||||||
/// Add new device-specific name to existing storage.
|
/// Make `storage` available for the current device.
|
||||||
/// For physical disk, the name is taken from system info automatically.
|
/// For physical disk, the name is taken from system info automatically.
|
||||||
Bind { storage: String },
|
Bind {
|
||||||
|
/// Name of the storage.
|
||||||
|
storage: String,
|
||||||
|
/// Device specific alias for the storage.
|
||||||
|
#[arg(short, long)]
|
||||||
|
alias: String,
|
||||||
|
/// Mount point on this device.
|
||||||
|
#[arg(short, long)]
|
||||||
|
path: path::PathBuf,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +215,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 => {
|
||||||
|
let use_sysinfo = {
|
||||||
|
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 storage
|
||||||
let (key, storage) = select_physical_storage(device, &storages)?;
|
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))
|
||||||
}
|
}
|
||||||
|
@ -233,7 +306,46 @@ fn main() -> Result<()> {
|
||||||
)?;
|
)?;
|
||||||
(key_name, Storage::SubDirectory(storage))
|
(key_name, Storage::SubDirectory(storage))
|
||||||
}
|
}
|
||||||
StorageType::Online => todo!(),
|
StorageType::Online => {
|
||||||
|
let path = path.unwrap_or_else(|| {
|
||||||
|
let mut cmd = Cli::command();
|
||||||
|
cmd.error(
|
||||||
|
ErrorKind::MissingRequiredArgument,
|
||||||
|
"<PATH> is required with sub-directory",
|
||||||
|
)
|
||||||
|
.exit();
|
||||||
|
});
|
||||||
|
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 provider = Text::new("Provider:")
|
||||||
|
.prompt()
|
||||||
|
.context("Failed to get provider")?;
|
||||||
|
let capacity: u64 = CustomType::<u64>::new("Capacity (byte):")
|
||||||
|
.with_error_message("Please type number.")
|
||||||
|
.prompt()
|
||||||
|
.context("Failed to get capacity.")?;
|
||||||
|
let alias = Text::new("Alias:")
|
||||||
|
.prompt()
|
||||||
|
.context("Failed to get provider")?;
|
||||||
|
let storage = OnlineStorage::new(
|
||||||
|
name.clone(),
|
||||||
|
provider,
|
||||||
|
capacity,
|
||||||
|
alias,
|
||||||
|
path,
|
||||||
|
&device,
|
||||||
|
);
|
||||||
|
(name, Storage::Online(storage))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// add to storages
|
// add to storages
|
||||||
|
@ -261,30 +373,31 @@ fn main() -> Result<()> {
|
||||||
for (k, storage) in &storages {
|
for (k, storage) in &storages {
|
||||||
println!("{}: {}", k, storage);
|
println!("{}: {}", k, storage);
|
||||||
println!(" {}", storage.mount_path(&device, &storages)?.display());
|
println!(" {}", storage.mount_path(&device, &storages)?.display());
|
||||||
|
// println!("{}: {}", storage.shorttypename(), storage.name()); // TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StorageCommands::Bind {
|
StorageCommands::Bind {
|
||||||
storage: storage_name,
|
storage: storage_name,
|
||||||
|
alias: new_alias,
|
||||||
|
path: mount_point,
|
||||||
} => {
|
} => {
|
||||||
|
let device = get_device(&config_dir)?;
|
||||||
// get storages
|
// get storages
|
||||||
let mut storages: HashMap<String, Storage> = get_storages(&config_dir)?;
|
let mut storages: HashMap<String, Storage> = get_storages(&config_dir)?;
|
||||||
let commit_comment = {
|
let commit_comment = {
|
||||||
// find matching storage
|
// find matching storage
|
||||||
let storage = storages
|
let storage = &mut storages
|
||||||
.get_mut(&storage_name)
|
.get_mut(&storage_name)
|
||||||
.context(format!("No storage has name {}", storage_name))?;
|
.context(format!("No storage has name {}", storage_name))?;
|
||||||
// get disk from sysinfo
|
let old_alias = storage
|
||||||
let mut sysinfo = sysinfo::System::new_all();
|
.local_info(&device)
|
||||||
sysinfo.refresh_disks();
|
.context(format!("Failed to get LocalInfo for {}", storage.name()))?
|
||||||
let disk = select_sysinfo_disk(&sysinfo)?;
|
.alias()
|
||||||
let system_name = disk
|
.clone();
|
||||||
.name()
|
// TODO: get mount path for directory automatically?
|
||||||
.to_str()
|
storage.bound_on_device(new_alias, mount_point, &device)?;
|
||||||
.context("Failed to convert disk name to valid string")?;
|
// trace!("storage: {}", &storage);
|
||||||
// add to storages
|
format!("{} to {}", old_alias, storage.name())
|
||||||
storage.bind_device(disk, &config_dir)?;
|
|
||||||
trace!("storage: {}", storage);
|
|
||||||
format!("{} to {}", system_name, storage.name())
|
|
||||||
};
|
};
|
||||||
trace!("bound new system name to the storage");
|
trace!("bound new system name to the storage");
|
||||||
trace!("storages: {:#?}", storages);
|
trace!("storages: {:#?}", storages);
|
||||||
|
@ -312,6 +425,11 @@ fn main() -> Result<()> {
|
||||||
Commands::Sync {} => {
|
Commands::Sync {} => {
|
||||||
unimplemented!("Sync is not implemented")
|
unimplemented!("Sync is not implemented")
|
||||||
}
|
}
|
||||||
|
Commands::Check {} => {
|
||||||
|
println!("Config dir: {}", &config_dir.display());
|
||||||
|
let _storages = storages::get_storages(&config_dir).context("Failed to parse storages file.");
|
||||||
|
todo!()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
full_status(&Repository::open(&config_dir)?)?;
|
full_status(&Repository::open(&config_dir)?)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -28,16 +28,21 @@ pub enum Storage {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Storage {
|
impl Storage {
|
||||||
/// Add or update alias of `disk` for current device.
|
/// Full type name like "PhysicalStorage".
|
||||||
pub fn bind_device(
|
pub fn typename(&self) -> &str {
|
||||||
&mut self,
|
|
||||||
disk: &sysinfo::Disk,
|
|
||||||
config_dir: &std::path::PathBuf,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
match self {
|
match self {
|
||||||
Self::PhysicalStorage(s) => s.bind_device(disk, config_dir),
|
Self::PhysicalStorage(_) => "PhysicalStorage",
|
||||||
Self::SubDirectory(_) => Err(anyhow!("SubDirectory doesn't have system alias.")),
|
Self::SubDirectory(_) => "SubDirectory",
|
||||||
Self::Online(_) => todo!(),
|
Self::Online(_) => "OnlineStorage",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Short type name with one letter like "P".
|
||||||
|
pub fn shorttypename(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::PhysicalStorage(_) => "P",
|
||||||
|
Self::SubDirectory(_) => "S",
|
||||||
|
Self::Online(_) => "O",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,11 +56,11 @@ impl StorageExt for Storage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_alias(&self, device: &devices::Device) -> bool {
|
fn local_info(&self, device: &devices::Device) -> Option<&local_info::LocalInfo> {
|
||||||
match self {
|
match self {
|
||||||
Self::PhysicalStorage(s) => s.has_alias(&device),
|
Self::PhysicalStorage(s) => s.local_info(device),
|
||||||
Self::SubDirectory(s) => s.has_alias(&device),
|
Self::SubDirectory(s) => s.local_info(device),
|
||||||
Self::Online(s) => s.has_alias(&device),
|
Self::Online(s) => s.local_info(device),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +75,19 @@ impl StorageExt for Storage {
|
||||||
Self::Online(s) => s.mount_path(&device, &storages),
|
Self::Online(s) => s.mount_path(&device, &storages),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bound_on_device(
|
||||||
|
&mut self,
|
||||||
|
alias: String,
|
||||||
|
mount_point: path::PathBuf,
|
||||||
|
device: &devices::Device,
|
||||||
|
) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
Storage::PhysicalStorage(s) => s.bound_on_device(alias, mount_point, device),
|
||||||
|
Storage::SubDirectory(s) => s.bound_on_device(alias, mount_point, device),
|
||||||
|
Storage::Online(s) => s.bound_on_device(alias, mount_point, device),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Storage {
|
impl fmt::Display for Storage {
|
||||||
|
@ -85,13 +103,30 @@ impl fmt::Display for Storage {
|
||||||
/// Trait to manipulate all `Storage`s (Enums).
|
/// Trait to manipulate all `Storage`s (Enums).
|
||||||
pub trait StorageExt {
|
pub trait StorageExt {
|
||||||
fn name(&self) -> &String;
|
fn name(&self) -> &String;
|
||||||
fn has_alias(&self, device: &devices::Device) -> bool;
|
|
||||||
|
/// Return `true` if `self` has local info on the `device`.
|
||||||
|
/// Used to check if the storage is bound on the `device`.
|
||||||
|
fn has_alias(&self, device: &devices::Device) -> bool {
|
||||||
|
self.local_info(device).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get local info of `device`.
|
||||||
|
fn local_info(&self, device: &devices::Device) -> Option<&local_info::LocalInfo>;
|
||||||
|
|
||||||
/// Get mount path of `self` on `device`.
|
/// Get mount path of `self` on `device`.
|
||||||
fn mount_path(
|
fn mount_path(
|
||||||
&self,
|
&self,
|
||||||
device: &devices::Device,
|
device: &devices::Device,
|
||||||
storages: &HashMap<String, Storage>,
|
storages: &HashMap<String, Storage>,
|
||||||
) -> Result<path::PathBuf>;
|
) -> Result<path::PathBuf>;
|
||||||
|
|
||||||
|
/// Add local info of `device` to `self`.
|
||||||
|
fn bound_on_device(
|
||||||
|
&mut self,
|
||||||
|
alias: String,
|
||||||
|
mount_point: path::PathBuf,
|
||||||
|
device: &devices::Device,
|
||||||
|
) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod directory;
|
pub mod directory;
|
||||||
|
|
|
@ -104,16 +104,6 @@ impl Directory {
|
||||||
let parent_mount_path = parent.mount_path(&device, &storages)?;
|
let parent_mount_path = parent.mount_path(&device, &storages)?;
|
||||||
Ok(parent_mount_path.join(self.relative_path.clone()))
|
Ok(parent_mount_path.join(self.relative_path.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bind_device(&mut self, alias: String, device: &devices::Device, storages: &HashMap<String, Storage>) -> Result<()> {
|
|
||||||
let mount_path = self.mount_path(&device, &storages)?;
|
|
||||||
let new_local_info = LocalInfo::new(alias, mount_path);
|
|
||||||
match self.local_info.insert(device.name(), new_local_info) {
|
|
||||||
Some(v) => println!("Value updated. old val is: {:?}", v),
|
|
||||||
None => println!("inserted new val"),
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StorageExt for Directory {
|
impl StorageExt for Directory {
|
||||||
|
@ -121,8 +111,8 @@ impl StorageExt for Directory {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_alias(&self, device: &devices::Device) -> bool {
|
fn local_info(&self, device: &devices::Device) -> Option<&LocalInfo> {
|
||||||
self.local_info.get(&device.name()).is_some()
|
self.local_info.get(&device.name())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mount_path(
|
fn mount_path(
|
||||||
|
@ -132,6 +122,21 @@ impl StorageExt for Directory {
|
||||||
) -> Result<path::PathBuf> {
|
) -> Result<path::PathBuf> {
|
||||||
Ok(self.mount_path(device, storages)?)
|
Ok(self.mount_path(device, storages)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This method doesn't use `mount_path`.
|
||||||
|
fn bound_on_device(
|
||||||
|
&mut self,
|
||||||
|
alias: String,
|
||||||
|
mount_point: path::PathBuf,
|
||||||
|
device: &devices::Device,
|
||||||
|
) -> Result<()> {
|
||||||
|
let new_local_info = LocalInfo::new(alias, mount_point);
|
||||||
|
match self.local_info.insert(device.name(), new_local_info) {
|
||||||
|
Some(v) => println!("Value updated. old val is: {:?}", v),
|
||||||
|
None => println!("inserted new val"),
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Directory {
|
impl fmt::Display for Directory {
|
||||||
|
|
|
@ -18,6 +18,10 @@ impl LocalInfo {
|
||||||
LocalInfo { alias, mount_path }
|
LocalInfo { alias, mount_path }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn alias(&self) -> String {
|
||||||
|
self.alias.clone() // ?
|
||||||
|
}
|
||||||
|
|
||||||
pub fn mount_path(&self) -> path::PathBuf {
|
pub fn mount_path(&self) -> path::PathBuf {
|
||||||
self.mount_path.clone()
|
self.mount_path.clone()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Online storage which is not a children of any physical drive.
|
//! Online storage which is not a children of any physical drive.
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::{Context, Result};
|
||||||
use byte_unit::Byte;
|
use byte_unit::Byte;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -9,6 +9,7 @@ use std::path;
|
||||||
|
|
||||||
use crate::devices;
|
use crate::devices;
|
||||||
|
|
||||||
|
use super::local_info;
|
||||||
use super::local_info::LocalInfo;
|
use super::local_info::LocalInfo;
|
||||||
use super::StorageExt;
|
use super::StorageExt;
|
||||||
|
|
||||||
|
@ -16,13 +17,26 @@ use super::StorageExt;
|
||||||
pub struct OnlineStorage {
|
pub struct OnlineStorage {
|
||||||
name: String,
|
name: String,
|
||||||
provider: String,
|
provider: String,
|
||||||
capacity: u8,
|
capacity: u64,
|
||||||
local_info: HashMap<String, LocalInfo>,
|
local_info: HashMap<String, LocalInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OnlineStorage {
|
impl OnlineStorage {
|
||||||
fn new(name: String, provider: String, capacity: u8, path: path::PathBuf, device: &devices::Device) -> OnlineStorage {
|
pub fn new(
|
||||||
todo!()
|
name: String,
|
||||||
|
provider: String,
|
||||||
|
capacity: u64,
|
||||||
|
alias: String,
|
||||||
|
path: path::PathBuf,
|
||||||
|
device: &devices::Device,
|
||||||
|
) -> OnlineStorage {
|
||||||
|
let local_info = local_info::LocalInfo::new(alias, path);
|
||||||
|
OnlineStorage {
|
||||||
|
name,
|
||||||
|
provider,
|
||||||
|
capacity,
|
||||||
|
local_info: HashMap::from([(device.name(), local_info)]),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,8 +45,8 @@ impl StorageExt for OnlineStorage {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_alias(&self, device: &devices::Device) -> bool {
|
fn local_info(&self, device: &devices::Device) -> Option<&LocalInfo> {
|
||||||
self.local_info.get(&device.name()).is_some()
|
self.local_info.get(&device.name())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mount_path(
|
fn mount_path(
|
||||||
|
@ -46,6 +60,22 @@ impl StorageExt for OnlineStorage {
|
||||||
.context(format!("LocalInfo for storage: {} not found", &self.name()))?
|
.context(format!("LocalInfo for storage: {} not found", &self.name()))?
|
||||||
.mount_path())
|
.mount_path())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bound_on_device(
|
||||||
|
&mut self,
|
||||||
|
alias: String,
|
||||||
|
mount_point: path::PathBuf,
|
||||||
|
device: &devices::Device,
|
||||||
|
) -> Result<()> {
|
||||||
|
match self
|
||||||
|
.local_info
|
||||||
|
.insert(device.name(), LocalInfo::new(alias, mount_point))
|
||||||
|
{
|
||||||
|
Some(old) => info!("Value replaced. Old value: {:?}", old),
|
||||||
|
None => info!("New value inserted."),
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for OnlineStorage {
|
impl fmt::Display for OnlineStorage {
|
||||||
|
|
|
@ -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,
|
||||||
|
@ -82,8 +101,8 @@ impl StorageExt for PhysicalDrivePartition {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_alias(&self, device: &devices::Device) -> bool {
|
fn local_info(&self, device: &devices::Device) -> Option<&local_info::LocalInfo> {
|
||||||
self.local_info.get(&device.name()).is_some()
|
self.local_info.get(&device.name())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mount_path(
|
fn mount_path(
|
||||||
|
@ -97,6 +116,22 @@ impl StorageExt for PhysicalDrivePartition {
|
||||||
.context(format!("LocalInfo for storage: {} not found", &self.name()))?
|
.context(format!("LocalInfo for storage: {} not found", &self.name()))?
|
||||||
.mount_path())
|
.mount_path())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bound_on_device(
|
||||||
|
&mut self,
|
||||||
|
alias: String,
|
||||||
|
mount_point: path::PathBuf,
|
||||||
|
device: &devices::Device,
|
||||||
|
) -> Result<()> {
|
||||||
|
match self
|
||||||
|
.local_info
|
||||||
|
.insert(device.name(), LocalInfo::new(alias, mount_point))
|
||||||
|
{
|
||||||
|
Some(old) => info!("Value replaced. Old value: {:?}", old),
|
||||||
|
None => info!("New value inserted."),
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for PhysicalDrivePartition {
|
impl fmt::Display for PhysicalDrivePartition {
|
||||||
|
@ -122,8 +157,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 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)
|
||||||
|
|
Loading…
Reference in a new issue