install capability

This commit is contained in:
Joshua Perry 2024-01-03 15:32:18 +00:00
parent f9fc882801
commit 11d5c308ea
9 changed files with 302 additions and 55 deletions

189
Cargo.lock generated
View File

@ -50,6 +50,22 @@ dependencies = [
"windows-sys", "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]] [[package]]
name = "clap" name = "clap"
version = "4.4.12" version = "4.4.12"
@ -105,18 +121,143 @@ dependencies = [
"walkdir", "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]] [[package]]
name = "heck" name = "heck"
version = "0.4.1" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 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]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.10" version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" 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]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.71" version = "1.0.71"
@ -141,6 +282,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"clap", "clap",
"copy_dir", "copy_dir",
"git2",
"serde_json", "serde_json",
] ]
@ -207,18 +349,65 @@ dependencies = [
"unicode-ident", "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]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.12" version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 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]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.1" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]] [[package]]
name = "walkdir" name = "walkdir"
version = "2.4.0" version = "2.4.0"

View File

@ -9,6 +9,7 @@ publish = ["gitea"]
[dependencies] [dependencies]
clap = { version = "4.4.12", features = ["derive"] } clap = { version = "4.4.12", features = ["derive"] }
copy_dir = "0.1.3" copy_dir = "0.1.3"
git2 = "0.18.1"
serde_json = "1.0.108" serde_json = "1.0.108"
[[bin]] [[bin]]

View File

@ -1,8 +1,7 @@
[ [
{"boot": [ {"boot": [
{"grub": [ {"grub": [
{"themes": {"themes": ["GradientGuy"]}
["GradientGuy"]}
]} ]}
]}, ]},
{"etc": [ {"etc": [

View File

@ -3,8 +3,8 @@ fn main() {
use rusty_dotfiles::config; use rusty_dotfiles::config;
let args = config::Args::parse(); let args = config::Args::parse();
match args.cmd { match args.command {
config::Commands::Collect => rusty_dotfiles::collect::run(&args), config::Command::Collect => rusty_dotfiles::collect::run(&args),
config::Commands::Install{ .. } => rusty_dotfiles::install::run(&args), config::Command::Install{ .. } => rusty_dotfiles::install::run(&args),
}; };
} }

View File

@ -1,34 +1,19 @@
use crate::config; use crate::config;
///TODO: Docs
pub fn run(args: &config::Args) { pub fn run(args: &config::Args) {
let mut config = config::Config::new(args); let mut config = config::Config::new(args);
config.run(); config.run();
config.run_command();
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(),
}
});
} }
///TODO: Docs
fn create_dir(destination: &str) {
use std::fs;
match fs::create_dir(&destination) { pub fn convert_user_folder(dirname: &str) -> &str {
Ok(_) => (), let env_var = match std::env::var("USER") {
Err(_) => super::errors::create_dir_error(&destination), Ok(var) => var,
} Err(_) => super::errors::env_var_error(),
} };
///TODO: Docs
fn copy_file(path: &str, destination: &str) { match dirname {
match copy_dir::copy_dir(path, &destination) { name if name == env_var => "$USER",
Ok(_) => (), _ => dirname,
Err(_) => super::errors::copy_file_error(path),
} }
} }

View File

@ -19,15 +19,12 @@ pub struct Args {
/// The path to the directory to do work in /// The path to the directory to do work in
#[arg(short, long = "destination", default_value = "~/dotfiles")] #[arg(short, long = "destination", default_value = "~/dotfiles")]
pub destination_dir_path: String, 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 /// The command to be ran
#[command(subcommand)] #[command(subcommand)]
pub cmd: Commands, pub command: Command,
} }
#[derive(Subcommand, Debug)] #[derive(Subcommand, Clone, Debug)]
pub enum Commands { pub enum Command {
/// Collect files into a destination directory /// Collect files into a destination directory
Collect, Collect,
/// Installs dotfiles from a git url. /// Installs dotfiles from a git url.
@ -43,6 +40,14 @@ pub enum Commands {
url: String, 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 /// The root of a Linux System
const ROOT: &str = "/"; const ROOT: &str = "/";
/// Defines the Configuration for the progam /// Defines the Configuration for the progam
@ -53,10 +58,10 @@ pub struct Config {
pub paths: Vec<String>, pub paths: Vec<String>,
/// The root path of all work done /// The root path of all work done
pub root: String, 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 /// The path to the directory to do work in
pub destination_path: String, pub destination_path: String,
/// The command to be ran
pub command: Command,
} }
impl Config { impl Config {
/// Returns a new instance of Config /// Returns a new instance of Config
@ -80,8 +85,8 @@ impl Config {
paths, paths,
config, config,
root: root.to_string(), root: root.to_string(),
use_username: args.use_username,
destination_path: args.destination_dir_path.to_string(), destination_path: args.destination_dir_path.to_string(),
command: args.command.clone(),
} }
} }
/// Initializes the Config instance /// Initializes the Config instance
@ -108,7 +113,7 @@ impl Config {
/// * `current_root` - The current operating directory path /// * `current_root` - The current operating directory path
fn generate_paths(&mut self, json_file_tree: &serde_json::Value, current_root: &mut String) { fn generate_paths(&mut self, json_file_tree: &serde_json::Value, current_root: &mut String) {
match json_file_tree { 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::Array(dir_list) => self.parse_list(dir_list, current_root),
serde_json::Value::String(file) => self.parse_file(file, current_root.clone()), 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 /// * `dir` - The JSON Object to be parsed as a Map
fn parse_dir(&mut self, mut current_root: String, dir: &serde_json::Map<String, serde_json::Value>) { fn parse_dir(&mut self, mut current_root: String, dir: &serde_json::Map<String, serde_json::Value>) {
let dirname = match dir.keys().next() { 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(), None => "".to_string(),
}; };
@ -150,7 +155,6 @@ impl Config {
self.generate_paths(dir, &mut current_root) self.generate_paths(dir, &mut current_root)
); );
} }
/// Parses a file from a JSON String /// Parses a file from a JSON String
/// ///
/// # Arguments /// # Arguments
@ -162,6 +166,30 @@ impl Config {
self.paths.push(current_root.to_string()) 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. /// Parses the JSON file at file_path and returns a serde_json::Value.
/// Exits program upon error. /// Exits program upon error.
@ -212,14 +240,18 @@ fn read_file_from_path(file_path: &str) -> String {
} }
} }
///TODO: Docs ///TODO: Docs
fn convert_if_user_folder(dirname: &str, want_username: bool) -> String { fn create_dir(destination: &str) {
use std::env; use std::fs;
let env_user = env::var("USER").unwrap_or("\n".to_string()); match fs::create_dir(&destination) {
Ok(_) => (),
match dirname { Err(_) => errors::create_dir_error(&destination),
name if !want_username & (name == env_user) => "$USER".to_string(), }
"$USER" if want_username => env_user, }
_ => dirname.to_string(), ///TODO: Docs
fn copy_file(path: &str, destination: &str) {
match copy_dir::copy_dir(path, &destination) {
Ok(_) => (),
Err(_) => errors::copy_file_error(path),
} }
} }

View File

@ -15,3 +15,15 @@ pub fn copy_file_error(path: &str) {
pub fn empty_path_error() { pub fn empty_path_error() {
eprintln!("A path seems to be empty"); 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);
}

View File

@ -1,8 +1,38 @@
use crate::config; use crate::config;
//TODO: mod install //TODO: mod install
//TODO: clone dotfiles //TODO: copy dotfiles from destination_path + path to path in config skip first item of list
//TODO: copy dotfiles to location in config
pub fn run(args: &config::Args) { 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(),
}
} }

View File

@ -1,5 +1,4 @@
pub mod collect; pub mod collect;
pub mod config; pub mod config;
pub mod errors; pub mod errors;
pub mod header;
pub mod install; pub mod install;