diff --git a/Cargo.lock b/Cargo.lock index 9b0dd42..6d7d6f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,6 +50,22 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] + [[package]] name = "clap" version = "4.4.12" @@ -105,18 +121,143 @@ dependencies = [ "walkdir", ] +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "git2" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf97ba92db08df386e10c8ede66a2a0369bd277090afd8710e19e38de9ec0cd" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "itoa" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +[[package]] +name = "jobserver" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +dependencies = [ + "libc", +] + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "libgit2-sys" +version = "0.16.1+1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2a2bb3680b094add03bb3732ec520ece34da31a8cd2d633d1389d0f0fb60d0c" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + +[[package]] +name = "libssh2-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pkg-config" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" + [[package]] name = "proc-macro2" version = "1.0.71" @@ -141,6 +282,7 @@ version = "0.1.0" dependencies = [ "clap", "copy_dir", + "git2", "serde_json", ] @@ -207,18 +349,65 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "unicode-bidi" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "walkdir" version = "2.4.0" diff --git a/Cargo.toml b/Cargo.toml index 18a85a5..16dac85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ publish = ["gitea"] [dependencies] clap = { version = "4.4.12", features = ["derive"] } copy_dir = "0.1.3" +git2 = "0.18.1" serde_json = "1.0.108" [[bin]] diff --git a/config.json b/config.json index c9a1070..c762b06 100644 --- a/config.json +++ b/config.json @@ -1,8 +1,7 @@ [ {"boot": [ {"grub": [ - {"themes": - ["GradientGuy"]} + {"themes": ["GradientGuy"]} ]} ]}, {"etc": [ diff --git a/src/bin/main.rs b/src/bin/main.rs index e8916f4..34ef63f 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -3,8 +3,8 @@ fn main() { use rusty_dotfiles::config; let args = config::Args::parse(); - match args.cmd { - config::Commands::Collect => rusty_dotfiles::collect::run(&args), - config::Commands::Install{ .. } => rusty_dotfiles::install::run(&args), + match args.command { + config::Command::Collect => rusty_dotfiles::collect::run(&args), + config::Command::Install{ .. } => rusty_dotfiles::install::run(&args), }; } diff --git a/src/collect.rs b/src/collect.rs index 34ceb8d..0271892 100644 --- a/src/collect.rs +++ b/src/collect.rs @@ -1,34 +1,19 @@ use crate::config; -///TODO: Docs + pub fn run(args: &config::Args) { let mut config = config::Config::new(args); - config.run(); - - config.paths.iter().for_each(|path| { - let mut destination = config.destination_path.to_string(); - destination.push_str(path.as_str()); - - match path.chars().last() { - Some('/') => create_dir(&destination), - Some(_) => copy_file(path.as_str(), &destination), - None => super::errors::empty_path_error(), - } - }); + config.run_command(); } -///TODO: Docs -fn create_dir(destination: &str) { - use std::fs; - match fs::create_dir(&destination) { - Ok(_) => (), - Err(_) => super::errors::create_dir_error(&destination), - } -} -///TODO: Docs -fn copy_file(path: &str, destination: &str) { - match copy_dir::copy_dir(path, &destination) { - Ok(_) => (), - Err(_) => super::errors::copy_file_error(path), +pub fn convert_user_folder(dirname: &str) -> &str { + let env_var = match std::env::var("USER") { + Ok(var) => var, + Err(_) => super::errors::env_var_error(), + }; + + match dirname { + name if name == env_var => "$USER", + _ => dirname, } } diff --git a/src/config.rs b/src/config.rs index 3fac277..00796d8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -19,15 +19,12 @@ pub struct Args { /// The path to the directory to do work in #[arg(short, long = "destination", default_value = "~/dotfiles")] pub destination_dir_path: String, - /// Whether to treat the user's home folder as their username or "$USER" - #[arg(short, long, default_value_t = false)] - pub use_username: bool, /// The command to be ran #[command(subcommand)] - pub cmd: Commands, + pub command: Command, } -#[derive(Subcommand, Debug)] -pub enum Commands { +#[derive(Subcommand, Clone, Debug)] +pub enum Command { /// Collect files into a destination directory Collect, /// Installs dotfiles from a git url. @@ -43,6 +40,14 @@ pub enum Commands { url: String, }, } +impl Command { + pub fn get_inner(&self) -> Option<&str> { + match self { + Command::Install { url } => Some(url), + Command::Collect => None, + } + } +} /// The root of a Linux System const ROOT: &str = "/"; /// Defines the Configuration for the progam @@ -53,10 +58,10 @@ pub struct Config { pub paths: Vec, /// The root path of all work done pub root: String, - /// Whether to treat the user's home folder as their username or "$USER" - pub use_username: bool, /// The path to the directory to do work in pub destination_path: String, + /// The command to be ran + pub command: Command, } impl Config { /// Returns a new instance of Config @@ -80,8 +85,8 @@ impl Config { paths, config, root: root.to_string(), - use_username: args.use_username, destination_path: args.destination_dir_path.to_string(), + command: args.command.clone(), } } /// Initializes the Config instance @@ -108,7 +113,7 @@ impl Config { /// * `current_root` - The current operating directory path fn generate_paths(&mut self, json_file_tree: &serde_json::Value, current_root: &mut String) { match json_file_tree { - serde_json::Value::Object(dir) => self.parse_dir(current_root.clone() ,dir), + serde_json::Value::Object(dir) => self.parse_dir(current_root.clone() , dir), serde_json::Value::Array(dir_list) => self.parse_list(dir_list, current_root), serde_json::Value::String(file) => self.parse_file(file, current_root.clone()), _ => (), @@ -122,7 +127,7 @@ impl Config { /// * `dir` - The JSON Object to be parsed as a Map fn parse_dir(&mut self, mut current_root: String, dir: &serde_json::Map) { let dirname = match dir.keys().next() { - Some(dirname) => convert_if_user_folder(dirname, self.use_username), + Some(dirname) => self.convert_user_folder(dirname), None => "".to_string(), }; @@ -150,7 +155,6 @@ impl Config { self.generate_paths(dir, &mut current_root) ); } - /// Parses a file from a JSON String /// /// # Arguments @@ -162,6 +166,30 @@ impl Config { self.paths.push(current_root.to_string()) } + fn convert_user_folder(&self, dirname: &str) -> String { + match self.command { + Command::Collect => super::collect::convert_user_folder(dirname).to_string(), + Command::Install { .. } => super::install::convert_user_folder(dirname), + } + } + + pub fn run_command(&self) { + self.paths.iter().for_each(|path| { + let mut other_path = self.destination_path.to_string(); + other_path.push_str(path); + + let collect = matches!(self.command, Command::Collect); + let install = matches!(self.command, Command::Install { .. }); + + match path.chars().last() { + Some('/') if collect => create_dir(&other_path), + Some('/') if install => create_dir(&path), + Some(_) if collect => copy_file(&path, &other_path), + Some(_) if install => copy_file(&other_path, &path), + _ => errors::empty_path_error(), + } + }) + } } /// Parses the JSON file at file_path and returns a serde_json::Value. /// Exits program upon error. @@ -212,14 +240,18 @@ fn read_file_from_path(file_path: &str) -> String { } } ///TODO: Docs -fn convert_if_user_folder(dirname: &str, want_username: bool) -> String { - use std::env; +fn create_dir(destination: &str) { + use std::fs; - let env_user = env::var("USER").unwrap_or("\n".to_string()); - - match dirname { - name if !want_username & (name == env_user) => "$USER".to_string(), - "$USER" if want_username => env_user, - _ => dirname.to_string(), + match fs::create_dir(&destination) { + Ok(_) => (), + Err(_) => errors::create_dir_error(&destination), + } +} +///TODO: Docs +fn copy_file(path: &str, destination: &str) { + match copy_dir::copy_dir(path, &destination) { + Ok(_) => (), + Err(_) => errors::copy_file_error(path), } } diff --git a/src/errors.rs b/src/errors.rs index 8d09b17..4a240b2 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -15,3 +15,15 @@ pub fn copy_file_error(path: &str) { pub fn empty_path_error() { eprintln!("A path seems to be empty"); } +pub fn no_url_error() -> ! { + eprintln!("No URL supplied!"); + std::process::exit(127); +} +pub fn clone_error() -> ! { + eprintln!("There was an issue cloning the repo. Make sure that you have an internet connection and the url is correct"); + std::process::exit(1); +} +pub fn env_var_error() -> ! { + eprintln!("The $USER Environment variable is not set."); + std::process::exit(1); +} diff --git a/src/install.rs b/src/install.rs index 3bfb582..f80b2b8 100644 --- a/src/install.rs +++ b/src/install.rs @@ -1,8 +1,38 @@ use crate::config; //TODO: mod install - //TODO: clone dotfiles - //TODO: copy dotfiles to location in config + //TODO: copy dotfiles from destination_path + path to path in config skip first item of list pub fn run(args: &config::Args) { - () + + let mut config = config::Config::new(args); + config.run(); + + let url = match config.command.get_inner() { + Some(url) => url, + None => super::errors::no_url_error(), + }; + + clone_repo(url, &config.destination_path); + config.run_command(); + } +pub fn convert_user_folder(dirname: &str) -> String { + let env_var = match std::env::var("USER") { + Ok(var) => var, + Err(_) => super::errors::env_var_error(), + }; + + match dirname { + name if name == "$USER" => env_var, + _ => dirname.to_string(), + } +} + + +fn clone_repo(url: &str, destination: &str) { + use git2::Repository; + + match Repository::clone(url, destination) { + Ok(_repo) => (), + Err(_) => super::errors::clone_error(), + } } diff --git a/src/lib.rs b/src/lib.rs index 9be775a..343f5be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,4 @@ pub mod collect; pub mod config; pub mod errors; -pub mod header; pub mod install;