aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorottjk <joshott16@gmail.com>2023-12-30 19:40:27 -0500
committerottjk <joshott16@gmail.com>2023-12-30 19:40:27 -0500
commit6536f3aae95e266f80a5a73288e181ff10ce650b (patch)
tree2fa309c246ab4ad860da8673a82dcb4ead924919
downloaddfa-6536f3aae95e266f80a5a73288e181ff10ce650b.tar.gz
dfa-6536f3aae95e266f80a5a73288e181ff10ce650b.zip
initial commit
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock571
-rw-r--r--Cargo.toml13
-rw-r--r--src/arguments.rs59
-rw-r--r--src/commands/mod.rs88
-rw-r--r--src/commands/sync.rs125
-rw-r--r--src/main.rs60
7 files changed, 917 insertions, 0 deletions
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..af11ca6
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,571 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "anstream"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "4.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "copy_dir"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "543d1dd138ef086e2ff05e3a48cf9da045da2033d16f8538fd76b86cd49b2ca3"
+dependencies = [
+ "walkdir",
+]
+
+[[package]]
+name = "dfa"
+version = "0.1.0"
+dependencies = [
+ "clap",
+ "copy_dir",
+ "dirs",
+ "serde",
+ "toml",
+]
+
+[[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 = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "getrandom"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "indexmap"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.151"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
+
+[[package]]
+name = "libredox"
+version = "0.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
+dependencies = [
+ "bitflags 2.4.1",
+ "libc",
+ "redox_syscall",
+]
+
+[[package]]
+name = "memchr"
+version = "2.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+
+[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.71"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
+dependencies = [
+ "getrandom",
+ "libredox",
+ "thiserror",
+]
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.193"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.193"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "syn"
+version = "2.0.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "toml"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "walkdir"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+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 0.52.0",
+]
+
+[[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]]
+name = "windows-targets"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.0",
+ "windows_aarch64_msvc 0.52.0",
+ "windows_i686_gnu 0.52.0",
+ "windows_i686_msvc 0.52.0",
+ "windows_x86_64_gnu 0.52.0",
+ "windows_x86_64_gnullvm 0.52.0",
+ "windows_x86_64_msvc 0.52.0",
+]
+
+[[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
+
+[[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
+
+[[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
+
+[[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
+
+[[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
+
+[[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
+
+[[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
+
+[[package]]
+name = "winnow"
+version = "0.5.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b5c3db89721d50d0e2a673f5043fc4722f76dcc352d7b1ab8b8288bed4ed2c5"
+dependencies = [
+ "memchr",
+]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..63838be
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "dfa"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+clap = { version = "4.4.11", features = ["derive"] }
+copy_dir = "0.1.3"
+dirs = "5.0.1"
+serde = { version = "1.0.193", features = ["derive"] }
+toml = "0.8.8"
diff --git a/src/arguments.rs b/src/arguments.rs
new file mode 100644
index 0000000..24697d9
--- /dev/null
+++ b/src/arguments.rs
@@ -0,0 +1,59 @@
+use clap::{Parser,Subcommand,Args};
+use std::path::PathBuf;
+
+#[derive(Parser)]
+pub struct Cli {
+ #[command(subcommand)]
+ pub command: Commands,
+
+ /// Configuration file location
+ #[arg(short, long)]
+ pub config: Option<PathBuf>,
+}
+
+#[derive(Subcommand)]
+pub enum Commands {
+ /// Add or update config from list
+ Modify(ModifyArgs),
+
+ /// Remove program from list
+ Remove(RemoveArgs),
+
+ /// Print list of tracked configs
+ List {},
+
+ /// Update a config
+ Sync(SyncArgs),
+}
+
+#[derive(Args)]
+pub struct ModifyArgs {
+ /// Name of program
+ pub name: String,
+
+ /// Parent directory on system config resides in
+ pub dest: PathBuf,
+
+ /// File(s) in configs directory
+ pub file: PathBuf,
+}
+
+#[derive(Args)]
+pub struct RemoveArgs {
+ /// Name of program
+ pub name: String,
+}
+
+#[derive(Args)]
+pub struct SyncArgs {
+ /// Names of programs to update
+ pub names: Vec<String>,
+
+ /// Sync all programs
+ #[arg(short, long)]
+ pub all: bool,
+
+ /// Never prompt to skip
+ #[arg(short, long)]
+ pub force: bool,
+}
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
new file mode 100644
index 0000000..b1c5f51
--- /dev/null
+++ b/src/commands/mod.rs
@@ -0,0 +1,88 @@
+use std::{
+ fs,
+ io::{self,Result,Write},
+ path::PathBuf,
+};
+
+mod sync;
+
+use crate::{Config,Entry};
+use crate::arguments::*;
+
+fn save_config(config: &Config, path: &PathBuf) -> Result<()> {
+ let toml = toml::to_string(config).unwrap();
+ fs::write(path.join("configs.toml"), toml)?;
+ Ok(())
+}
+
+pub fn modify_command(
+ config: &mut Config,
+ args: ModifyArgs,
+ configs_path: &PathBuf,
+) -> Result<()> {
+
+ if config.contains_key(&args.name) { loop {
+ let mut input = String::new();
+
+ print!("Entry already exists for {}. Overwrite it? (y/n) ", &args.name);
+ let _ = io::stdout().flush();
+ io::stdin()
+ .read_line(&mut input)
+ .expect("Failed to read user input.");
+
+ match input.to_lowercase().trim() {
+ "y" => break,
+ "n" => return Ok(()),
+ _ => {
+ println!("Try again :)");
+ continue;
+ },
+ };
+ }}
+
+ let entry = Entry { file: args.file, parent: args.dest };
+ config.insert(args.name, entry);
+ save_config(&config, configs_path)?;
+
+ Ok(())
+}
+
+pub fn remove_command(
+ config: &mut Config,
+ args: RemoveArgs,
+ configs_path: &PathBuf
+) -> Result<()> {
+
+ if !config.contains_key(&args.name) {
+ panic!("Such an entry does not exist.");
+ }
+ config.remove(&args.name);
+ save_config(&config, configs_path)?;
+
+ Ok(())
+}
+
+pub fn list_command(config: &Config) {
+ for (key,entry) in config {
+ println!(r#""{}" - src: {}, dest: {}"#, key, entry.file.to_str().unwrap(), entry.parent.to_str().unwrap());
+ }
+}
+
+pub fn sync_command(config: &Config, args: SyncArgs, configs_path: &PathBuf) {
+ if args.all {
+ sync::sync_all(config, configs_path, args.force);
+ return
+ }
+
+ for name in &args.names {
+ let entry = match config.get(name) {
+ Some(e) => e,
+ None => {
+ eprintln!(r#"Entry "{name}" not found."#);
+ continue;
+ },
+ };
+
+ sync::sync_config(name, entry, configs_path, &args.force);
+ }
+}
diff --git a/src/commands/sync.rs b/src/commands/sync.rs
new file mode 100644
index 0000000..eaf3dab
--- /dev/null
+++ b/src/commands/sync.rs
@@ -0,0 +1,125 @@
+use std::{
+ io::{self,ErrorKind,Write},
+ fs,
+ path::PathBuf,
+};
+use copy_dir::copy_dir;
+
+use crate::{resolve_home,Config,Entry};
+
+#[derive(Debug, PartialEq, Eq)]
+enum ConflictOption {
+ Remove,
+ Rename,
+ Skip,
+}
+
+fn remove_ambiguous(path: &PathBuf) -> io::Result<()> {
+ if path.is_dir() {
+ fs::remove_dir_all(&path)?;
+ } else {
+ fs::remove_file(&path)?;
+ }
+
+ Ok(())
+}
+
+fn rename_conflict(path: &PathBuf) -> io::Result<()> {
+ let rename_path = path.with_extension("old");
+
+ if rename_path.try_exists()? {
+ remove_ambiguous(&rename_path)?;
+ }
+
+ fs::rename(&path, &rename_path)?;
+ println!("Old config renamed to {}.", rename_path.file_name().unwrap().to_str().unwrap());
+
+ Ok(())
+}
+
+fn remove_existing(name: &str, path: &PathBuf) -> io::Result<Option<ConflictOption>> {
+ loop {
+ let mut input = String::new();
+
+ print!("Remove existing config for {name}? (y/n/r[ename]) ");
+ let _ = io::stdout().flush();
+ io::stdin()
+ .read_line(&mut input)
+ .expect("Failed to read user input.");
+
+ let choice = match input.to_lowercase().trim() {
+ "y" => ConflictOption::Remove,
+ "n" => ConflictOption::Skip,
+ "r" => ConflictOption::Rename,
+ _ => {
+ println!("Try again :)");
+ continue;
+ },
+ };
+
+ match choice {
+ ConflictOption::Remove =>
+ remove_ambiguous(path)?,
+ ConflictOption::Rename =>
+ rename_conflict(path)?,
+ ConflictOption::Skip =>
+ println!("Skipping {name}."),
+ };
+
+ return Ok(Some(choice));
+ }
+}
+
+fn sync_file(
+ name: &str,
+ src: &PathBuf,
+ dest: &PathBuf,
+ force: &bool
+) -> io::Result<Option<ConflictOption>> {
+
+ let copy_result = copy_dir(src, dest);
+
+ if let Err(error) = copy_result {
+ if error.kind() == ErrorKind::AlreadyExists {
+ if *force {
+ remove_ambiguous(dest)?;
+ return Ok(Some(ConflictOption::Remove));
+ } else {
+ return remove_existing(name, dest);
+ }
+ }
+ return Err(error);
+ }
+
+ Ok(None)
+}
+
+pub fn sync_config(name: &str, entry: &Entry, configs_path: &PathBuf, force: &bool) {
+ let src = configs_path.join(&entry.file);
+ let mut dest = entry.parent.join(&entry.file);
+ resolve_home(&mut dest);
+
+ loop {
+ let sync_result = sync_file(name, &src, &dest, force);
+
+ match sync_result {
+ Ok(None) => println!("Updated {}.", dest.to_str().unwrap()),
+ Ok(Some(choice)) => match choice {
+ ConflictOption::Skip => break,
+ _ => continue,
+ },
+ Err(error) => {
+ eprintln!("Skipping sync of {src:?} to {dest:?}: {error:?}");
+ break;
+ },
+ };
+
+ break;
+ }
+}
+
+pub fn sync_all(config: &Config, configs_path: &PathBuf, force: bool) {
+ for (name, entry) in config {
+ sync_config(&name, &entry, configs_path, &force);
+ }
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..59f3a3c
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,60 @@
+use std::{
+ fs,
+ io::Result,
+ collections::HashMap,
+ path::PathBuf,
+};
+use serde::{Deserialize,Serialize};
+use clap::Parser;
+
+mod commands;
+use commands::*;
+
+mod arguments;
+use arguments::*;
+
+#[derive(Deserialize, Serialize)]
+pub struct Entry {
+ file: PathBuf,
+ parent: PathBuf,
+}
+
+pub type Config = HashMap<String, Entry>;
+
+pub fn resolve_home(path: &mut PathBuf) {
+ if path.starts_with("~") {
+ let temp = path.strip_prefix("~").unwrap();
+ *path = dirs::home_dir().expect("Could not resolve home directory.")
+ .join(temp);
+ }
+}
+
+fn read_config(path: &PathBuf) -> Config {
+ let file = fs::read_to_string(path).unwrap_or(String::new());
+ toml::from_str(&file).unwrap()
+}
+
+fn main() -> Result<()> {
+ let cli = Cli::parse();
+
+ let mut configs_path = cli.config
+ .unwrap_or(dirs::config_dir()
+ .expect("Could not resolve config directory.")
+ .join("dotfiles"));
+ resolve_home(&mut configs_path);
+
+ if !configs_path.try_exists().unwrap() {
+ fs::create_dir_all(&configs_path).unwrap();
+ }
+
+ let mut config = read_config(&configs_path.join("configs.toml"));
+
+ match cli.command {
+ Commands::Modify(args) => modify_command(&mut config, args, &configs_path)?,
+ Commands::Remove(args) => remove_command(&mut config, args, &configs_path)?,
+ Commands::Sync(args) => sync_command(&config, args, &configs_path),
+ Commands::List {} => list_command(&config),
+ };
+
+ Ok(())
+}