Completely reworked copying and added more error handling
This commit is contained in:
18
src/args/args.rs
Normal file
18
src/args/args.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
use clap::{Arg, Command, ArgAction, ArgMatches};
|
||||||
|
|
||||||
|
|
||||||
|
pub fn parse_args() -> ArgMatches {
|
||||||
|
|
||||||
|
let matches = Command::new("dotfiles")
|
||||||
|
.version("0.1")
|
||||||
|
.author("Ethan Simmons")
|
||||||
|
.about("Manages dotfiles")
|
||||||
|
.arg(Arg::new("from-git")
|
||||||
|
.short('f')
|
||||||
|
.long("from-git")
|
||||||
|
.action(ArgAction::SetTrue)
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
matches
|
||||||
|
}
|
||||||
@@ -1,18 +1 @@
|
|||||||
use clap::{Arg, Command, ArgAction, ArgMatches};
|
pub mod args;
|
||||||
|
|
||||||
|
|
||||||
pub fn parse_args() -> ArgMatches {
|
|
||||||
|
|
||||||
let matches = Command::new("dotfiles")
|
|
||||||
.version("0.1")
|
|
||||||
.author("Ethan Simmons")
|
|
||||||
.about("Manages dotfiles")
|
|
||||||
.arg(Arg::new("from-git")
|
|
||||||
.short('f')
|
|
||||||
.long("from-git")
|
|
||||||
.action(ArgAction::SetTrue)
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
matches
|
|
||||||
}
|
|
||||||
|
|||||||
162
src/config/config.rs
Normal file
162
src/config/config.rs
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use toml::Table;
|
||||||
|
|
||||||
|
use crate::dotfile::dotfile::{self, ManagedDotfile};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub struct Config {
|
||||||
|
pub manager_dir: PathBuf,
|
||||||
|
pub dotfiles: Vec<Result<ManagedDotfile, ConfigParseError>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn parse(path: PathBuf) -> Result<Self, ConfigParseError> {
|
||||||
|
|
||||||
|
let config_file = Config::read_config(path).unwrap();
|
||||||
|
|
||||||
|
let dotfiles = Config::get_dotfiles(&config_file).unwrap();
|
||||||
|
|
||||||
|
let manager_dir = Config::get_manager_dir(&config_file);
|
||||||
|
|
||||||
|
Ok(Config{manager_dir, dotfiles})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn read_config(path: PathBuf) -> Result<Table, ConfigParseError> {
|
||||||
|
|
||||||
|
let file = fs::read(path)?;
|
||||||
|
|
||||||
|
let read_file = String::from_utf8(file)?;
|
||||||
|
|
||||||
|
let config: Table = read_file.parse()?;
|
||||||
|
|
||||||
|
Ok(config)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn get_dotfiles(config: &Table) -> Result<Vec<Result<ManagedDotfile, ConfigParseError>>, ConfigParseError> {
|
||||||
|
|
||||||
|
let read_dotfiles = config.get("dotfiles");
|
||||||
|
|
||||||
|
let dotfiles = match read_dotfiles {
|
||||||
|
Some(dotfiles) => dotfiles,
|
||||||
|
None => return Err(ConfigParseError::DotfilesParseError),
|
||||||
|
};
|
||||||
|
|
||||||
|
let dotfile_iter = match dotfiles.as_array() {
|
||||||
|
Some(dotfiles) => dotfiles.iter(),
|
||||||
|
None => return Err(ConfigParseError::DotfilesArrayParseError),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let dotfiles = dotfile_iter.map(|dotfile| {
|
||||||
|
|
||||||
|
let dotfile_table = dotfile.as_table().unwrap();
|
||||||
|
|
||||||
|
let manager_path = PathBuf::from(
|
||||||
|
match dotfile_table.get("manager_path") {
|
||||||
|
Some(path) => path.as_str().expect("Invalid character in dotfile path"),
|
||||||
|
None => return Err(ConfigParseError::DotfilesTableParseError),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let system_path = PathBuf::from(
|
||||||
|
match dotfile_table.get("system_path") {
|
||||||
|
Some(path) => path.as_str().expect("Invalid character in dotfile path"),
|
||||||
|
None => return Err(ConfigParseError::DotfilesTableParseError),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(ManagedDotfile::new(manager_path, system_path)?)
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(dotfiles.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn get_manager_dir(config: &Table) -> PathBuf {
|
||||||
|
|
||||||
|
let manager_dir = if config.contains_key("manager_directory") {
|
||||||
|
PathBuf::from(config.get("manager_directory").unwrap().as_str().unwrap())
|
||||||
|
} else {
|
||||||
|
PathBuf::from("$HOME/.dotfiles")
|
||||||
|
};
|
||||||
|
|
||||||
|
manager_dir
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ConfigParseError {
|
||||||
|
FileReadError(std::io::Error),
|
||||||
|
FromUtfError(std::string::FromUtf8Error),
|
||||||
|
TomlParseError(toml::de::Error),
|
||||||
|
DotfilesParseError,
|
||||||
|
DotfilesArrayParseError,
|
||||||
|
DotfilesTableParseError,
|
||||||
|
DotfilesCreateError(dotfile::DotfileError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for ConfigParseError {}
|
||||||
|
|
||||||
|
impl fmt::Display for ConfigParseError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ConfigParseError::FileReadError(io_error) => {
|
||||||
|
write!(f, "{}", io_error)
|
||||||
|
},
|
||||||
|
ConfigParseError::FromUtfError(utf_error) => {
|
||||||
|
write!(f, "{}", utf_error)
|
||||||
|
},
|
||||||
|
ConfigParseError::TomlParseError(parse_error) => {
|
||||||
|
write!(f, "{}", parse_error)
|
||||||
|
},
|
||||||
|
ConfigParseError::DotfilesCreateError(create_error) => {
|
||||||
|
write!(f, "{}", create_error)
|
||||||
|
},
|
||||||
|
ConfigParseError::DotfilesParseError => {
|
||||||
|
write!(f, "Dotfiles section not found in config file")
|
||||||
|
},
|
||||||
|
ConfigParseError::DotfilesArrayParseError => {
|
||||||
|
write!(f, "Dotfiles is not a valid array, Hint: use [[dotfiles]]")
|
||||||
|
},
|
||||||
|
ConfigParseError::DotfilesTableParseError => {
|
||||||
|
write!(f, "Dotfile table is not valid")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for ConfigParseError {
|
||||||
|
fn from(error: std::io::Error) -> ConfigParseError {
|
||||||
|
ConfigParseError::FileReadError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::string::FromUtf8Error> for ConfigParseError {
|
||||||
|
fn from(error: std::string::FromUtf8Error) -> ConfigParseError {
|
||||||
|
ConfigParseError::FromUtfError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<toml::de::Error> for ConfigParseError {
|
||||||
|
fn from(error: toml::de::Error) -> ConfigParseError {
|
||||||
|
ConfigParseError::TomlParseError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<dotfile::DotfileError> for ConfigParseError {
|
||||||
|
fn from(error: dotfile::DotfileError) -> ConfigParseError {
|
||||||
|
ConfigParseError::DotfilesCreateError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,162 +1 @@
|
|||||||
use std::fs;
|
pub mod config;
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use toml::Table;
|
|
||||||
|
|
||||||
use crate::dotfile::Dotfile;
|
|
||||||
|
|
||||||
|
|
||||||
pub struct Config {
|
|
||||||
pub manager_dir: PathBuf,
|
|
||||||
pub dotfiles: Vec<Result<Dotfile, ConfigParseError>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl Config {
|
|
||||||
pub fn parse(path: PathBuf) -> Result<Self, ConfigParseError> {
|
|
||||||
|
|
||||||
let config_file = Config::read_config(path).unwrap();
|
|
||||||
|
|
||||||
let dotfiles = Config::get_dotfiles(&config_file).unwrap();
|
|
||||||
|
|
||||||
let manager_dir = Config::get_manager_dir(&config_file);
|
|
||||||
|
|
||||||
Ok(Config{manager_dir, dotfiles})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn read_config(path: PathBuf) -> Result<Table, ConfigParseError> {
|
|
||||||
|
|
||||||
let file = fs::read(path)?;
|
|
||||||
|
|
||||||
let read_file = String::from_utf8(file)?;
|
|
||||||
|
|
||||||
let config: Table = read_file.parse()?;
|
|
||||||
|
|
||||||
Ok(config)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn get_dotfiles(config: &Table) -> Result<Vec<Result<Dotfile, ConfigParseError>>, ConfigParseError> {
|
|
||||||
|
|
||||||
let read_dotfiles = config.get("dotfiles");
|
|
||||||
|
|
||||||
let dotfiles = match read_dotfiles {
|
|
||||||
Some(dotfiles) => dotfiles,
|
|
||||||
None => return Err(ConfigParseError::DotfilesParseError),
|
|
||||||
};
|
|
||||||
|
|
||||||
let dotfile_iter = match dotfiles.as_array() {
|
|
||||||
Some(dotfiles) => dotfiles.iter(),
|
|
||||||
None => return Err(ConfigParseError::DotfilesArrayParseError),
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let dotfiles = dotfile_iter.map(|dotfile| {
|
|
||||||
|
|
||||||
let dotfile_table = dotfile.as_table().unwrap();
|
|
||||||
|
|
||||||
let manager_path = PathBuf::from(
|
|
||||||
match dotfile_table.get("manager_path") {
|
|
||||||
Some(path) => path.as_str().expect("Invalid character in dotfile path"),
|
|
||||||
None => return Err(ConfigParseError::DotfilesTableParseError),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let system_path = PathBuf::from(
|
|
||||||
match dotfile_table.get("system_path") {
|
|
||||||
Some(path) => path.as_str().expect("Invalid character in dotfile path"),
|
|
||||||
None => return Err(ConfigParseError::DotfilesTableParseError),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(Dotfile::new(manager_path, system_path)?)
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(dotfiles.collect())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn get_manager_dir(config: &Table) -> PathBuf {
|
|
||||||
|
|
||||||
let manager_dir = if config.contains_key("manager_directory") {
|
|
||||||
PathBuf::from(config.get("manager_directory").unwrap().as_str().unwrap())
|
|
||||||
} else {
|
|
||||||
PathBuf::from("$HOME/.dotfiles")
|
|
||||||
};
|
|
||||||
|
|
||||||
manager_dir
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ConfigParseError {
|
|
||||||
FileReadError(std::io::Error),
|
|
||||||
FromUtfError(std::string::FromUtf8Error),
|
|
||||||
TomlParseError(toml::de::Error),
|
|
||||||
DotfilesParseError,
|
|
||||||
DotfilesArrayParseError,
|
|
||||||
DotfilesTableParseError,
|
|
||||||
DotfilesCreateError(Box<dyn Error>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for ConfigParseError {}
|
|
||||||
|
|
||||||
impl fmt::Display for ConfigParseError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
ConfigParseError::FileReadError(io_error) => {
|
|
||||||
write!(f, "{}", io_error)
|
|
||||||
},
|
|
||||||
ConfigParseError::FromUtfError(utf_error) => {
|
|
||||||
write!(f, "{}", utf_error)
|
|
||||||
},
|
|
||||||
ConfigParseError::TomlParseError(parse_error) => {
|
|
||||||
write!(f, "{}", parse_error)
|
|
||||||
},
|
|
||||||
ConfigParseError::DotfilesParseError => {
|
|
||||||
write!(f, "Dotfiles section not found in config file")
|
|
||||||
},
|
|
||||||
ConfigParseError::DotfilesArrayParseError => {
|
|
||||||
write!(f, "Dotfiles is not a valid array, Hint: use [[dotfiles]]")
|
|
||||||
},
|
|
||||||
ConfigParseError::DotfilesTableParseError => {
|
|
||||||
write!(f, "Dotfile table is not valid")
|
|
||||||
},
|
|
||||||
ConfigParseError::DotfilesCreateError(create_error) => {
|
|
||||||
write!(f, "Failed to create dotfile {}", *create_error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for ConfigParseError {
|
|
||||||
fn from(error: std::io::Error) -> Self {
|
|
||||||
ConfigParseError::FileReadError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::string::FromUtf8Error> for ConfigParseError {
|
|
||||||
fn from(error: std::string::FromUtf8Error) -> Self {
|
|
||||||
ConfigParseError::FromUtfError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<toml::de::Error> for ConfigParseError {
|
|
||||||
fn from(error: toml::de::Error) -> Self {
|
|
||||||
ConfigParseError::TomlParseError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Box<dyn Error>> for ConfigParseError {
|
|
||||||
fn from(error: Box<dyn Error>) -> Self {
|
|
||||||
ConfigParseError::DotfilesCreateError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
195
src/dotfile/dir.rs
Normal file
195
src/dotfile/dir.rs
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::fmt;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use crate::dotfile::file::{self, File};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub struct Directory {
|
||||||
|
files: Vec<File>,
|
||||||
|
directories: Vec<Directory>,
|
||||||
|
pub path: PathBuf,
|
||||||
|
pub errors: Vec<DirError>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Directory {
|
||||||
|
pub fn new(path: &PathBuf) -> Result<Directory, DirError> {
|
||||||
|
|
||||||
|
if !path.exists() {
|
||||||
|
fs::create_dir_all(path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dir: Vec<_> = fs::read_dir(path)?.collect();
|
||||||
|
|
||||||
|
// Find a better way to do this sometime
|
||||||
|
let mut read_errors: Vec<DirError> = Vec::new();
|
||||||
|
let mut metadata_errors: Vec<DirError> = Vec::new();
|
||||||
|
let mut create_dir_errors: Vec<DirError> = Vec::new();
|
||||||
|
let mut create_file_errors: Vec<DirError> = Vec::new();
|
||||||
|
|
||||||
|
let entries = dir.into_iter().filter_map(|entry| match entry {
|
||||||
|
Ok(entry) => Some(entry),
|
||||||
|
Err(e) => {
|
||||||
|
read_errors.push(DirError::from(e));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
let valid_entries: Vec<_> = entries
|
||||||
|
.filter_map(|entry| match entry.metadata() {
|
||||||
|
Ok(_) => Some(entry),
|
||||||
|
Err(e) => {
|
||||||
|
metadata_errors.push(DirError::from(e));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
|
||||||
|
let directories: Vec<_> = valid_entries
|
||||||
|
.iter()
|
||||||
|
.filter_map(|entry|
|
||||||
|
if entry.metadata().unwrap().is_dir() {
|
||||||
|
match Directory::new(&entry.path()) {
|
||||||
|
Ok(dir) => Some(dir),
|
||||||
|
Err(e) => {
|
||||||
|
create_dir_errors.push(DirError::from(e));
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
|
||||||
|
let files: Vec<File> = valid_entries
|
||||||
|
.iter()
|
||||||
|
.filter_map(|entry|
|
||||||
|
if entry.metadata().unwrap().is_file() {
|
||||||
|
match File::new(&entry.path()) {
|
||||||
|
Ok(file) => Some(file),
|
||||||
|
Err(e) => {
|
||||||
|
create_file_errors.push(DirError::from(e));
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Fix sometime
|
||||||
|
let errors: Vec<DirError> = read_errors.into_iter().chain(metadata_errors.into_iter().chain(create_dir_errors.into_iter().chain(create_file_errors.into_iter()))).collect();
|
||||||
|
|
||||||
|
|
||||||
|
Ok(Directory{ files, directories, path: path.to_path_buf(), errors })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn copy(&self, dest_path: &PathBuf) -> Result<Vec<DirError>, DirError> {
|
||||||
|
|
||||||
|
let file_copy_results: Vec<_> = self.files
|
||||||
|
.iter()
|
||||||
|
.map(|file| {
|
||||||
|
file.copy( &dest_path.join( PathBuf::from(&file.filename) ) )
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
|
||||||
|
let dir_copy_results = {
|
||||||
|
let dirs = self.directories.iter();
|
||||||
|
|
||||||
|
let result = dirs.map(|dir| {
|
||||||
|
let dir_name = match dir.path.file_name() {
|
||||||
|
Some(filename) => filename,
|
||||||
|
None => return Err(DirError::NoDirNameError),
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_dest_path = dest_path.join(PathBuf::from(dir_name));
|
||||||
|
|
||||||
|
if !new_dest_path.exists() {
|
||||||
|
fs::create_dir(&new_dest_path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
dir.copy(&new_dest_path)
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
result
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut copy_errors = Vec::new();
|
||||||
|
|
||||||
|
file_copy_results.into_iter().for_each(|result| if result.is_err() {
|
||||||
|
copy_errors.push(DirError::from(result.err().unwrap()))
|
||||||
|
});
|
||||||
|
|
||||||
|
dir_copy_results.into_iter().for_each(|result| match result {
|
||||||
|
Err(e) => {
|
||||||
|
copy_errors.push(DirError::from(e));
|
||||||
|
},
|
||||||
|
Ok(copy_results) => {
|
||||||
|
copy_results.into_iter().for_each(|error| copy_errors.push(error));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Ok(copy_errors)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum DirError {
|
||||||
|
DirCopyMetadataError(std::env::VarError),
|
||||||
|
DirIOError(std::io::Error),
|
||||||
|
DirFileCopyError(file::FileError),
|
||||||
|
NoDirNameError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for DirError {}
|
||||||
|
|
||||||
|
impl fmt::Display for DirError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
DirError::DirCopyMetadataError(var_error) => {
|
||||||
|
write!(f, "{}", var_error)
|
||||||
|
},
|
||||||
|
DirError::DirIOError(io_error) => {
|
||||||
|
write!(f, "{}", io_error)
|
||||||
|
},
|
||||||
|
DirError::DirFileCopyError(copy_error) => {
|
||||||
|
write!(f, "{}", copy_error)
|
||||||
|
},
|
||||||
|
DirError::NoDirNameError => {
|
||||||
|
write!(f, "Directory does not have a valid name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::env::VarError> for DirError {
|
||||||
|
fn from(error: std::env::VarError) -> DirError {
|
||||||
|
DirError::DirCopyMetadataError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for DirError {
|
||||||
|
fn from(error: std::io::Error) -> DirError {
|
||||||
|
DirError::DirIOError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<file::FileError> for DirError {
|
||||||
|
fn from(error: file::FileError) -> DirError {
|
||||||
|
DirError::DirFileCopyError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
170
src/dotfile/dotfile.rs
Normal file
170
src/dotfile/dotfile.rs
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::env;
|
||||||
|
use std::fs;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use crate::dotfile::dir;
|
||||||
|
use crate::dotfile::file;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub enum Dotfile {
|
||||||
|
File(file::File),
|
||||||
|
Dir(dir::Directory)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct ManagedDotfile {
|
||||||
|
pub manager_dotfile: Dotfile,
|
||||||
|
pub system_dotfile: Dotfile,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ManagedDotfile {
|
||||||
|
pub fn new(rel_git_location: PathBuf, sys_location: PathBuf) -> Result<Self, DotfileError> {
|
||||||
|
|
||||||
|
let home_dir = PathBuf::from(env::var("HOME")?);
|
||||||
|
let manager_dir = home_dir.join(PathBuf::from(".dotfiles/"));
|
||||||
|
|
||||||
|
let manager_path = manager_dir.join(rel_git_location);
|
||||||
|
let system_path = sys_location;
|
||||||
|
|
||||||
|
let manager_path_data = fs::metadata(&manager_path);
|
||||||
|
let sys_path_data = fs::metadata(&system_path);
|
||||||
|
|
||||||
|
let is_dir = match (manager_path_data, sys_path_data) {
|
||||||
|
(Ok(manager_data), Ok(sys_data)) => manager_data.is_dir() && sys_data.is_dir(),
|
||||||
|
|
||||||
|
(Ok(manager_data), Err(_)) => {
|
||||||
|
if manager_data.is_dir() {
|
||||||
|
let _ = fs::create_dir_all(&system_path);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
let _ = fs::create_dir_all(&system_path.parent().unwrap());
|
||||||
|
false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
(Err(_), Ok(sys_data)) => {
|
||||||
|
if sys_data.is_dir() {
|
||||||
|
let _ = fs::create_dir_all(&manager_path);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
let _ = fs::create_dir_all(&manager_path.parent().unwrap());
|
||||||
|
false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
(Err(e1), Err(e2)) => return Err(DotfileError::FilesDontExistError((e1, e2)))
|
||||||
|
};
|
||||||
|
|
||||||
|
let manager_dotfile = if is_dir {
|
||||||
|
Dotfile::Dir(dir::Directory::new(&manager_path)?)
|
||||||
|
} else {
|
||||||
|
Dotfile::File(file::File::new(&manager_path)?)
|
||||||
|
};
|
||||||
|
|
||||||
|
let system_dotfile = if is_dir {
|
||||||
|
Dotfile::Dir(dir::Directory::new(&system_path)?)
|
||||||
|
} else {
|
||||||
|
Dotfile::File(file::File::new(&system_path)?)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self { manager_dotfile, system_dotfile })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn copy_dotfile(&self, to_sys: bool) -> Result<(), DotfileError> {
|
||||||
|
|
||||||
|
let (current, destination) = if to_sys {
|
||||||
|
(&self.manager_dotfile, &self.system_dotfile)
|
||||||
|
} else {
|
||||||
|
(&self.system_dotfile, &self.manager_dotfile)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if let (Dotfile::File(current_file), Dotfile::File(dest_file)) = (current, destination) {
|
||||||
|
current_file.copy(&dest_file.path)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if let (Dotfile::Dir(current_dir), Dotfile::Dir(dest_dir)) = (current, destination) {
|
||||||
|
let results = current_dir.copy(&dest_dir.path)?;
|
||||||
|
|
||||||
|
results.into_iter().for_each(|result| {
|
||||||
|
println!("Error copying directory: {}", result)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum DotfileError {
|
||||||
|
DotfileIOError(std::io::Error),
|
||||||
|
DotfileEnvError(std::env::VarError),
|
||||||
|
FilesDontExistError((std::io::Error, std::io::Error)),
|
||||||
|
FileCopyError(file::FileError),
|
||||||
|
DirectoryCopyError(dir::DirError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for DotfileError {}
|
||||||
|
|
||||||
|
impl fmt::Display for DotfileError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
DotfileError::DotfileIOError(io_error) => {
|
||||||
|
write!(f, "{}", io_error)
|
||||||
|
},
|
||||||
|
DotfileError::DotfileEnvError(env_error) => {
|
||||||
|
write!(f, "{}", env_error)
|
||||||
|
}
|
||||||
|
DotfileError::FilesDontExistError((io_error_1, io_error_2)) => {
|
||||||
|
write!(f, "Neither file exists: {}, {}", io_error_1, io_error_2)
|
||||||
|
}
|
||||||
|
DotfileError::FileCopyError(copy_error) => {
|
||||||
|
write!(f, "{}", copy_error)
|
||||||
|
}
|
||||||
|
DotfileError::DirectoryCopyError(copy_error) => {
|
||||||
|
write!(f, "{}", copy_error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for DotfileError {
|
||||||
|
fn from(error: std::io::Error) -> DotfileError {
|
||||||
|
DotfileError::DotfileIOError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::env::VarError> for DotfileError {
|
||||||
|
fn from(error: std::env::VarError) -> DotfileError {
|
||||||
|
DotfileError::DotfileEnvError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(std::io::Error, std::io::Error)> for DotfileError {
|
||||||
|
fn from(error: (std::io::Error, std::io::Error)) -> DotfileError {
|
||||||
|
DotfileError::FilesDontExistError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<file::FileError> for DotfileError {
|
||||||
|
fn from(error: file::FileError) -> DotfileError {
|
||||||
|
DotfileError::FileCopyError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<dir::DirError> for DotfileError {
|
||||||
|
fn from(error: dir::DirError) -> DotfileError {
|
||||||
|
DotfileError::DirectoryCopyError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
71
src/dotfile/file.rs
Normal file
71
src/dotfile/file.rs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
use std::fs;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub struct File {
|
||||||
|
pub path: PathBuf,
|
||||||
|
pub filename: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl File {
|
||||||
|
pub fn new(path: &PathBuf) -> Result<File, FileError> {
|
||||||
|
|
||||||
|
let filename = match path.file_name() {
|
||||||
|
Some(filename) => match filename.to_str() {
|
||||||
|
Some(filename) => String::from(filename),
|
||||||
|
None => return Err(FileError::InvalidUTFError),
|
||||||
|
},
|
||||||
|
None => return Err(FileError::NoFileNameError),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(File{ path: path.to_path_buf(), filename })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn copy(&self, dest_path: &PathBuf) -> Result<(), FileError> {
|
||||||
|
|
||||||
|
fs::copy(&self.path, dest_path)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum FileError {
|
||||||
|
CopyError(std::io::Error),
|
||||||
|
NoFileNameError,
|
||||||
|
InvalidUTFError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for FileError {}
|
||||||
|
|
||||||
|
impl fmt::Display for FileError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
FileError::CopyError(copy_error) => {
|
||||||
|
write!(f, "{}", copy_error)
|
||||||
|
},
|
||||||
|
FileError::InvalidUTFError => {
|
||||||
|
write!(f, "Invalild UTF in filename")
|
||||||
|
},
|
||||||
|
FileError::NoFileNameError => {
|
||||||
|
write!(f, "File does not have a valid filename")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for FileError {
|
||||||
|
fn from(error: std::io::Error) -> FileError {
|
||||||
|
FileError::CopyError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,71 +1,4 @@
|
|||||||
use std::path::PathBuf;
|
mod dir;
|
||||||
use std::error::Error;
|
mod file;
|
||||||
use std::env;
|
|
||||||
use std::fs;
|
|
||||||
|
|
||||||
use crate::copy_directory;
|
pub mod dotfile;
|
||||||
|
|
||||||
pub struct Dotfile {
|
|
||||||
pub manager_path: PathBuf,
|
|
||||||
pub system_path: PathBuf,
|
|
||||||
is_dir: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl Dotfile {
|
|
||||||
pub fn new(rel_git_location: PathBuf, sys_location: PathBuf) -> Result<Self, Box<dyn Error>> {
|
|
||||||
|
|
||||||
let home_dir = PathBuf::from(env::var("HOME").expect("$HOME not set"));
|
|
||||||
let manager_dir = home_dir.join(PathBuf::from(".dotfiles/"));
|
|
||||||
|
|
||||||
let manager_path = manager_dir.join(rel_git_location);
|
|
||||||
let system_path = sys_location;
|
|
||||||
|
|
||||||
let manager_path_data = fs::metadata(&manager_path);
|
|
||||||
let sys_path_data = fs::metadata(&system_path);
|
|
||||||
|
|
||||||
let is_dir = match (manager_path_data, sys_path_data) {
|
|
||||||
(Ok(manager_data), Ok(sys_data)) => manager_data.is_dir() && sys_data.is_dir(),
|
|
||||||
(Ok(manager_data), Err(_)) => {
|
|
||||||
if manager_data.is_dir() {
|
|
||||||
let _ = fs::create_dir_all(&system_path);
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
let _ = fs::create_dir_all(&system_path.parent().unwrap());
|
|
||||||
false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(Err(_), Ok(sys_data)) => {
|
|
||||||
if sys_data.is_dir() {
|
|
||||||
let _ = fs::create_dir_all(&manager_path);
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
let _ = fs::create_dir_all(&manager_path.parent().unwrap());
|
|
||||||
false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(Err(e1), Err(e2)) => panic!("Neither {} nor {} exists or is readable: {}, {}", manager_path.to_str().unwrap(), system_path.to_str().unwrap(), e1, e2),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Self { manager_path, system_path, is_dir })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy_dotfile(&self, to_sys: bool) -> Result<(), Box<dyn Error>> {
|
|
||||||
|
|
||||||
let (curr, dest) = if to_sys {
|
|
||||||
(&self.manager_path, &self.system_path)
|
|
||||||
} else {
|
|
||||||
(&self.system_path, &self.manager_path)
|
|
||||||
};
|
|
||||||
|
|
||||||
if !self.is_dir {
|
|
||||||
println!("Copying file");
|
|
||||||
fs::copy(curr, dest)?;
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
println!("Starting Copy Dir");
|
|
||||||
copy_directory(curr, dest)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
54
src/lib.rs
54
src/lib.rs
@@ -1,51 +1,12 @@
|
|||||||
use std::fs;
|
|
||||||
use std::path::{self, PathBuf};
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
|
||||||
|
use crate::config::config::Config;
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod dotfile;
|
pub mod dotfile;
|
||||||
pub mod args;
|
pub mod args;
|
||||||
|
|
||||||
use config::Config;
|
|
||||||
|
|
||||||
fn copy_directory(dir_path: &PathBuf, dest_path: &PathBuf) -> Result<(), Box<dyn Error>> {
|
|
||||||
|
|
||||||
let dir = fs::read_dir(&dir_path)?;
|
|
||||||
|
|
||||||
let entries: Vec<_> = dir.map(|entry| entry.unwrap()).collect();
|
|
||||||
|
|
||||||
let files = entries.iter().filter(|entry| entry.metadata().unwrap().is_file());
|
|
||||||
let dirs = entries.iter().filter(|entry| entry.metadata().unwrap().is_dir());
|
|
||||||
|
|
||||||
files.for_each(|file| {
|
|
||||||
let file_path = dir_path.join(file.file_name());
|
|
||||||
let dest_path = dest_path.join(file.file_name());
|
|
||||||
|
|
||||||
let _ = fs::copy(file_path, dest_path);
|
|
||||||
|
|
||||||
println!("Copying file");
|
|
||||||
});
|
|
||||||
|
|
||||||
dirs.for_each(|dir| {
|
|
||||||
let current_dir_path = dir_path.join(dir.file_name());
|
|
||||||
|
|
||||||
let dest_path = dest_path.join(dir.file_name());
|
|
||||||
|
|
||||||
if !(path::Path::try_exists(&dest_path).unwrap()) {
|
|
||||||
let _ = fs::create_dir(&dest_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
println!("Copying dir");
|
|
||||||
let _ = copy_directory(¤t_dir_path, &dest_path);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn run(args: ArgMatches, config: Config) -> Result<(), Box<dyn Error>> {
|
pub fn run(args: ArgMatches, config: Config) -> Result<(), Box<dyn Error>> {
|
||||||
|
|
||||||
@@ -64,14 +25,13 @@ pub fn run(args: ArgMatches, config: Config) -> Result<(), Box<dyn Error>> {
|
|||||||
let copy_results = valid_dotfiles.iter().map(|dotfile| (dotfile.copy_dotfile(copy_to_sys), dotfile));
|
let copy_results = valid_dotfiles.iter().map(|dotfile| (dotfile.copy_dotfile(copy_to_sys), dotfile));
|
||||||
|
|
||||||
copy_results.for_each(|result| {
|
copy_results.for_each(|result| {
|
||||||
if let Err(e) = result.0 {
|
match result.0 {
|
||||||
let failed_dotfile = result.1;
|
Err(e) => println!("Failed to copy dotfile: {}", e),
|
||||||
|
_ => (),
|
||||||
if copy_to_sys {
|
|
||||||
println!("Faled to copy {}, with error: {}", failed_dotfile.manager_path.to_str().expect("Error printing error"), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use dotfiles_manager::args;
|
use dotfiles_manager::args::args;
|
||||||
use dotfiles_manager::config::Config;
|
use dotfiles_manager::config::config::Config;
|
||||||
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
|||||||
Reference in New Issue
Block a user