diff --git a/Cargo.lock b/Cargo.lock index 6c33344..7561d67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.4" @@ -124,6 +139,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bumpalo" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + [[package]] name = "byte-unit" version = "4.0.19" @@ -150,6 +171,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.0", +] + [[package]] name = "clap" version = "4.4.11" @@ -442,6 +478,29 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "idna" version = "0.5.0" @@ -520,6 +579,15 @@ dependencies = [ "libc", ] +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1145,6 +1213,60 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + [[package]] name = "winapi" version = "0.3.9" @@ -1176,6 +1298,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -1316,6 +1447,7 @@ dependencies = [ "assert_cmd", "assert_fs", "byte-unit", + "chrono", "clap", "clap-verbosity-flag", "dirs", diff --git a/Cargo.toml b/Cargo.toml index 79cb708..d33d1cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ clap = { version = "4.4.0", features = ["derive"] } sysinfo = { version = "0.29.8", features = ["serde"] } log = "0.4" clap-verbosity-flag = "2.0.1" +chrono = { version = "0.4.35", features = ["serde"] } env_logger = "0.10.0" inquire = "0.6.2" git2 = "0.17.2" diff --git a/src/backups.rs b/src/backups.rs new file mode 100644 index 0000000..923ea9d --- /dev/null +++ b/src/backups.rs @@ -0,0 +1,55 @@ +use std::path::PathBuf; + +use chrono::{DateTime, Local}; +use serde::{Deserialize, Serialize}; + +use crate::storages::Storage; + +/// Targets for backup source or destination. +#[derive(Debug, Serialize, Deserialize)] +pub struct BackupTarget { + storage: Storage, + path: PathBuf, +} + +/// Type of backup commands. +#[derive(Debug, Serialize, Deserialize)] +pub enum BackupCommand { + ExternallyInvoked(ExternallyInvoked), +} + +/// Backup commands which is not invoked from xdbm itself. +/// Call xdbm externally to record backup datetime and status. +#[derive(Debug, Serialize, Deserialize)] +pub struct ExternallyInvoked { + name: String, + pub note: String, +} + +/// Backup execution log. +#[derive(Debug, Serialize, Deserialize)] +pub struct BackupLog { + datetime: DateTime, + status: BackupResult, + log: String, +} + +/// Result of backup. +#[derive(Debug, Serialize, Deserialize)] +pub enum BackupResult { + Success, + Failure, +} + +/// Backup source, destination, command and logs. +#[derive(Debug, Serialize, Deserialize)] +pub struct Backup { + /// must be unique + name: String, + /// name of [`crate::Device`] + device: String, + from: BackupTarget, + to: BackupTarget, + command: BackupCommand, + logs: Vec, +} diff --git a/src/cmd_args.rs b/src/cmd_args.rs index 80c031d..e7ce004 100644 --- a/src/cmd_args.rs +++ b/src/cmd_args.rs @@ -1,6 +1,5 @@ //! CLI arguments -use crate::StorageType; use crate::path; use crate::PathBuf; use clap::Args; @@ -41,6 +40,10 @@ pub(crate) enum Commands { /// Manage storages. Storage(StorageArgs), + /// Manage backups. + #[command(subcommand)] + Backup(BackupSubCommands), + /// Print config dir. Path {}, @@ -129,5 +132,40 @@ pub(crate) enum StorageAddCommands { /// Device specific alias for the storage. #[arg(short, long)] alias: String, - } + }, +} + +#[derive(Subcommand, Debug)] +pub(crate) enum BackupSubCommands { + /// Add new backup config. + Add { + name: String, + /// Source of the data backuped. + #[arg(short, long)] + src: PathBuf, + /// Destination of the backuped data. + #[arg(short, long)] + dest: PathBuf, + #[command(subcommand)] + cmd: BackupAddCommands, + }, + /// Print configured backups. + List {}, + /// Record xdbm that the backup with the name has finished right now. + Done { + name: String, + succeeded: bool, + #[arg(short, long)] + log: Option, + }, +} + +#[derive(Subcommand, Debug)] +pub(crate) enum BackupAddCommands { + /// Invoke logging via cli of xdbm. The simplest one. + External { + name: String, + #[arg(default_value = "")] + note: String, + }, } diff --git a/src/cmd_init.rs b/src/cmd_init.rs index debec88..ab7f68a 100644 --- a/src/cmd_init.rs +++ b/src/cmd_init.rs @@ -82,7 +82,7 @@ pub(crate) fn cmd_init( } // validate device name if device_name.chars().count() == 0 { - log::error!("Device name cannnot by empty"); + log::error!("Device name cannot by empty"); return Err(anyhow!("Device name is empty")); } // get repo or initialize it diff --git a/src/devices.rs b/src/devices.rs index 3a46315..d93b73c 100644 --- a/src/devices.rs +++ b/src/devices.rs @@ -12,7 +12,7 @@ pub const DEVICESFILE: &str = "devices.yml"; /// Represents each devices. /// Identified by name, which is accessible from `name()`. -/// Store os name, os version and hostname as supplimental information. +/// Store os name, os version and hostname as supplemental information. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Device { name: String, diff --git a/src/main.rs b/src/main.rs index 7309716..3a951ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,21 +15,18 @@ extern crate dirs; use anyhow::{anyhow, Context, Result}; use clap::{CommandFactory, Parser}; use git2::{Commit, Oid, Repository}; -use inquire::{min_length, Confirm, CustomType, Select}; -use inquire::{validator::Validation, Text}; -use serde_yaml; -use std::collections::HashMap; use std::path::Path; use std::path::{self, PathBuf}; use storages::Storages; use crate::cmd_args::{Cli, Commands, StorageCommands}; -use crate::storages::online_storage; use crate::storages::{ - directory, local_info, physical_drive_partition, Storage, StorageExt, StorageType, STORAGESFILE, + directory, local_info, online_storage, physical_drive_partition, Storage, StorageExt, + StorageType, STORAGESFILE, }; use devices::{Device, DEVICESFILE, *}; +mod backups; mod cmd_args; mod cmd_init; mod cmd_storage; @@ -38,8 +35,6 @@ mod devices; mod inquire_filepath_completer; mod storages; -struct BackupLog {} - fn main() -> Result<()> { let cli = Cli::parse(); env_logger::Builder::new() @@ -98,6 +93,7 @@ fn main() -> Result<()> { let _storages = Storages::read(&config_dir)?; todo!() } + Commands::Backup(_) => todo!(), } full_status(&Repository::open(&config_dir)?)?; Ok(()) diff --git a/src/storages.rs b/src/storages.rs index b491916..7019443 100644 --- a/src/storages.rs +++ b/src/storages.rs @@ -8,13 +8,7 @@ use anyhow::{anyhow, Context, Result}; use clap::ValueEnum; use core::panic; use serde::{Deserialize, Serialize}; -use std::ffi::OsString; -use std::{ - collections::HashMap, - ffi, - fmt::{self, format}, - fs, io, path, u64, -}; +use std::{collections::HashMap, fmt, fs, io, path, u64}; /// YAML file to store known storages.. pub const STORAGESFILE: &str = "storages.yml"; @@ -74,11 +68,7 @@ impl StorageExt for Storage { } } - fn mount_path( - &self, - device: &devices::Device, - storages: &Storages, - ) -> Result { + fn mount_path(&self, device: &devices::Device, storages: &Storages) -> Result { match self { Self::PhysicalStorage(s) => s.mount_path(&device, &storages), Self::SubDirectory(s) => s.mount_path(&device, &storages), @@ -145,11 +135,7 @@ pub trait StorageExt { /// Get mount path of `self` on `device`. /// `storages` is a `HashMap` with key of storage name and value of the storage. - fn mount_path( - &self, - device: &devices::Device, - storages: &Storages, - ) -> Result; + fn mount_path(&self, device: &devices::Device, storages: &Storages) -> Result; /// Add local info of `device` to `self`. fn bound_on_device( @@ -226,7 +212,7 @@ impl Storages { let storages_file = config_dir.join(STORAGESFILE); if !storages_file.exists() { warn!("No storages file found."); - return Err(anyhow!("Couln't find {}", STORAGESFILE)) + return Err(anyhow!("Couln't find {}", STORAGESFILE)); } trace!("Reading {:?}", storages_file); let f = fs::File::open(storages_file)?; @@ -237,8 +223,10 @@ impl Storages { } pub fn write(self, config_dir: &path::Path) -> Result<()> { - let f = fs::File::create(config_dir.join(STORAGESFILE)).context("Failed to open storages file")?; + let f = fs::File::create(config_dir.join(STORAGESFILE)) + .context("Failed to open storages file")?; let writer = io::BufWriter::new(f); - serde_yaml::to_writer(writer, &self).context(format!("Failed to writing to {:?}", STORAGESFILE)) + serde_yaml::to_writer(writer, &self) + .context(format!("Failed to writing to {:?}", STORAGESFILE)) } } diff --git a/src/storages/directory.rs b/src/storages/directory.rs index d9fc716..e2789b6 100644 --- a/src/storages/directory.rs +++ b/src/storages/directory.rs @@ -30,7 +30,7 @@ impl Directory { /// - `name`: id /// - `parent`: where the directory locates. /// - `relative_path`: path from root of the parent storage. - /// - `notes`: supplimental notes. + /// - `notes`: supplemental notes. fn new( name: String, parent: String, diff --git a/src/storages/physical_drive_partition.rs b/src/storages/physical_drive_partition.rs index 826efc4..b61d9d5 100644 --- a/src/storages/physical_drive_partition.rs +++ b/src/storages/physical_drive_partition.rs @@ -192,7 +192,7 @@ pub fn select_physical_storage( device: Device, ) -> Result { trace!("select_physical_storage"); - // get disk info fron sysinfo + // get disk info from sysinfo let sys_disks = sysinfo::System::new_with_specifics(sysinfo::RefreshKind::new().with_disks_list()); trace!("refresh");