Copy even if locally our file doesn't exist.

pull/3/head
Joshua Potter 2022-01-08 16:55:46 -05:00
parent 4bff745573
commit a43561832f
4 changed files with 89 additions and 52 deletions

View File

@ -1,5 +1,5 @@
use super::{config::PathConfig, path, path::ResPathBuf}; use super::{config::PathConfig, path, path::ResPathBuf};
use simplelog::{info, paris}; use simplelog::{info, paris, warn};
use std::{ use std::{
collections::HashMap, collections::HashMap,
env::VarError, env::VarError,
@ -58,15 +58,32 @@ impl error::Error for Error {}
fn apply_all(pc: &PathConfig) -> Result<()> { fn apply_all(pc: &PathConfig) -> Result<()> {
let workdir = path::resolve(&pc.config.repos.local)?; let workdir = path::resolve(&pc.config.repos.local)?;
let repo_lookup = get_repo_lookup(workdir.as_ref(), workdir.as_ref())?; let repo_files = walk_repo(workdir.as_ref())?;
let package_lookup = get_package_lookup(pc); let package_lookup = get_package_lookup(pc);
for (repo_unresolved, repo_resolved) in &repo_lookup { for repo_file in &repo_files {
if let Some(package_resolved) = package_lookup.get(repo_unresolved) { let path = match package_lookup.get(repo_file.unresolved()) {
fs::copy(repo_resolved, package_resolved)?; Some(value) => value,
None => continue,
};
if let Some(value) = path {
fs::copy(repo_file.resolved(), value.resolved())?;
info!( info!(
"Copied `{}` from local repository.", "Copied `{}` from local repository.",
repo_unresolved.display(), repo_file.unresolved().display(),
);
} else {
let expanded = match path::expand(repo_file.unresolved()) {
Ok(expanded) => expanded,
Err(_) => continue,
};
if let Some(p) = expanded.parent() {
fs::create_dir_all(p)?;
}
fs::copy(repo_file.resolved(), expanded)?;
info!(
"Copied `{}` from local repository.",
repo_file.unresolved().display(),
); );
} }
} }
@ -74,25 +91,36 @@ fn apply_all(pc: &PathConfig) -> Result<()> {
Ok(()) Ok(())
} }
fn apply_one(pc: &PathConfig, target: &Path) -> Result<()> { fn apply_one(pc: &PathConfig, package: &str) -> Result<()> {
let workdir = path::resolve(&pc.config.repos.local)?; let workdir = path::resolve(&pc.config.repos.local)?;
let repo_lookup = get_repo_lookup(workdir.as_ref(), workdir.as_ref())?;
let package_lookup = get_package_lookup(pc);
// The user must specify a path that matches the unresolved one. if let Some(paths) = pc.config.packages.get(package) {
if let Some(repo_resolved) = repo_lookup.get(target) { for path in paths {
if let Some(package_resolved) = package_lookup.get(target) { let mut repo_file = workdir.resolved().to_path_buf();
fs::copy(repo_resolved, package_resolved)?; repo_file.push(path);
info!("Copied `{}` from local repository.", target.display(),); if !repo_file.exists() {
continue;
} }
let expanded = match path::expand(path) {
Ok(expanded) => expanded,
Err(_) => continue,
};
if let Some(p) = expanded.parent() {
fs::create_dir_all(p)?;
}
fs::copy(repo_file, expanded)?;
info!("Copied `{}` from local repository.", path.display());
}
} else {
warn!("Could not find package `{}` in config.", package);
} }
Ok(()) Ok(())
} }
pub fn apply(pc: &PathConfig, file: Option<&str>) -> Result<()> { pub fn apply(pc: &PathConfig, package: Option<&str>) -> Result<()> {
if let Some(file) = file { if let Some(package) = package {
apply_one(pc, Path::new(file)) apply_one(pc, package)
} else { } else {
apply_all(pc) apply_all(pc)
} }
@ -104,16 +132,16 @@ pub fn apply(pc: &PathConfig, file: Option<&str>) -> Result<()> {
pub fn stage(pc: &PathConfig) -> Result<()> { pub fn stage(pc: &PathConfig) -> Result<()> {
let workdir = path::resolve(&pc.config.repos.local)?; let workdir = path::resolve(&pc.config.repos.local)?;
let repo_lookup = get_repo_lookup(workdir.as_ref(), workdir.as_ref())?; let repo_files = walk_repo(workdir.as_ref())?;
let package_lookup = get_package_lookup(pc); let package_lookup = get_package_lookup(pc);
// Find all files in our repository that are no longer being referenced in // Find all files in our repository that are no longer being referenced in
// our primary config file. They should be removed from the repository. // our primary config file. They should be removed from the repository.
for (repo_unresolved, repo_resolved) in &repo_lookup { for repo_file in &repo_files {
if !package_lookup.contains_key(repo_unresolved) { if !package_lookup.contains_key(repo_file.unresolved()) {
fs::remove_file(repo_resolved)?; fs::remove_file(repo_file.resolved())?;
} }
if let Some(p) = repo_resolved.resolved().parent() { if let Some(p) = repo_file.resolved().parent() {
if p.read_dir()?.next().is_none() { if p.read_dir()?.next().is_none() {
fs::remove_dir(p)?; fs::remove_dir(p)?;
} }
@ -122,13 +150,15 @@ pub fn stage(pc: &PathConfig) -> Result<()> {
// Find all resolvable files in our primary config and copy them into the // Find all resolvable files in our primary config and copy them into the
// repository. // repository.
for (package_unresolved, package_resolved) in &package_lookup { for (key, value) in &package_lookup {
let mut copy = package_resolved.resolved().to_path_buf(); if let Some(value) = value {
copy.push(package_unresolved); let mut copy = value.resolved().to_path_buf();
copy.push(key);
if let Some(p) = copy.parent() { if let Some(p) = copy.parent() {
fs::create_dir_all(p)?; fs::create_dir_all(p)?;
} }
fs::copy(package_resolved.resolved(), copy)?; fs::copy(value.resolved(), copy)?;
}
} }
info!( info!(
@ -143,8 +173,8 @@ pub fn stage(pc: &PathConfig) -> Result<()> {
// Utility // Utility
// ======================================== // ========================================
fn get_repo_lookup(root: &Path, path: &Path) -> Result<HashMap<PathBuf, ResPathBuf>> { fn recursive_walk_repo(root: &Path, path: &Path) -> Result<Vec<ResPathBuf>> {
let mut seen = HashMap::new(); let mut seen = Vec::new();
if path.is_dir() { if path.is_dir() {
for entry in fs::read_dir(path)? { for entry in fs::read_dir(path)? {
let nested = entry?.path(); let nested = entry?.path();
@ -152,26 +182,31 @@ fn get_repo_lookup(root: &Path, path: &Path) -> Result<HashMap<PathBuf, ResPathB
if nested.ends_with(".git") { if nested.ends_with(".git") {
continue; continue;
} }
let nested = get_repo_lookup(root, &nested)?; let nested = recursive_walk_repo(root, &nested)?;
seen.extend(nested); seen.extend_from_slice(&nested);
} else { } else {
let relative = nested let relative = nested
.strip_prefix(root) .strip_prefix(root)
.expect("Relative git file could not be stripped properly.") .expect("Relative git file could not be stripped properly.");
.to_path_buf(); seen.push(ResPathBuf::new(&nested, relative)?);
seen.insert(relative, ResPathBuf::new(&nested)?);
} }
} }
} }
Ok(seen) Ok(seen)
} }
fn get_package_lookup(pc: &PathConfig) -> HashMap<PathBuf, ResPathBuf> { fn walk_repo(root: &Path) -> Result<Vec<ResPathBuf>> {
recursive_walk_repo(root, root)
}
fn get_package_lookup(pc: &PathConfig) -> HashMap<PathBuf, Option<ResPathBuf>> {
let mut seen = HashMap::new(); let mut seen = HashMap::new();
for (_, packages) in &pc.config.packages { for (_, packages) in &pc.config.packages {
for path in packages { for path in packages {
if let Ok(resolved) = path::resolve(path) { if let Ok(resolved) = path::resolve(path) {
seen.insert(path.to_path_buf(), resolved); seen.insert(path.to_path_buf(), Some(resolved));
} else {
seen.insert(path.to_path_buf(), None);
} }
} }
} }

View File

@ -9,8 +9,8 @@ use std::error::Error;
type Result = std::result::Result<(), Box<dyn Error>>; type Result = std::result::Result<(), Box<dyn Error>>;
pub fn run_apply(config: PathConfig, file: Option<&str>) -> Result { pub fn run_apply(config: PathConfig, package: Option<&str>) -> Result {
copy::apply(&config, file)?; copy::apply(&config, package)?;
Ok(()) Ok(())
} }

View File

@ -1,8 +1,10 @@
use clap::{App, AppSettings, Arg}; use clap::{App, AppSettings, Arg};
use homesync::path::ResPathBuf; use homesync::path::ResPathBuf;
use simplelog;
use simplelog::{error, paris};
use std::{error::Error, io, path::PathBuf}; use std::{error::Error, io, path::PathBuf};
use {
simplelog,
simplelog::{error, paris},
};
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
fn log_level() -> simplelog::LevelFilter { fn log_level() -> simplelog::LevelFilter {
@ -42,18 +44,18 @@ fn main() {
App::new("apply") App::new("apply")
.about("Copy files from local repository to rest of desktop") .about("Copy files from local repository to rest of desktop")
.arg( .arg(
Arg::new("file") Arg::new("package")
.value_name("FILE") .value_name("PACKAGE")
.conflicts_with("all") .conflicts_with("all")
.required_unless_present("all") .required_unless_present("all")
.help("The file we want to overwrite from the local repository") .help("The package we want to configure from the local repository")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
Arg::new("all") Arg::new("all")
.long("all") .long("all")
.conflicts_with("file") .conflicts_with("package")
.help("Indicates we want to copy all files from the local repository") .help("Indicates we want to copy all configurations from the local repository")
.takes_value(false), .takes_value(false),
), ),
) )
@ -93,7 +95,7 @@ fn dispatch(matches: clap::ArgMatches) -> Result<(), Box<dyn Error>> {
let candidates = find_candidates(&matches)?; let candidates = find_candidates(&matches)?;
let config = homesync::config::load(&candidates)?; let config = homesync::config::load(&candidates)?;
match matches.subcommand() { match matches.subcommand() {
Some(("apply", matches)) => Ok(homesync::run_apply(config, matches.value_of("file"))?), Some(("apply", matches)) => Ok(homesync::run_apply(config, matches.value_of("package"))?),
Some(("daemon", matches)) => { Some(("daemon", matches)) => {
let freq_secs: u64 = match matches.value_of("frequency") { let freq_secs: u64 = match matches.value_of("frequency") {
Some(f) => f.parse().unwrap_or(0), Some(f) => f.parse().unwrap_or(0),

View File

@ -68,13 +68,13 @@ fn unresolved_error(path: &Path) -> io::Error {
} }
impl ResPathBuf { impl ResPathBuf {
pub fn new(path: &Path) -> Result<Self> { pub fn new(inner: &Path, unresolved: &Path) -> Result<Self> {
if !path.is_absolute() { if !inner.is_absolute() {
Err(unresolved_error(path))?; Err(unresolved_error(inner))?;
} }
Ok(ResPathBuf { Ok(ResPathBuf {
inner: path.to_path_buf(), inner: inner.to_path_buf(),
unresolved: path.to_path_buf(), unresolved: unresolved.to_path_buf(),
}) })
} }