From ed0baec0a3f953c99445f6842dadc5566e89cb75 Mon Sep 17 00:00:00 2001 From: diogo464 Date: Thu, 8 Jul 2021 17:11:46 -0400 Subject: Initial commit --- dotup_cli/src/commands/link.rs | 181 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 dotup_cli/src/commands/link.rs (limited to 'dotup_cli/src/commands/link.rs') diff --git a/dotup_cli/src/commands/link.rs b/dotup_cli/src/commands/link.rs new file mode 100644 index 0000000..fd99253 --- /dev/null +++ b/dotup_cli/src/commands/link.rs @@ -0,0 +1,181 @@ +use clap::Clap; +use std::{ + fs::{DirEntry, Metadata}, + path::{Path, PathBuf}, +}; + +use super::prelude::*; + +#[derive(Clap)] +pub struct Opts { + #[clap(long)] + directory: bool, + + paths: Vec, +} + +/* + config/ + nvim/ + init.vim + lua/ + setup.lua + bash/ + .bashrc + + link nvim .config/nvim + nvim/init.vim -> .config/nvim/init.vim + nvim/lua/setup.lua -> config/nvim/lua/setup.lua + + link bash . + bash/.bashrc -> ./.bashrc + + link --directory scripts .scripts + scripts/ -> ./.scripts +*/ + +pub fn main(config: Config, opts: Opts) -> anyhow::Result<()> { + let mut depot = utils::read_depot(&config.archive_path)?; + + let (origins, destination) = match opts.paths.as_slice() { + p @ [] | p @ [_] => (p, None), + [o @ .., dest] => (o, Some(dest)), + _ => unreachable!(), + }; + + if let Some(destination) = destination { + for path in collect_file_type(origins, FileType::File)? { + let link_desc = LinkDesc { + origin: path, + destination: destination.clone(), + }; + log::info!("Creating link : {}", link_desc); + depot.create_link(link_desc)?; + } + } else { + let base_path = match origins { + [] => std::env::current_dir()?, + [path] => path.clone(), + _ => unreachable!(), + }; + + for link in utils::collect_links_by_base_paths(&depot, std::iter::once(base_path)) { + log::info!("{}", link); + } + } + + //if let Some(destination) = destination { + // for origin in origins { + // let origin_canonical = origin.canonicalize()?; + // let base = if origin_canonical.is_file() { + // origin_canonical.parent().unwrap().to_path_buf() + // } else { + // origin_canonical.to_path_buf() + // }; + + // link(&mut depot, origin.as_path(), destination.as_path(), &base)?; + // } + //} else { + // log::warn!("Missing destination"); + //} + + utils::write_depot(&depot)?; + + Ok(()) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +enum FileType { + File, + Directory, +} + +/// Collects canonical files of the given type starting from, and including, entry_paths +fn collect_file_type( + entry_paths: impl IntoIterator>, + collect_type: FileType, +) -> anyhow::Result> { + let entry_paths: Vec = entry_paths + .into_iter() + .map(|p| p.as_ref().to_path_buf()) + .collect(); + let mut collected = Vec::new(); + let mut pending: Vec<_> = entry_paths.iter().cloned().filter(|p| p.is_dir()).collect(); + + for path in entry_paths { + let path = path.canonicalize()?; + if (path.is_file() && collect_type == FileType::File) + || (path.is_dir() && collect_type == FileType::Directory) + { + collected.push(path); + } + } + + while let Some(dir_path) = pending.pop() { + for entry in dir_path.read_dir()? { + let entry = entry?; + let filetype = entry.file_type()?; + + if filetype.is_file() && collect_type == FileType::File { + collected.push(entry.path()); + } else if filetype.is_dir() { + if collect_type == FileType::Directory { + collected.push(entry.path()); + } + pending.push(entry.path()); + } + } + } + + Ok(collected) +} + +fn link(depot: &mut Depot, origin: &Path, destination: &Path, base: &Path) -> anyhow::Result<()> { + let metadata = std::fs::metadata(origin)?; + if metadata.is_file() { + link_file(depot, origin, destination, base)?; + } else if metadata.is_dir() { + link_directory_recursive(depot, origin, destination, base)?; + } else { + unimplemented!() + } + Ok(()) +} + +fn link_file( + depot: &mut Depot, + origin: &Path, + destination: &Path, + base: &Path, +) -> anyhow::Result<()> { + let origin_canonical = origin + .canonicalize() + .expect("Failed to canonicalize origin path"); + let partial = origin_canonical + .strip_prefix(base) + .expect("Failed to remove prefix from origin path"); + let destination = destination.join(partial); + + let link_desc = LinkDesc { + origin: origin_canonical, + destination, + }; + + log::debug!("Linking file {:#?}", link_desc); + depot.create_link(link_desc)?; + + Ok(()) +} + +fn link_directory_recursive( + depot: &mut Depot, + dir_path: &Path, + destination: &Path, + base: &Path, +) -> anyhow::Result<()> { + for origin in dir_path.read_dir()? { + let origin = origin?.path(); + link(depot, &origin, destination, base)?; + } + Ok(()) +} -- cgit