pub mod cli;
pub mod path;
pub mod readme_template;
use git2::build::RepoBuilder;
use git2::{Cred, FetchOptions, RemoteCallbacks, Repository};
use log::info;
use path::{file_to_path, home_path};
use rayon::prelude::*;
use readme_template::write_template_readme;
use serde::{Deserialize, Serialize};
use std::fs::{copy, create_dir_all, File};
use std::path::{Path, PathBuf};
use std::string::String;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct DotFiles {
dotfiles: Vec<String>,
}
fn copy_file(dest_file: String, orig_file: String, dry_run: bool) -> Result<u8, String> {
let source_file_pathbuf: PathBuf = file_to_path(&orig_file, true)?;
let dest_file_pathbuf: PathBuf = file_to_path(&dest_file, false)?;
let prefix: &Option<&Path> = &dest_file_pathbuf.parent();
if let Some(prefix_path) = prefix {
create_dir_all(prefix_path).map_err(|e| e.to_string())?
}
let mut label: &str = "Dry run";
if !dry_run {
copy(source_file_pathbuf, dest_file_pathbuf).map_err(|e| e.to_string())?;
label = "Shell";
}
info!("[{}] Copied {} to {}", label, orig_file, dest_file,);
Ok(0)
}
pub fn save(
dotfile_list: Vec<String>,
destination_dir: String,
dry_run: bool,
) -> Result<Vec<u8>, String> {
let home_dir: String = home_path()?;
if !dry_run {
write_template_readme(format!("{}/README.md", &destination_dir))?;
}
dotfile_list
.into_par_iter()
.map(|dotfile| {
let orig_file: String = format!("{}/{}", home_dir, dotfile);
let dest_file: String = format!("{}/{}", destination_dir, dotfile);
copy_file(dest_file, orig_file, dry_run)
})
.collect()
}
pub fn apply(
dotfile_list: Vec<String>,
dotfiles_dir: String,
dry_run: bool,
) -> Result<Vec<u8>, String> {
info!("Applying dotfiles from: {}", dotfiles_dir);
let home_dir: String = home_path()?;
dotfile_list
.into_par_iter()
.map(|dotfile| {
let orig_file: String = format!("{}/{}", dotfiles_dir, dotfile);
let dest_file: String = format!("{}/{}", home_dir, dotfile);
copy_file(dest_file, orig_file, dry_run)
})
.collect()
}
pub fn install(
dotfile_list: Vec<String>,
github_url: String,
ssh_key_file: String,
dry_run: bool,
) -> Result<Vec<u8>, String> {
let home_dir: String = home_path()?;
let git_dotfiles_dir: String = format!("{}/dotfiles", &home_dir);
let git_dotfiles_path: &Path = Path::new(&git_dotfiles_dir);
let _repo = match git_dotfiles_path.exists() {
true => Repository::open(git_dotfiles_path)
.map_err(|_| format!("Folder not exists: {}", git_dotfiles_dir)),
_ => {
let repo: Result<Repository, String> =
git_clone(github_url, &git_dotfiles_dir, ssh_key_file);
info!("Clone complete");
repo
}
}?;
apply(dotfile_list, git_dotfiles_dir, dry_run)
}
fn git_clone(
github_url: String,
git_dotfiles_dir: &String,
ssh_private_key_fn: String,
) -> Result<Repository, String> {
match github_url.starts_with("git@github.com") {
true => {
info!("Cloning {} into {}", github_url, git_dotfiles_dir);
let git_dotfiles_path: &Path = Path::new(&git_dotfiles_dir);
let ssh_pub_key_fn: String = format!("{}.pub", &ssh_private_key_fn);
let ssh_pub_key_file_path: PathBuf = file_to_path(&ssh_pub_key_fn, true)?;
let ssh_private_key_file_path: PathBuf = file_to_path(&ssh_private_key_fn, true)?;
let mut builder: RepoBuilder = RepoBuilder::new();
let mut callbacks: RemoteCallbacks = RemoteCallbacks::new();
let mut fetch_options: FetchOptions = FetchOptions::new();
callbacks.credentials(|_, _, _| {
let credentials: Cred = Cred::ssh_key(
"git",
Some(&ssh_pub_key_file_path),
&ssh_private_key_file_path,
None,
).expect("Credential problem");
Ok(credentials)
});
fetch_options.remote_callbacks(callbacks);
builder.fetch_options(fetch_options);
builder
.clone(&github_url, git_dotfiles_path)
.map_err(|e| e.to_string())
},
_ => Err(String::from("We only support ssh-key cloning, which the github url should start with git@github.com prefix"))
}
}
pub fn read_yaml(yaml_fn: &str) -> Result<Vec<String>, String> {
let f: File = File::open(yaml_fn).map_err(|e| e.to_string())?;
let data: DotFiles = serde_yaml::from_reader(f).map_err(|e| e.to_string())?;
Ok(data.dotfiles)
}