From 553bbac36bdc483135a7053ca64507e01397e5e1 Mon Sep 17 00:00:00 2001 From: fxqnlr Date: Mon, 9 Sep 2024 23:03:49 +0200 Subject: add package manager recognition --- Cargo.lock | 267 +++++++++++++++++++++++++++++++++++++++++++----- Cargo.toml | 33 +++++- src/backup.rs | 17 ++- src/cli.rs | 25 +++++ src/config.rs | 30 +++++- src/error.rs | 12 ++- src/main.rs | 42 +++++--- src/packages.rs | 85 ++++++++++++++- src/packages/pacman.rs | 13 ++- src/packages/portage.rs | 4 +- src/pathinfo.rs | 41 ++++---- 11 files changed, 479 insertions(+), 90 deletions(-) create mode 100644 src/cli.rs diff --git a/Cargo.lock b/Cargo.lock index 5027c92..95d4a49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,55 @@ dependencies = [ "memchr", ] +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" version = "1.0.86" @@ -37,8 +86,10 @@ name = "arbs" version = "0.1.0" dependencies = [ "anyhow", + "clap", "color-eyre", "config", + "dirs", "gethostname", "serde", "serde_json", @@ -115,6 +166,46 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + [[package]] name = "color-eyre" version = "0.6.3" @@ -142,6 +233,12 @@ dependencies = [ "tracing-error", ] +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + [[package]] name = "config" version = "0.14.0" @@ -159,7 +256,6 @@ dependencies = [ "serde", "serde_json", "toml", - "yaml-rust", ] [[package]] @@ -250,6 +346,27 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dlv-list" version = "0.5.2" @@ -272,7 +389,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -302,7 +419,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc3655aa6818d65bc620d6911f05aa7b6aeb596291e1e9f79e52df85583d1e30" dependencies = [ "rustix", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -334,6 +451,12 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "indenter" version = "0.3.3" @@ -350,6 +473,12 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itoa" version = "1.0.11" @@ -380,10 +509,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] -name = "linked-hash-map" -version = "0.5.6" +name = "libredox" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", +] [[package]] name = "linux-raw-sys" @@ -468,6 +601,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "ordered-multimap" version = "0.6.0" @@ -571,6 +710,17 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.10.6" @@ -653,7 +803,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -735,6 +885,12 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.77" @@ -957,6 +1113,12 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.10.0" @@ -1006,13 +1168,37 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -1021,28 +1207,46 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -1055,24 +1259,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -1087,12 +1315,3 @@ checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] diff --git a/Cargo.toml b/Cargo.toml index 629f6a6..31a210e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,41 @@ [package] name = "arbs" -version = "0.1.0" edition = "2021" +version = "0.1.0" [dependencies] anyhow = "1.0.86" color-eyre = "0.6.3" -config = "0.14.0" +dirs = "5.0.1" gethostname = "0.5.0" -serde = { version = "1.0.209", features = ["derive"] } serde_json = "1.0.128" thiserror = "1.0.63" toml = "0.8.19" tracing = "0.1.40" tracing-appender = "0.2.3" -tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } -uuid = { version = "1.10.0", features = ["v4"] } + +[dependencies.clap] +version = "4.5.17" +features = ["derive"] + +[dependencies.config] +version = "0.14.0" +features = ["ini", "toml", "json5", "ron", "json", "convert-case", "async"] +default-features = false + +[dependencies.serde] +version = "1.0.209" +features = ["derive"] + +[dependencies.tracing-subscriber] +version = "0.3.18" +features = ["env-filter"] + +[dependencies.uuid] +version = "1.10.0" +features = ["v4"] + +[features] +default = ["pacman", "portage"] +pacman = [] +portage = [] diff --git a/src/backup.rs b/src/backup.rs index e463593..f9de139 100644 --- a/src/backup.rs +++ b/src/backup.rs @@ -12,7 +12,7 @@ use uuid::Uuid; use crate::{ config::Config, error::{Error, Result}, - packages::Package, + packages::{Manager, PackageList}, pathinfo::PathInfo, }; @@ -22,13 +22,13 @@ pub type Id = String; pub struct Backup { pub id: String, pub timestamp: u64, - packages: Vec, + pub packages: PackageList, pub files: Vec, - device: String, + pub device: String, } impl Backup { - pub fn create(config: &Config, packages: Vec) -> Result { + pub fn create(config: &Config, manager: Option) -> Result { let mut files: Vec = Vec::new(); for dir in &config.directories { files.push(PathInfo::from_path(config, dir)?); @@ -37,7 +37,7 @@ impl Backup { // TODO: UUID not really needed, maybe a shorter hash id: Uuid::new_v4().to_string(), timestamp: Self::get_timestamp(), - packages, + packages: Manager::get_manager(manager)?.get_installed()?, files, device: config.device.clone(), }) @@ -62,6 +62,7 @@ impl Backup { pub fn get_last(config: &Config) -> Result> { let backup_index_root = format!("{}/index.json", config.root); + info!(?backup_index_root, "backup index location:"); let list: Vec = match Self::get_json_content(&backup_index_root) { Ok(list) => list, Err(err) => { @@ -72,6 +73,8 @@ impl Backup { } }; + info!(?list, "backup index:"); + Ok(Some(Self::from_index( config, &list.last().ok_or(Error::BackupNotFound)?.id, @@ -109,6 +112,10 @@ impl Backup { format!("{loc}/{rel_location}") } + pub fn restore(&self) { + todo!() + } + fn get_json_content Deserialize<'a>>(path: &str) -> Result { let mut file = File::open(path)?; let mut content = String::new(); diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..6ffe03f --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,25 @@ +use std::path::PathBuf; + +use clap::{Parser, Subcommand}; + +use crate::packages::Manager; + +#[derive(Parser)] +pub struct Cli { + #[arg(short, long)] + pub config: Option, + + #[command(subcommand)] + pub subcommand: Subcommands, +} + +#[derive(Subcommand)] +pub enum Subcommands { + GenerateConfig, + Save { + #[arg(short, long)] + package_manager: Option, + }, + Restore, +} + diff --git a/src/config.rs b/src/config.rs index 13dd0e4..46d2204 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,3 +1,5 @@ +use std::{fs::create_dir_all, io::Write, path::PathBuf}; + use config::{File, Map}; use serde::{Deserialize, Serialize}; use tracing::{debug, trace}; @@ -6,7 +8,6 @@ use tracing::{debug, trace}; #[serde(default)] pub struct Config { pub root: String, - pub user: Vec, pub directories: Vec, pub custom_directories: Map, pub device: String, @@ -16,7 +17,6 @@ impl Default for Config { fn default() -> Self { Self { root: "/mnt/backup".to_string(), - user: vec![], directories: vec![], custom_directories: Map::new(), device: gethostname::gethostname() @@ -27,10 +27,16 @@ impl Default for Config { } impl Config { - pub fn load() -> Result { + pub fn load(path: Option) -> core::result::Result { debug!("load config"); + let source = if let Some(source) = path { + source + } else { + Self::get_location() + }; + let config = config::Config::builder() - .add_source(File::with_name("config.toml").required(false)) + .add_source(File::with_name(&source.to_string_lossy()).required(false)) .add_source(config::Environment::with_prefix("FXBAUP").separator("_")) .build()?; @@ -39,4 +45,20 @@ impl Config { cfg } + + pub fn generate() -> crate::error::Result<()> { + let loc = Self::get_location(); + create_dir_all(loc.parent().unwrap())?; + let mut f = std::fs::File::create(loc)?; + f.write_all(toml::to_string(&Self::default())?.as_bytes())?; + + Ok(()) + } + + fn get_location() -> PathBuf { + let mut conf_dir = dirs::config_local_dir().unwrap(); + conf_dir.push("arbs"); + conf_dir.push("config.toml"); + conf_dir + } } diff --git a/src/error.rs b/src/error.rs index 0cf4dca..e24c3b1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -14,9 +14,6 @@ pub enum Error { #[error("invalid directory '{0}'")] InvalidDirectory(String), - #[error("Only exactly one user allowed in config")] - MultiUser, - #[error("Requested backup not found")] BackupNotFound, @@ -24,12 +21,21 @@ pub enum Error { #[error("Unknown Package Manger Output")] UnknownOutput, + #[error("Unsupported os/distro")] + Unsupported, + #[error("json: {source}")] SerdeJson { #[from] source: serde_json::Error, }, + #[error("toml serializer: {source}")] + TomlSerialize { + #[from] + source: toml::ser::Error, + }, + #[error("io: {source}")] Io { #[from] diff --git a/src/main.rs b/src/main.rs index 1284e0c..7393af9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,13 @@ use backup::Backup; +use clap::Parser; +use cli::Subcommands; use config::Config; -use packages::{pacman::Pacman, PackageManager}; -use tracing::{debug, info, level_filters::LevelFilter}; +use error::Error; +use tracing::{debug, level_filters::LevelFilter}; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; mod backup; +mod cli; mod config; mod error; mod packages; @@ -13,7 +16,7 @@ mod pathinfo; fn main() -> color_eyre::Result<()> { color_eyre::install()?; - let file_appender = tracing_appender::rolling::never("./", "arps.log"); + let file_appender = tracing_appender::rolling::never("./", "arbs.log"); let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender); tracing_subscriber::registry() @@ -33,19 +36,24 @@ fn main() -> color_eyre::Result<()> { .init(); debug!("logging initialized"); - let mut cfg = Config::load()?; - cfg.user.push("fx".to_string()); - cfg.directories.push("~/.config/nvim".to_string()); - cfg.root = "./backup".to_string(); - - let pacman = Pacman; - let pkgs = pacman.get_installed()?; - let backup = Backup::create(&cfg, pkgs); - // info!(?backup); - // pacman.install(vec![Package { - // id: "lapce".to_string(), - // version: "0.4.2-1".to_string(), - // explicit: true, - // }])?; + let cli = cli::Cli::parse(); + + let config = Config::load(cli.config)?; + + match cli.subcommand { + Subcommands::GenerateConfig => Config::generate()?, + Subcommands::Save { package_manager } => { + let backup = Backup::create(&config, package_manager)?; + backup.save(&config)?; + } + Subcommands::Restore => { + let Some(last_backup) = Backup::get_last(&config)? else { + return Err(Error::BackupNotFound)?; + }; + + last_backup.packages.install()?; + last_backup.restore(); + } + }; Ok(()) } diff --git a/src/packages.rs b/src/packages.rs index 5ee5664..2eadcfc 100644 --- a/src/packages.rs +++ b/src/packages.rs @@ -1,19 +1,96 @@ +use std::{fs::File, io::Read}; + +use pacman::Pacman; +use portage::Portage; use serde::{Deserialize, Serialize}; -use crate::error::Result; +use crate::error::{Error, Result}; -pub mod pacman; -pub mod portage; +#[cfg(feature = "pacman")] +mod pacman; +#[cfg(feature = "portage")] +mod portage; #[derive(Debug, Serialize, Deserialize)] +pub struct PackageList { + packages: Vec, + manager: Manager, +} + +impl PackageList { + pub fn install(&self) -> Result<()> { + self.manager.to_package_manager().install(self.packages.clone()) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Package { pub id: String, pub version: String, pub explicit: bool, } +#[derive(Debug, Clone, clap::ValueEnum, Serialize, Deserialize)] +pub enum Manager { + #[cfg(feature = "pacman")] + Pacman, + #[cfg(feature = "portage")] + Portage, +} + + +impl Manager { + pub fn get_manager(manager: Option) -> Result> { + #[cfg(not(target_os = "linux"))] + { + return Err(Error::Unsupported); + } + + #[cfg(target_os = "linux")] + { + if let Some(man) = manager { + return Ok(man.to_package_manager()); + } + let mut os_release = File::open("/etc/os-release")?; + let mut content = String::new(); + os_release.read_to_string(&mut content)?; + + let lines: Vec<&str> = content.split('\n').collect(); + for line in lines { + let Some((key, value)) = line.split_once('=') else { + continue; + }; + if key == "ID" { + return Self::from_str(value); + } + } + Err(Error::Unsupported) + } + } + + fn from_str(value: &str) -> Result> { + Ok(match value { + #[cfg(feature = "pacman")] + "arch" => Box::new(Pacman), + #[cfg(feature = "portage")] + "gentoo" => Box::new(Portage), + _ => return Err(Error::Unsupported), + }) + } + + fn to_package_manager(&self) -> Box { + match self { + #[cfg(feature = "pacman")] + Self::Pacman => Box::new(Pacman), + #[cfg(feature = "portage")] + Self::Portage => Box::new(Portage), + } + } +} + + pub trait PackageManager { - fn get_installed(&self) -> Result>; + fn get_installed(&self) -> Result; fn install(&self, pkgs: Vec) -> Result<()>; } diff --git a/src/packages/pacman.rs b/src/packages/pacman.rs index e10c6fb..0ad463b 100644 --- a/src/packages/pacman.rs +++ b/src/packages/pacman.rs @@ -1,13 +1,13 @@ use std::process::{Command, Stdio}; -use super::{Package, PackageManager}; +use super::{Package, PackageList, PackageManager}; use crate::error::{Error, Result}; pub struct Pacman; impl PackageManager for Pacman { - fn get_installed(&self) -> Result> { + fn get_installed(&self) -> Result { let pm_pkgs = Command::new("pacman").args(["-Q"]).output().unwrap(); let pm_e_pkgs = Command::new("pacman") .args(["-Q", "--explicit"]) @@ -37,16 +37,19 @@ impl PackageManager for Pacman { }); } - Ok(pkgs) + Ok(PackageList { + packages: pkgs, + manager: super::Manager::Pacman, + }) } fn install(&self, pkgs: Vec) -> Result<()> { - let mut args = vec!["--noconfirm".to_string(), "-S".to_string()]; + let mut args = vec!["pacman".to_string(), "--noconfirm".to_string(), "-S".to_string()]; for pkg in pkgs { args.push(pkg.id); } - Command::new("pacman") + Command::new("doas") .stdout(Stdio::inherit()) .args(args) .spawn()? diff --git a/src/packages/portage.rs b/src/packages/portage.rs index f9a760b..7fa09a8 100644 --- a/src/packages/portage.rs +++ b/src/packages/portage.rs @@ -1,11 +1,11 @@ use tracing::error; -use super::PackageManager; +use super::{PackageList, PackageManager}; pub struct Portage; impl PackageManager for Portage { - fn get_installed(&self) -> crate::error::Result> { + fn get_installed(&self) -> crate::error::Result { todo!() } diff --git a/src/pathinfo.rs b/src/pathinfo.rs index 8b1ca2f..03b8a6b 100644 --- a/src/pathinfo.rs +++ b/src/pathinfo.rs @@ -120,7 +120,8 @@ impl PathInfo { let old_path = modified_backup.get_absolute_file_location(config, &last_file.rel_location); let new_path = format!("{location_root}/{rel_location}"); - let mut old = File::open(old_path)?; let mut new = File::open(new_path)?; + let mut old = File::open(old_path)?; + let mut new = File::open(new_path)?; let old_len = old.metadata()?.len(); let new_len = new.metadata()?.len(); @@ -195,12 +196,9 @@ impl PathInfo { return Err(Error::InvalidDirectory(value.to_string())); }; if split.0.starts_with('~') { - if config.user.len() != 1 { - return Err(Error::MultiUser); - } return Ok(( split.1.to_string(), - LocationRoot::User(config.user[0].clone()), + LocationRoot::User, )); }; Ok(( @@ -212,18 +210,22 @@ impl PathInfo { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum LocationRoot { - User(String), + User, Custom(String), - SystemSettings, + SystemConfig, + UserConfig, Root, } impl Display for LocationRoot { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - LocationRoot::User(user) => write!(f, "/home/{user}"), + LocationRoot::User => write!(f, "{}", dirs::home_dir().unwrap().to_string_lossy()), LocationRoot::Custom(loc) => write!(f, "{loc}"), - LocationRoot::SystemSettings => write!(f, "/etc"), + LocationRoot::SystemConfig => write!(f, "/etc"), + LocationRoot::UserConfig => { + write!(f, "{}", dirs::config_local_dir().unwrap().to_string_lossy()) + } LocationRoot::Root => write!(f, "/"), } } @@ -236,8 +238,9 @@ impl LocationRoot { return Err(Error::NoIndex); }; match split_op.0 { - "u" => Ok(Self::User(split_op.1.to_string())), - "s" => Ok(Self::SystemSettings), + "u" => Ok(Self::User), + "s" => Ok(Self::SystemConfig), + "d" => Ok(Self::UserConfig), "r" => Ok(Self::Root), "c" => Ok(Self::Custom( config @@ -262,7 +265,6 @@ mod tests { backup::Backup, config::Config, error::{Error, Result}, - packages::{pacman::Pacman, PackageManager}, }; use super::LocationRoot; @@ -276,8 +278,8 @@ mod tests { .insert("test".to_string(), "/usr/local/test".to_string()); let mut values_ok: Vec<(&str, LocationRoot)> = Vec::new(); - values_ok.push(("u:test", LocationRoot::User("test".to_string()))); - values_ok.push(("s:", LocationRoot::SystemSettings)); + values_ok.push(("u:test", LocationRoot::User)); + values_ok.push(("s:", LocationRoot::SystemConfig)); values_ok.push(("r:", LocationRoot::Root)); values_ok.push(( "c:test", @@ -321,7 +323,6 @@ mod tests { #[test] fn parse_location() -> Result<()> { let mut config = Config::default(); - config.user.push("test".to_string()); config .custom_directories .insert("test".to_string(), "/usr/local/test".to_string()); @@ -331,14 +332,14 @@ mod tests { "~/.config/nvim", ( ".config/nvim".to_string(), - LocationRoot::User("test".to_string()), + LocationRoot::User, ), )); values_ok.push(( "u:test/.config/nvim", ( ".config/nvim".to_string(), - LocationRoot::User("test".to_string()), + LocationRoot::User, ), )); values_ok.push(( @@ -351,7 +352,7 @@ mod tests { )); values_ok.push(( "s:/.config/nvim", - (".config/nvim".to_string(), LocationRoot::SystemSettings), + (".config/nvim".to_string(), LocationRoot::SystemConfig), )); values_ok.push(( "c:test/.config/nvim", @@ -385,9 +386,7 @@ mod tests { let mut f = File::create("./backup-test-dir/nothing.txt")?; f.write_all("unmodified".as_bytes())?; - let pacman = Pacman; - let pkgs = pacman.get_installed()?; - let backup = Backup::create(&config, pkgs)?; + let backup = Backup::create(&config, None)?; backup.save(&config)?; let mut f = File::create("./backup-test-dir/size.txt")?; -- cgit v1.2.3