diff --git a/Cargo.lock b/Cargo.lock index c0ba196..204f087 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -833,6 +833,7 @@ dependencies = [ "ntapi", "once_cell", "rayon", + "serde", "winapi", ] diff --git a/Cargo.toml b/Cargo.toml index 4d16f73..f87316c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] clap = { version = "4.4.0", features = ["derive"] } -sysinfo = "0.29.8" +sysinfo = { version = "0.29.8", features = ["serde"] } log = "0.4" clap-verbosity-flag = "2.0.1" env_logger = "0.10.0" diff --git a/src/init.rs b/src/init.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/main.rs b/src/main.rs index 9477d93..0fb6106 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,11 +12,15 @@ use inquire::{ }; use serde::{Deserialize, Serialize}; use serde_yaml; -use std::fs::{File, OpenOptions}; use std::io::prelude::*; use std::io::{self, BufWriter}; +use std::{ + collections::{hash_map::RandomState, HashMap}, + fmt::Debug, + fs::{File, OpenOptions}, +}; use std::{env, io::BufReader, path::Path}; -use sysinfo::{System, SystemExt}; +use sysinfo::{DiskExt, System, SystemExt}; #[derive(Parser)] #[command(author, version, about, long_about = None)] @@ -35,8 +39,25 @@ enum Commands { repo_url: Option, // url? }, + 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 {}, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -47,12 +68,6 @@ struct Device { hostname: String, } -const DEVICESFILE: &str = "devices.yml"; - -struct Storage {} - -struct BackupLog {} - impl Device { fn new(name: String) -> Device { let sys = System::new(); @@ -74,6 +89,85 @@ impl Device { } } +const DEVICESFILE: &str = "devices.yml"; + +/// All storage types. +#[derive(Serialize, Deserialize, Debug)] +enum Storage { + PhysicalStorage(PhysicalDrivePartition), + // /// Online storage provided by others. + // OnlineStorage { + // name: String, + // provider: String, + // capacity: u8, + // }, +} + +/// Partitoin of physical (on-premises) drive. +#[derive(Serialize, Deserialize, Debug)] +struct PhysicalDrivePartition { + name: String, + kind: String, + capacity: u64, + fs: String, + is_removable: bool, + aliases: HashMap, +} + +impl PhysicalDrivePartition { + /// Try to get Physical drive info from sysinfo. + fn try_from_sysinfo_disk( + disk: sysinfo::Disk, + name: String, + device: Device, + ) -> Result { + let alias = match disk.name().to_str() { + Some(s) => s.to_string(), + None => return Err("Failed to convert storage name to valid str.".to_string()), + }; + let fs = disk.file_system(); + trace!("fs: {:?}", fs); + let fs = match std::str::from_utf8(fs) { + Ok(s) => s, + Err(e) => return Err(e.to_string()), + }; + Ok(PhysicalDrivePartition { + name: name, + kind: format!("{:?}", disk.kind()), + capacity: disk.total_space(), + fs: fs.to_string(), + is_removable: disk.is_removable(), + aliases: HashMap::from([(device.name, alias)]), + }) + } + + fn name(&self) -> &String { + &self.name + } + + fn add_alias(self, disk: sysinfo::Disk, device: Device) -> Result { + let alias = match disk.name().to_str() { + Some(s) => s.to_string(), + None => return Err("Failed to convert storage name to valid str.".to_string()), + }; + let mut aliases = self.aliases; + let _ = match aliases.insert(device.name, alias) { + Some(v) => v, + None => return Err("Failed to insert alias".to_string()), + }; + Ok(PhysicalDrivePartition { + name: self.name, + kind: self.kind, + capacity: self.capacity, + fs: self.fs, + is_removable: self.is_removable, + aliases, + }) + } +} + +struct BackupLog {} + fn main() -> Result<(), String> { let cli = Cli::parse(); env_logger::Builder::new() @@ -120,10 +214,18 @@ fn main() -> Result<(), String> { }; let mut buf = BufWriter::new(f); match buf.write("devname".as_bytes()) { - Ok(_) => trace!("successfully created ignore file"), + Ok(_) => trace!("successfully created ignore file"), Err(e) => return Err(e.to_string()), }; - match add_and_commit(&repo, Path::new(".gitignore"), "Add devname to gitignore.") { + match buf.flush() { + Ok(_) => (), + Err(e) => return Err(e.to_string()), + }; + match add_and_commit( + &repo, + Path::new(".gitignore"), + "Add devname to gitignore.", + ) { Ok(_) => (), Err(e) => return Err(e.to_string()), }; @@ -154,6 +256,9 @@ fn main() -> Result<(), String> { } else { get_devices(&config_dir)? }; + if devices.iter().any(|x| x.name == device.name) { + return Err("device name is already used.".to_string()); + } devices.push(device.clone()); trace!("Devices: {:?}", devices); write_devices(&config_dir, devices)?; @@ -169,9 +274,19 @@ fn main() -> Result<(), String> { Err(e) => return Err(e.to_string()), } } + Commands::Storage(storage) => { + match storage.command { + StorageCommands::Add {} => { + unimplemented!() + } + } + } Commands::Path {} => { println!("{}", &config_dir.display()); } + Commands::Sync {} => { + unimplemented!("Sync is not implemented") + } } Ok(()) } @@ -226,7 +341,11 @@ fn get_devices(config_dir: &Path) -> Result, String> { /// Write `devices` to yaml file in `config_dir`. fn write_devices(config_dir: &Path, devices: Vec) -> Result<(), String> { trace!("write_devices"); - let f = match OpenOptions::new().create(true).write(true).open(config_dir.join(DEVICESFILE)) { + let f = match OpenOptions::new() + .create(true) + .write(true) + .open(config_dir.join(DEVICESFILE)) + { Ok(f) => f, Err(e) => return Err(e.to_string()), };