rewrite with diff

master
Rasmus Rosengren 3 years ago
parent 9e267dc6f5
commit 41ac587c34
Signed by: rsrp
GPG Key ID: A13BC7BC4F81CF5F
  1. 287
      Cargo.lock
  2. 12
      Cargo.toml
  3. 1
      rustfmt.toml
  4. 79
      src/build.rs
  5. 36
      src/check.rs
  6. 10
      src/config.rs
  7. 53
      src/diff.rs
  8. 44
      src/install.rs
  9. 52
      src/link.rs
  10. 103
      src/main.rs
  11. 22
      src/opts.rs
  12. 44
      src/tree.rs
  13. 68
      src/utils.rs

287
Cargo.lock generated

@ -2,15 +2,6 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi",
]
[[package]]
name = "async-recursion"
version = "0.3.2"
@ -45,19 +36,47 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bytes"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.33.3"
version = "3.0.0-beta.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
checksum = "feff3878564edb93745d58cf63e17b63f24142506e7a20c87a5521ed7bfb1d63"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"clap_derive",
"indexmap",
"lazy_static",
"os_str_bytes",
"strsim",
"termcolor",
"textwrap",
"unicode-width",
"vec_map",
"unicase",
]
[[package]]
name = "clap_derive"
version = "3.0.0-beta.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b15c6b4f786ffb6192ffe65a36855bc1fc2444bcd0945ae16748dcd6ed7d0d3"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
@ -66,11 +85,33 @@ version = "0.1.0"
dependencies = [
"async-recursion",
"clap",
"directories",
"futures",
"terminal_size",
"tokio",
"xdg",
]
[[package]]
name = "directories"
version = "4.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "futures"
version = "0.3.16"
@ -165,6 +206,32 @@ dependencies = [
"slab",
]
[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
@ -174,18 +241,74 @@ dependencies = [
"libc",
]
[[package]]
name = "indexmap"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "mio"
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
dependencies = [
"libc",
"log",
"miow",
"ntapi",
"winapi",
]
[[package]]
name = "miow"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
dependencies = [
"winapi",
]
[[package]]
name = "ntapi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
dependencies = [
"winapi",
]
[[package]]
name = "num_cpus"
version = "1.13.0"
@ -196,6 +319,21 @@ dependencies = [
"libc",
]
[[package]]
name = "once_cell"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
[[package]]
name = "os_str_bytes"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "addaa943333a514159c80c97ff4a93306530d965d27e139188283cd13e06a799"
dependencies = [
"memchr",
]
[[package]]
name = "pin-project-lite"
version = "0.2.7"
@ -208,6 +346,30 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
@ -238,6 +400,34 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_users"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [
"getrandom",
"redox_syscall",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.4"
@ -246,9 +436,9 @@ checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590"
[[package]]
name = "strsim"
version = "0.8.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
@ -261,11 +451,30 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "terminal_size"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "textwrap"
version = "0.11.0"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
dependencies = [
"unicode-width",
]
@ -277,9 +486,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92036be488bb6594459f2e03b60e42df6f937fe6ca5c5ffdcb539c6b84dc40f5"
dependencies = [
"autocfg",
"bytes",
"libc",
"mio",
"num_cpus",
"once_cell",
"pin-project-lite",
"signal-hook-registry",
"tokio-macros",
"winapi",
]
[[package]]
@ -293,6 +508,21 @@ dependencies = [
"syn",
]
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-segmentation"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-width"
version = "0.1.8"
@ -306,10 +536,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "vec_map"
version = "0.8.2"
name = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "winapi"
@ -327,6 +563,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"

@ -1,16 +1,14 @@
[package]
name = "dfm"
version = "0.1.0"
edition = "2018"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.release]
lto = true
codegen-units = 1
[dependencies]
async-recursion = "0.3"
clap = "2.33"
clap = "3.0.0-beta.5"
directories = "4.0"
futures = "0.3"
tokio = { version = "1.10", features = ["fs", "macros", "rt-multi-thread"] }
terminal_size = "0.1"
tokio = { version = "1.10", features = ["fs", "macros", "process", "rt-multi-thread"] }
xdg = "2.2"

@ -0,0 +1 @@
hard_tabs = true

@ -1,71 +1,30 @@
use std::path::PathBuf;
use async_recursion::async_recursion;
use futures::future::join_all;
use tokio::fs::{copy, create_dir, read_dir};
use crate::{
config::Config,
utils::{get_tree_files, remove_dir_if_empty},
};
use crate::Context;
pub async fn build(config: &Config) -> std::io::Result<()> {
let source_files = get_tree_files(config, &config.source_dir).await?;
let build_files = get_tree_files(config, &config.build_dir).await?;
pub async fn build_tree(context: &Context) -> std::io::Result<()> {
dir(context, PathBuf::new()).await
for file_path in source_files.iter() {
if let Some(folder_path) = file_path.parent() {
let dir = config.build_dir.join(folder_path);
tokio::fs::create_dir_all(dir).await?;
}
#[async_recursion]
async fn dir(context: &Context, relative_path: PathBuf) -> std::io::Result<()> {
let source_path = context.tree_source_dir.join(&relative_path);
let build_path = context.tree_build_dir.join(&relative_path);
match create_dir(build_path).await {
Err(e) if e.kind() != std::io::ErrorKind::AlreadyExists => {
return Err(e);
let from = config.source_dir.join(file_path);
let to = config.build_dir.join(file_path);
tokio::fs::copy(from, to).await?;
}
_ => (),
}
let mut dir_walker = read_dir(source_path).await?;
let mut dir_tasks = vec![];
let mut file_tasks = vec![];
while let Some(entry) = dir_walker.next_entry().await? {
let metadata = entry.metadata().await?;
let os_name = entry.file_name();
let name = os_name.to_str();
if name == Some(".git") {
continue;
}
let relative_path = relative_path.join(entry.file_name());
if metadata.is_dir() {
dir_tasks.push(dir(context, relative_path));
} else if metadata.is_file() {
file_tasks.push(file(context, relative_path));
for file_path in build_files {
if !source_files.contains(&file_path) {
let file = config.build_dir.join(file_path);
tokio::fs::remove_file(file.clone()).await?;
remove_dir_if_empty(file.parent().unwrap()).await?;
}
}
let dir_tasks = async {
join_all(dir_tasks)
.await
.into_iter()
.collect::<std::io::Result<Vec<_>>>()
};
let file_tasks = async {
join_all(file_tasks)
.await
.into_iter()
.collect::<std::io::Result<Vec<_>>>()
};
tokio::try_join!(dir_tasks, file_tasks)?;
Ok(())
}
async fn file(context: &Context, relative_path: PathBuf) -> std::io::Result<()> {
let source_path = context.tree_source_dir.join(&relative_path);
let build_path = context.tree_build_dir.join(&relative_path);
copy(source_path, build_path).await?;
Ok(())
}

@ -1,36 +0,0 @@
use std::path::PathBuf;
use tokio::fs::read;
use crate::{tree::get_tree_files, Context};
pub async fn check_tree(
context: &Context,
) -> std::io::Result<(Vec<PathBuf>, Vec<PathBuf>, Vec<PathBuf>)> {
let source_files = get_tree_files(&context.tree_source_dir).await?;
let installed_files = get_tree_files(&context.tree_install_dir).await?;
let mut deleted_files = vec![];
let mut changed_files = vec![];
for installed_file in installed_files.iter() {
if source_files.contains(installed_file) {
let source_file_content = read(context.tree_build_dir.join(installed_file)).await?;
let installed_file_content =
read(context.tree_install_dir.join(installed_file)).await?;
if source_file_content != installed_file_content {
changed_files.push(installed_file.clone());
}
} else {
deleted_files.push(installed_file.clone());
}
}
let mut added_files = vec![];
for source_file in source_files.iter() {
if !installed_files.contains(source_file) {
added_files.push(source_file.clone());
}
}
Ok((added_files, changed_files, deleted_files))
}

@ -0,0 +1,10 @@
use std::path::PathBuf;
#[derive(Debug)]
pub struct Config {
pub source_dir: PathBuf,
pub build_dir: PathBuf,
pub install_dir: PathBuf,
pub link_dir: PathBuf,
pub ignored_dirs: Vec<String>,
}

@ -0,0 +1,53 @@
use std::{io::Write, path::PathBuf};
use terminal_size::{terminal_size, Width};
use crate::{config::Config, utils::get_tree_files};
pub async fn diff(config: &Config, diff_command: String) -> std::io::Result<()> {
let built_files = get_tree_files(config, &config.build_dir).await?;
let installed_files = get_tree_files(config, &config.install_dir).await?;
let mut all_files = built_files.clone();
all_files.extend(installed_files.clone());
let mut all_files: Vec<_> = all_files.iter().collect();
all_files.sort();
let (Width(terminal_width), _) = terminal_size().expect("Could not get terminal size");
for file in all_files {
let is_added = built_files.get(file).is_some() && installed_files.get(file).is_none();
let is_removed = built_files.get(file).is_none() && installed_files.get(file).is_some();
let (workdir, first_path, second_path) = if is_added {
(
config.build_dir.clone(),
file.clone(),
PathBuf::from("/dev/null"),
)
} else if is_removed {
(
config.install_dir.clone(),
PathBuf::from("/dev/null"),
file.clone(),
)
} else {
(
config.build_dir.clone(),
file.clone(),
config.install_dir.join(file),
)
};
let output = tokio::process::Command::new(&diff_command)
.current_dir(workdir)
.arg(second_path)
.arg(first_path)
.arg("-w")
.arg(terminal_width.to_string())
.output()
.await?;
std::io::stdout().write_all(&output.stdout)?;
}
Ok(())
}

@ -0,0 +1,44 @@
use crate::{
config::Config,
utils::{get_tree_files, remove_dir_if_empty},
};
pub async fn install(config: &Config) -> std::io::Result<()> {
let built_files = get_tree_files(config, &config.build_dir).await?;
let installed_files = get_tree_files(config, &config.install_dir).await?;
for file_path in built_files.iter() {
if let Some(folder_path) = file_path.parent() {
let dir = config.build_dir.join(folder_path);
tokio::fs::create_dir_all(dir).await?;
}
let from = config.source_dir.join(file_path);
let to = config.build_dir.join(file_path);
tokio::fs::copy(from, to).await?;
if let Some(folder_path) = file_path.parent() {
let dir = config.link_dir.join(folder_path);
tokio::fs::create_dir_all(dir).await?;
}
tokio::fs::symlink(
config.install_dir.join(&file_path),
config.link_dir.join(&file_path),
)
.await?;
}
for file_path in installed_files {
if !built_files.contains(&file_path) {
let installed_file = config.install_dir.join(&file_path);
let linked_file = config.link_dir.join(&file_path);
tokio::fs::remove_file(linked_file.clone()).await?;
remove_dir_if_empty(linked_file.parent().unwrap()).await?;
tokio::fs::remove_file(installed_file.clone()).await?;
remove_dir_if_empty(installed_file.parent().unwrap()).await?;
}
}
Ok(())
}

@ -1,52 +0,0 @@
use std::path::PathBuf;
use tokio::fs::{copy, create_dir_all, remove_file, symlink};
use crate::{check::check_tree, Context};
pub async fn link_tree(context: &Context) -> std::io::Result<()> {
let (add, update, remove) = check_tree(context).await?;
for file in add {
copy_and_link(context, file).await?;
}
for file in update {
copy_and_link(context, file).await?;
}
for file in remove {
remove_file(context.link_root_dir.join(file)).await?;
}
Ok(())
}
async fn copy_and_link(context: &Context, relative_path: PathBuf) -> std::io::Result<()> {
// Create all necessary directories that are accessed
let mut current_install_dir = context.tree_install_dir.join(&relative_path);
current_install_dir.pop();
let mut current_link_dir = context.link_root_dir.join(&relative_path);
current_link_dir.pop();
create_dir_all(current_install_dir).await?;
create_dir_all(current_link_dir).await?;
// Copy from build to install
copy(
context.tree_build_dir.join(&relative_path),
context.tree_install_dir.join(&relative_path),
)
.await?;
// Make sure symlink doesn't exist before attempting to symlink
match remove_file(context.link_root_dir.join(&relative_path)).await {
Ok(_) => {}
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
Err(e) => return Err(e),
};
symlink(
context.tree_install_dir.join(&relative_path),
context.link_root_dir.join(&relative_path),
)
.await?;
Ok(())
}

@ -1,88 +1,51 @@
mod build;
mod check;
mod link;
mod tree;
mod config;
mod diff;
mod install;
mod opts;
mod utils;
use std::path::PathBuf;
use build::build_tree;
use check::check_tree;
use link::link_tree;
use clap::Parser;
use directories::{ProjectDirs, UserDirs};
const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
use crate::{
build::build, config::Config, diff::diff, install::install, opts::Opts,
utils::remove_dir_if_empty,
};
#[tokio::main]
async fn main() -> std::io::Result<()> {
let app = clap::App::new("DotFiles Manager")
.version(APP_VERSION)
.author("Rasmus Rosengren <rasmus.rosengren@protonmail.com>")
.about("Utility to manage dotfiles")
.arg(
clap::Arg::with_name("dry-run")
.long("dry-run")
.short("d")
.help("Report changes without installing"),
)
.arg(
clap::Arg::with_name("repo-path")
.long("repo-path")
.short("r")
.default_value(".df")
.help("Location of repo, relative to $HOME"),
);
let matches = app.get_matches();
let opts = Opts::parse();
let home_dir = std::env::var("HOME").expect("$HOME variable not found");
let xdg_dirs = xdg::BaseDirectories::with_prefix("dfm").expect("xdg dirs");
let user_dirs = UserDirs::new().expect("Could not find user directories");
let project_dirs =
ProjectDirs::from("se", "rsrp", "dfm").expect("Could not find project directories");
let context = Context {
tree_source_dir: format!("{}/{}", home_dir, matches.value_of("repo-path").unwrap()).into(),
tree_build_dir: xdg_dirs
.create_cache_directory("tree")
.expect("xdg cache dir"),
tree_install_dir: xdg_dirs
.create_config_directory("tree")
.expect("xdg config dir"),
link_root_dir: home_dir.into(),
let repo_path = if opts.repo_path.is_relative() {
let mut repo_path = PathBuf::from(user_dirs.home_dir());
repo_path.push(opts.repo_path);
repo_path
} else {
opts.repo_path
};
build_tree(&context).await?;
if matches.is_present("dry-run") {
let (add, change, delete) = check_tree(&context).await?;
if !add.is_empty() {
println!("The following files have been added:");
for file in add {
println!("\t{}", file.to_string_lossy());
}
println!();
}
if !change.is_empty() {
println!("The following files have been changed:");
for file in change {
println!("\t{}", file.to_string_lossy());
}
println!();
}
let config = Config {
source_dir: repo_path,
build_dir: project_dirs.cache_dir().to_path_buf().join("tree"),
install_dir: project_dirs.config_dir().to_path_buf().join("tree"),
link_dir: user_dirs.home_dir().to_path_buf(),
ignored_dirs: vec![".git".to_string()],
};
if !delete.is_empty() {
println!("The following files have been deleted:");
for file in delete {
println!("\t{}", file.to_string_lossy());
}
println!();
}
remove_dir_if_empty(&PathBuf::from("/home/rosen/test")).await?;
build(&config).await?;
if opts.install {
install(&config).await?;
} else {
link_tree(&context).await?;
diff(&config, opts.diff_command).await?;
}
Ok(())
}
pub struct Context {
tree_source_dir: PathBuf,
tree_build_dir: PathBuf,
tree_install_dir: PathBuf,
link_root_dir: PathBuf,
}

@ -0,0 +1,22 @@
use std::path::PathBuf;
use clap::Parser;
use crate::utils::APP_VERSION;
#[derive(Debug, Parser)]
#[clap(
version = APP_VERSION,
author = "Rasmus Rosengren <rasmus.rosengren@protonmail.com>",
about = "Utility to manage dotfiles"
)]
pub struct Opts {
#[clap(short, long)]
pub install: bool,
#[clap(short, long, default_value = ".df")]
pub repo_path: PathBuf,
#[clap(short, long, default_value = "delta")]
pub diff_command: String,
}

@ -1,44 +0,0 @@
use std::{
collections::HashSet,
path::{Path, PathBuf},
};
use async_recursion::async_recursion;
use futures::future::try_join_all;
use tokio::fs::read_dir;
pub async fn get_tree_files(tree_root_path: &Path) -> std::io::Result<HashSet<PathBuf>> {
dir(tree_root_path, PathBuf::new()).await
}
#[async_recursion]
async fn dir(tree_root_path: &Path, relative_path: PathBuf) -> std::io::Result<HashSet<PathBuf>> {
let current_path = tree_root_path.join(&relative_path);
let mut dir_walker = read_dir(&current_path).await?;
let mut dir_tasks = vec![];
let mut files = HashSet::new();
while let Some(entry) = dir_walker.next_entry().await? {
let metadata = entry.metadata().await?;
let os_name = entry.file_name();
let name = os_name.to_string_lossy().to_string();
if name == ".git" {
continue;
}
if metadata.is_dir() {
dir_tasks.push(dir(tree_root_path, relative_path.join(name)));
} else if metadata.is_file() {
files.insert(relative_path.join(name));
}
}
let file_sets = try_join_all(dir_tasks).await?;
for file_set in file_sets {
files.extend(file_set);
}
Ok(files)
}

@ -0,0 +1,68 @@
use std::{
collections::HashSet,
path::{Path, PathBuf},
};
use futures::future::try_join_all;
use tokio::fs::read_dir;
use crate::config::Config;
pub const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
pub async fn get_tree_files(
config: &Config,
tree_root: &Path,
) -> std::io::Result<HashSet<PathBuf>> {
get_tree_files_recursively(config, tree_root, PathBuf::new()).await
}
#[async_recursion::async_recursion]
async fn get_tree_files_recursively(
config: &Config,
tree_root: &Path,
relative_path: PathBuf,
) -> std::io::Result<HashSet<PathBuf>> {
let current_path = tree_root.join(&relative_path);
let mut dir_walker = read_dir(&current_path).await?;
let mut dir_tasks = vec![];
let mut files = HashSet::new();
while let Some(entry) = dir_walker.next_entry().await? {
let metadata = entry.metadata().await?;
let os_name = entry.file_name();
let name = os_name.to_string_lossy().to_string();
if config.ignored_dirs.contains(&name) {
continue;
}
if metadata.is_dir() {
dir_tasks.push(get_tree_files_recursively(
config,
tree_root,
relative_path.join(name),
));
} else if metadata.is_file() {
files.insert(relative_path.join(name));
}
}
let file_sets = try_join_all(dir_tasks).await?;
for file_set in file_sets {
files.extend(file_set);
}
Ok(files)
}
pub async fn remove_dir_if_empty(path: &Path) -> std::io::Result<()> {
let mut dir_walker = tokio::fs::read_dir(path).await?;
if dir_walker.next_entry().await?.is_none() {
tokio::fs::remove_dir(path).await?;
}
Ok(())
}
Loading…
Cancel
Save