From 3e1cb020d5449849b37874f91cadfa4a9c878747 Mon Sep 17 00:00:00 2001 From: fxqnlr Date: Fri, 6 Sep 2024 10:56:30 +0200 Subject: initial commit, can save index, no modification check --- .gitignore | 1 + Cargo.lock | 564 +++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 13 ++ src/backup.rs | 44 ++++ src/config.rs | 33 +++ src/error.rs | 19 ++ src/main.rs | 32 +++ src/packages.rs | 16 ++ src/packages/pacman.rs | 46 ++++ src/pathinfo.rs | 246 +++++++++++++++++++++ src/storage.rs | 8 + 11 files changed, 1022 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/backup.rs create mode 100644 src/config.rs create mode 100644 src/error.rs create mode 100644 src/main.rs create mode 100644 src/packages.rs create mode 100644 src/packages/pacman.rs create mode 100644 src/pathinfo.rs create mode 100644 src/storage.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ef301aa --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,564 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "async-trait" +version = "0.1.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "config" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be" +dependencies = [ + "async-trait", + "convert_case", + "json5", + "lazy_static", + "nom", + "pathdiff", + "ron", + "rust-ini", + "serde", + "serde_json", + "toml", + "yaml-rust", +] + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fxbaup" +version = "0.1.0" +dependencies = [ + "anyhow", + "config", + "serde", + "serde_json", + "thiserror", + "toml", + "uuid", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "indexmap" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "ordered-multimap" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e" +dependencies = [ + "dlv-list", + "hashbrown 0.13.2", +] + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "pest" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64", + "bitflags", + "serde", + "serde_derive", +] + +[[package]] +name = "rust-ini" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.209" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.209" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +dependencies = [ + "getrandom", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +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 new file mode 100644 index 0000000..7057f7a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "fxbaup" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.86" +config = "0.14.0" +serde = { version = "1.0.209", features = ["derive"] } +serde_json = "1.0.128" +thiserror = "1.0.63" +toml = "0.8.19" +uuid = { version = "1.10.0", features = ["v4"] } diff --git a/src/backup.rs b/src/backup.rs new file mode 100644 index 0000000..4e74c97 --- /dev/null +++ b/src/backup.rs @@ -0,0 +1,44 @@ +use std::time::{SystemTime, UNIX_EPOCH}; + +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use crate::{config::Config, pathinfo::PathInfo, packages::Package, error::Result}; + +pub type BackupId = String; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Backup { + id: String, + timestamp: u64, + packages: Vec, + files: Vec, +} + +impl Backup { + pub fn create(config: &Config, packages: Vec) -> Result { + let mut files: Vec = Vec::new(); + for dir in &config.directories { + files.push(PathInfo::from_path(config, dir)?); + } + Ok(Self { + // UUID not really needed, maybe a shorter hash + id: Uuid::new_v4().to_string(), + timestamp: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(), + packages, + files, + }) + } + + +} + +struct BackupLocation { + id: BackupId, + rel_location: String, +} + +type BackupList = Vec; diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..625118a --- /dev/null +++ b/src/config.rs @@ -0,0 +1,33 @@ +use config::{File, Map}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(default)] +pub struct Config { + pub root: String, + pub user: Vec, + pub directories: Vec, + pub custom_directories: Map +} + +impl Default for Config { + fn default() -> Self { + Self { + root: "/mnt/backup".to_string(), + user: vec![], + directories: vec![], + custom_directories: Map::new(), + } + } +} + +impl Config { + pub fn load() -> Result { + let config = config::Config::builder() + .add_source(File::with_name("config.toml").required(false)) + .add_source(config::Environment::with_prefix("FXBAUP").separator("_")) + .build()?; + + config.try_deserialize() + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..77eab69 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,19 @@ +pub type Result = std::result::Result; + +#[derive(Debug, PartialEq, Eq, thiserror::Error)] +pub enum Error { + #[error("unknown custom directory '{0}'")] + CustomDirectory(String), + + #[error("invalid directory index '{0}'")] + InvalidIndex(String), + + #[error("no directory index given")] + NoIndex, + + #[error("invalid directory '{0}'")] + InvalidDirectory(String), + + #[error("Only exactly one user allowed in config")] + MultiUser, +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..1fdcebf --- /dev/null +++ b/src/main.rs @@ -0,0 +1,32 @@ +use backup::Backup; +use config::Config; +use packages::{pacman::Pacman, PackageManager}; +use storage::save_index; + +mod backup; +mod config; +mod error; +mod pathinfo; +mod packages; +mod storage; + +fn main() -> anyhow::Result<()> { + let mut cfg = Config::load()?; + cfg.user.push("fx".to_string()); + cfg.directories.push("~/.config/nvim".to_string()); + cfg.directories.push("~/.config/hypr".to_string()); + let toml = toml::to_string(&cfg)?; + println!("{toml}"); + + let pacman = Pacman; + let pkgs = pacman.get_installed(); + + let backup = Backup::create(&cfg, pkgs)?; + // println!("{backup:#?}"); + + save_index(backup); + + // let fi = FileInfo::new("~/.config/nvim", &cfg)?; + // println!("{:?}", fi.get_absolute_path()); + Ok(()) +} diff --git a/src/packages.rs b/src/packages.rs new file mode 100644 index 0000000..9f765d6 --- /dev/null +++ b/src/packages.rs @@ -0,0 +1,16 @@ +use serde::{Deserialize, Serialize}; + +pub mod pacman; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Package { + pub id: String, + pub version: String, + pub explicit: bool +} + +pub trait PackageManager { + fn get_installed(&self) -> Vec; + + fn install(&self, pkgs: Vec); +} diff --git a/src/packages/pacman.rs b/src/packages/pacman.rs new file mode 100644 index 0000000..0a9e1ff --- /dev/null +++ b/src/packages/pacman.rs @@ -0,0 +1,46 @@ +use std::process::Command; + +use crate::packages::Package; + +use super::PackageManager; + +pub struct Pacman; + +impl PackageManager for Pacman { + fn get_installed(&self) -> Vec { + let pm_pkgs = Command::new("pacman").args(["-Q"]).output().unwrap(); + let pm_e_pkgs = Command::new("pacman") + .args(["-Q", "--explicit"]) + .output() + .unwrap(); + + let pm_pkgs_out = String::from_utf8(pm_pkgs.stdout).unwrap(); + let pm_e_pkgs_out = String::from_utf8(pm_e_pkgs.stdout).unwrap(); + + let mut pkgs: Vec = Vec::new(); + let pacman_pkgs: Vec<&str> = pm_pkgs_out.split('\n').collect(); + for pkg in pacman_pkgs { + if pkg.is_empty() { + continue; + }; + let split: Vec<&str> = pkg.split_whitespace().collect(); + if split.len() != 2 { + panic!("Unknown Pacman Output"); + }; + + let explicit = pm_e_pkgs_out.contains(pkg); + + pkgs.push(Package { + id: split[0].to_string(), + version: split[1].to_string(), + explicit + }) + } + + pkgs + } + + fn install(&self, pkgs: Vec) { + todo!(); + } +} diff --git a/src/pathinfo.rs b/src/pathinfo.rs new file mode 100644 index 0000000..b0c3be4 --- /dev/null +++ b/src/pathinfo.rs @@ -0,0 +1,246 @@ +use std::{ + fmt::Display, + path::PathBuf, +}; + +use serde::{Deserialize, Serialize}; + +use crate::{ + backup::BackupId, + config::Config, + error::{Error, Result}, +}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PathInfo { + pub modified: bool, + pub is_file: bool, + rel_location: String, + location_root: LocationRoot, + last_modified: BackupId, + children: Vec +} + +impl PathInfo { + pub fn from_path(config: &Config, path: &str) -> Result { + let locations = Self::parse_location(path, config)?; + + Ok(Self::handle_dir(config, &locations.0, &locations.1)?) + } + + fn handle_dir( + config: &Config, + rel_location: &str, + location_root: &LocationRoot, + ) -> Result { + println!("Handling {rel_location}"); + let path = Self::get_abs_path(&location_root.to_string(), rel_location); + Ok(if path.is_dir() { + let mut modified = false; + let mut children: Vec = Vec::new(); + + let paths = std::fs::read_dir(path).unwrap(); + for path in paths { + let pathstr = path.unwrap().path().to_string_lossy().to_string(); + let root = format!("{}/", location_root.to_string()); + let Some(rl) = pathstr.split_once(&root) else { + panic!("HUH"); + }; + let handle = Self::handle_dir(config, rl.1, location_root)?; + if handle.modified { + modified = true; + }; + children.push(handle); + } + Self { + modified, + is_file: false, + rel_location: rel_location.to_string(), + location_root: location_root.clone(), + last_modified: "".to_string(), + children + } + } else { + Self::from_file(rel_location, location_root.clone())? + }) + } + + fn from_file(rel_location: &str, location_root: LocationRoot) -> Result { + println!("From file {rel_location}"); + + let modified = false; + + Ok(Self { + rel_location: rel_location.to_string(), + location_root, + modified, + last_modified: "".to_string(), + is_file: true, + children: Vec::new() + }) + } + + pub fn get_absolute_path(&self) -> PathBuf { + Self::get_abs_path(&self.location_root.to_string(), &self.rel_location) + } + + fn get_abs_path(location_root: &str, rel_location: &str) -> PathBuf { + let path = format!("{}/{}", location_root, rel_location); + PathBuf::from(path) + } + + fn parse_location(value: &str, config: &Config) -> Result<(String, LocationRoot)> { + let Some(split) = value.split_once('/') else { + 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()), + )); + }; + Ok(( + split.1.to_string(), + LocationRoot::from_op_str(split.0, config)?, + )) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum LocationRoot { + User(String), + Custom(String), + SystemSettings, + 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::Custom(loc) => write!(f, "{loc}"), + LocationRoot::SystemSettings => write!(f, "/etc"), + LocationRoot::Root => write!(f, "/"), + } + } +} + +impl LocationRoot { + fn from_op_str(value: &str, config: &Config) -> Result { + let split_str = value.split_once(':'); + let Some(split_op) = split_str else { + return Err(Error::NoIndex); + }; + match split_op.0 { + "u" => Ok(Self::User(split_op.1.to_string())), + "s" => Ok(Self::SystemSettings), + "r" => Ok(Self::Root), + "c" => Ok(Self::Custom( + config + .custom_directories + .get(split_op.1) + .ok_or_else(|| Error::CustomDirectory(split_op.1.to_string()))? + .to_string(), + )), + _ => Err(Error::InvalidIndex(split_op.0.to_string())), + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + config::Config, + error::{Error, Result}, + pathinfo::PathInfo, + }; + + use super::LocationRoot; + + #[test] + fn from_op_str() -> Result<()> { + let mut config = Config::default(); + config + .custom_directories + .insert("test".to_string(), "/usr/local/test".to_string()); + + let mut values: Vec<(&str, Result)> = Vec::new(); + values.push(("u:test", Ok(LocationRoot::User("test".to_string())))); + values.push(("s:", Ok(LocationRoot::SystemSettings))); + values.push(("r:", Ok(LocationRoot::Root))); + values.push(( + "c:test", + Ok(LocationRoot::Custom("/usr/local/test".to_string())), + )); + values.push(("c:rest", Err(Error::CustomDirectory("rest".to_string())))); + values.push(("t:test/", Err(Error::InvalidIndex("t".to_string())))); + values.push(( + "test:test/usr", + Err(Error::InvalidIndex("test".to_string())), + )); + values.push(("/usr/local/test", Err(Error::NoIndex))); + values.push(("c/usr/local/test", Err(Error::NoIndex))); + + for value in values { + print!("Testing {value:?}"); + assert_eq!(LocationRoot::from_op_str(value.0, &config), value.1); + println!("\rTesting {value:?} ✓"); + } + + Ok(()) + } + + #[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()); + + let mut values: Vec<(&str, Result<(String, LocationRoot)>)> = Vec::new(); + values.push(( + "~/.config/nvim", + Ok(( + ".config/nvim".to_string(), + LocationRoot::User("test".to_string()), + )), + )); + values.push(( + "u:test/.config/nvim", + Ok(( + ".config/nvim".to_string(), + LocationRoot::User("test".to_string()), + )), + )); + values.push(( + "r:/.config/nvim", + Ok((".config/nvim".to_string(), LocationRoot::Root)), + )); + values.push(( + "r:/.config/nvim", + Ok((".config/nvim".to_string(), LocationRoot::Root)), + )); + values.push(( + "s:/.config/nvim", + Ok((".config/nvim".to_string(), LocationRoot::SystemSettings)), + )); + values.push(( + "c:test/.config/nvim", + Ok(( + ".config/nvim".to_string(), + LocationRoot::Custom("/usr/local/test".to_string()), + )), + )); + + for value in values { + print!("Testing {value:?}"); + assert_eq!(PathInfo::parse_location(&value.0, &config), value.1); + println!("\rTesting {value:?} ✓"); + } + Ok(()) + } +} diff --git a/src/storage.rs b/src/storage.rs new file mode 100644 index 0000000..b9e8de9 --- /dev/null +++ b/src/storage.rs @@ -0,0 +1,8 @@ +use std::{fs::File, io::Write}; + +use crate::backup::Backup; + +pub fn save_index(backup: Backup) { + let mut f = File::create("./index.json").unwrap(); + f.write_all(&serde_json::to_vec(&backup).unwrap()).unwrap(); +} -- cgit v1.2.3