From e5a38bab67f790803ff98484fc5835adba7bf62a Mon Sep 17 00:00:00 2001 From: diogo464 Date: Fri, 23 Sep 2022 13:45:57 +0100 Subject: rewrite --- src/depot.rs | 850 ----------------------------------------------------------- 1 file changed, 850 deletions(-) delete mode 100644 src/depot.rs (limited to 'src/depot.rs') diff --git a/src/depot.rs b/src/depot.rs deleted file mode 100644 index b2d4e3c..0000000 --- a/src/depot.rs +++ /dev/null @@ -1,850 +0,0 @@ -use anyhow::Context; -use std::{ - collections::HashSet, - ffi::{OsStr, OsString}, - ops::Index, - path::{Path, PathBuf}, -}; -use thiserror::Error; - -use slotmap::{Key, SlotMap}; - -//pub type Result = std::result::Result; -pub use anyhow::Result; -pub use disk::{read, write}; - -slotmap::new_key_type! {pub struct LinkID;} -slotmap::new_key_type! {struct NodeID;} - -#[derive(Debug, Error)] -enum DepotError { - #[error("path must be relative")] - InvalidPath, - #[error("path must be relative and not empty")] - InvalidLinkPath, -} - -#[derive(Debug, Clone)] -struct Node { - comp: OsString, - parent: NodeID, - kind: NodeKind, -} - -#[derive(Debug, Clone)] -enum NodeKind { - Link(LinkID), - Directory(HashSet), -} - -#[derive(Debug, Clone, PartialEq, Eq)] -enum NodeSearchResult { - Found(NodeID), - /// the closest NodeID up the the search point. - NotFound(NodeID), -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum DirNode { - Link(LinkID), - Directory(PathBuf), -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum SearchResult { - Found(LinkID), - Ancestor(LinkID), - NotFound, -} - -#[derive(Debug, Clone)] -struct Link { - origin: PathBuf, - destination: PathBuf, - origin_id: NodeID, -} - -#[derive(Debug)] -pub struct LinkView<'a> { - link_id: LinkID, - depot: &'a Depot, -} - -impl<'a> LinkView<'a> { - pub fn origin(&self) -> &Path { - &self.depot.links[self.link_id].origin - } - - pub fn destination(&self) -> &Path { - &self.depot.links[self.link_id].destination - } -} - -#[derive(Debug, Clone)] -struct DepotTree { - root: NodeID, - nodes: SlotMap, -} - -impl Default for DepotTree { - fn default() -> Self { - let mut nodes = SlotMap::::default(); - let root = nodes.insert(Node { - comp: Default::default(), - parent: Default::default(), - kind: NodeKind::Directory(Default::default()), - }); - Self { root, nodes } - } -} - -impl Index for DepotTree { - type Output = Node; - - fn index(&self, index: NodeID) -> &Self::Output { - self.nodes.index(index) - } -} - -impl DepotTree { - /// create a node of kind [`NodeKind::Link`]. - pub fn link_create(&mut self, path: &Path, link_id: LinkID) -> Result { - debug_assert!(path_verify_link(path).is_ok()); - - let path_search_result = self.search(path); - - // handle the error cases - match path_search_result { - NodeSearchResult::Found(node_id) => { - let node = &self.nodes[node_id]; - match &node.kind { - NodeKind::Link(_) => Err(anyhow::anyhow!("link already exists")), - NodeKind::Directory(_) => { - Err(anyhow::anyhow!("path already has links under it")) - } - } - } - NodeSearchResult::NotFound(ancestor_node_id) => { - let ancestor_node = &self.nodes[ancestor_node_id]; - match &ancestor_node.kind { - NodeKind::Link(_) => Err(anyhow::anyhow!( - "an ancestor of this path is already linked" - )), - NodeKind::Directory(_) => Ok(()), - } - } - }?; - - // create the node - // unwrap: this is a verfied link path, it must have atleast one component - let filename = path.file_name().unwrap(); - let parent_path = path_parent_or_empty(path); - let node_id = self.nodes.insert(Node { - comp: filename.to_owned(), - parent: Default::default(), - kind: NodeKind::Link(link_id), - }); - let parent_id = self.directory_get_or_create(parent_path, node_id); - self.nodes[node_id].parent = parent_id; - Ok(node_id) - } - - pub fn link_update_id(&mut self, node_id: NodeID, link_id: LinkID) { - let node = &mut self.nodes[node_id]; - match &mut node.kind { - NodeKind::Link(lid) => *lid = link_id, - NodeKind::Directory(_) => unreachable!(), - } - } - - /// attempts to moves a node of kind [`NodeKind::Link`] to `destination`. - pub fn link_move(&mut self, node_id: NodeID, destination: &Path) -> Result<()> { - let parent_id = self.nodes[node_id].parent; - let parent = &mut self.nodes[parent_id]; - - // remove the node from its parent temporarily so that the search never returns this - // link and that way any link will find means an error. - // if an error does happen then we re-add this node to its parent to keep the data - // consistent. - match &mut parent.kind { - NodeKind::Link(_) => unreachable!(), - NodeKind::Directory(children) => children.remove(&node_id), - }; - - let search_result = self.search(destination); - // handle the error cases - match search_result { - NodeSearchResult::Found(found_id) => { - assert!(found_id != node_id); - self.directory_add_child(parent_id, node_id); - return Err(anyhow::anyhow!("link already exists at that path")); - } - NodeSearchResult::NotFound(ancestor_id) => { - let ancestor = &self.nodes[ancestor_id]; - match &ancestor.kind { - NodeKind::Link(_) => { - self.directory_add_child(parent_id, node_id); - return Err(anyhow::anyhow!("ancestor path is already linked")); - } - NodeKind::Directory(_) => {} - } - } - }; - - let destination_parent = path_parent_or_empty(destination); - let new_parent_id = self.directory_get_or_create(destination_parent, node_id); - if new_parent_id != parent_id { - self.nodes[node_id].parent = new_parent_id; - - // we have to re-add and call the remove function because it could lead to the removal - // of several directories if they become empty after this remove. - self.directory_add_child(parent_id, node_id); - self.directory_remove_child(parent_id, node_id); - } - - // unwrap: destination is a verified link path so it has atleast 1 component - let comp = destination.file_name().unwrap(); - let node = &mut self.nodes[node_id]; - if node.comp != comp { - node.comp = comp.to_owned(); - } - - Ok(()) - } - - pub fn link_search(&self, path: &Path) -> SearchResult { - match self.search(path) { - NodeSearchResult::Found(node_id) => match &self.nodes[node_id].kind { - NodeKind::Link(link_id) => SearchResult::Found(*link_id), - NodeKind::Directory(_) => SearchResult::NotFound, - }, - NodeSearchResult::NotFound(node_id) => match &self.nodes[node_id].kind { - NodeKind::Link(link_id) => SearchResult::Ancestor(*link_id), - NodeKind::Directory(_) => SearchResult::NotFound, - }, - } - } - - /// remove a node of kind [`NodeKind::Link`]. - pub fn link_remove(&mut self, node_id: NodeID) { - let node = &self.nodes[node_id]; - assert!(std::matches!(node.kind, NodeKind::Link(_))); - let parent_id = node.parent; - self.nodes.remove(node_id); - self.directory_remove_child(parent_id, node_id); - } - - pub fn links_under(&self, path: &Path) -> impl Iterator + '_ { - let links = match self.search(path) { - NodeSearchResult::Found(node_id) => { - let node = &self.nodes[node_id]; - match &node.kind { - NodeKind::Link(link_id) => vec![*link_id], - NodeKind::Directory(children) => { - let mut links = Vec::new(); - let mut node_ids = Vec::from_iter(children.iter().copied()); - while let Some(child_id) = node_ids.pop() { - let child = &self.nodes[child_id]; - match &child.kind { - NodeKind::Link(link_id) => links.push(*link_id), - NodeKind::Directory(extra_children) => { - node_ids.extend(extra_children.iter().copied()) - } - } - } - links - } - } - } - NodeSearchResult::NotFound(_) => vec![], - }; - links.into_iter() - } - - pub fn has_links_under(&self, path: &Path) -> bool { - // it does not matter what type of node is found. if a directory exists then there - // must be atleast one link under it. - match self.search(path) { - NodeSearchResult::Found(_) => true, - NodeSearchResult::NotFound(_) => false, - } - } - - pub fn read_dir(&self, path: &Path) -> Result + '_> { - match self.search(path) { - NodeSearchResult::Found(node_id) => match &self.nodes[node_id].kind { - NodeKind::Link(_) => Err(anyhow::anyhow!("read dir called on a link")), - NodeKind::Directory(children) => Ok(children.iter().map(|child_id| { - let child = &self.nodes[*child_id]; - match &child.kind { - NodeKind::Link(link_id) => DirNode::Link(*link_id), - NodeKind::Directory(_) => DirNode::Directory(self.build_path(*child_id)), - } - })), - }, - NodeSearchResult::NotFound(_) => Err(anyhow::anyhow!("directory not found")), - } - } - - pub fn build_path(&self, node_id: NodeID) -> PathBuf { - fn recursive_helper(nodes: &SlotMap, nid: NodeID, pbuf: &mut PathBuf) { - if nid.is_null() { - return; - } - let parent_id = nodes[nid].parent; - recursive_helper(nodes, parent_id, pbuf); - pbuf.push(&nodes[nid].comp); - } - - let mut node_path = PathBuf::default(); - recursive_helper(&self.nodes, node_id, &mut node_path); - node_path - } - - fn search(&self, path: &Path) -> NodeSearchResult { - debug_assert!(path_verify(path).is_ok()); - - let mut curr_node_id = self.root; - let mut comp_iter = path_iter_comps(path).peekable(); - while let Some(comp) = comp_iter.next() { - if let Some(child_id) = self.directory_search_children(curr_node_id, comp) { - let child = &self.nodes[child_id]; - match &child.kind { - NodeKind::Link(_) => { - if comp_iter.peek().is_some() { - return NodeSearchResult::NotFound(child_id); - } else { - return NodeSearchResult::Found(child_id); - } - } - NodeKind::Directory(_) => curr_node_id = child_id, - } - } else { - return NodeSearchResult::NotFound(curr_node_id); - } - } - NodeSearchResult::Found(curr_node_id) - } - - // creates directories all the way up to and including path. - // there cannot be any links up to `path`. - fn directory_get_or_create(&mut self, path: &Path, initial_child: NodeID) -> NodeID { - // TODO: this could be replaced if the search function also returned the depth of the - // node and we skip those components and just start creating directories up to the - // path. - let mut curr_node_id = self.root; - for comp in path_iter_comps(path) { - if let Some(child_id) = self.directory_search_children(curr_node_id, comp) { - debug_assert!(std::matches!( - self.nodes[child_id].kind, - NodeKind::Directory(_) - )); - curr_node_id = child_id; - } else { - let new_node_id = self.nodes.insert(Node { - comp: comp.to_owned(), - parent: curr_node_id, - kind: NodeKind::Directory(Default::default()), - }); - self.directory_add_child(curr_node_id, new_node_id); - curr_node_id = new_node_id; - } - } - self.directory_add_child(curr_node_id, initial_child); - curr_node_id - } - - fn directory_search_children(&self, node_id: NodeID, comp: &OsStr) -> Option { - let node = &self.nodes[node_id]; - match &node.kind { - NodeKind::Link(_) => unreachable!(), - NodeKind::Directory(children) => { - for &child_id in children { - let child = &self.nodes[child_id]; - if child.comp == comp { - return Some(child_id); - } - } - } - } - None - } - - fn directory_add_child(&mut self, node_id: NodeID, child_id: NodeID) { - let node = &mut self.nodes[node_id]; - match &mut node.kind { - NodeKind::Link(_) => unreachable!(), - NodeKind::Directory(children) => children.insert(child_id), - }; - } - - fn directory_remove_child(&mut self, node_id: NodeID, child_id: NodeID) { - let node = &mut self.nodes[node_id]; - match &mut node.kind { - NodeKind::Link(_) => unreachable!(), - NodeKind::Directory(children) => { - children.remove(&child_id); - if children.is_empty() && !node.parent.is_null() { - let parent_id = node.parent; - self.directory_remove_child(parent_id, node_id); - } - } - } - } -} - -#[derive(Debug, Default, Clone)] -pub struct Depot { - links: SlotMap, - origin: DepotTree, -} - -impl Depot { - pub fn link_create( - &mut self, - origin: impl AsRef, - destination: impl AsRef, - ) -> Result { - let origin = origin.as_ref(); - let destination = destination.as_ref(); - path_verify_link(origin)?; - path_verify_link(destination)?; - self.link_create_unchecked(origin, destination) - } - - pub fn link_remove(&mut self, link_id: LinkID) { - let node_id = self.links[link_id].origin_id; - self.links.remove(link_id); - self.origin.link_remove(node_id); - } - - /// moves the link specified by `link_id` to the path at `destination`. - /// if the link is already at the destination nothing is done. - /// if the destination is another link that that link is removed. - /// if the destination is under another link then an error is returned. - /// `destination` will be the link's new origin. - pub fn link_move(&mut self, link_id: LinkID, destination: impl AsRef) -> Result<()> { - let destination = destination.as_ref(); - path_verify_link(destination)?; - self.link_move_unchecked(link_id, destination) - } - - #[allow(unused)] - pub fn link_search(&self, path: impl AsRef) -> Result { - let path = path.as_ref(); - path_verify(path)?; - Ok(self.link_search_unchecked(path)) - } - - pub fn link_find(&self, path: impl AsRef) -> Result> { - let path = path.as_ref(); - path_verify(path)?; - Ok(self.link_find_unchecked(path)) - } - - pub fn links_under(&self, path: impl AsRef) -> Result + '_> { - let path = path.as_ref(); - path_verify(path)?; - Ok(self.links_under_unchecked(path)) - } - - pub fn has_links_under(&self, path: impl AsRef) -> Result { - let path = path.as_ref(); - path_verify(path)?; - Ok(self.has_links_under_unchecked(path)) - } - - pub fn links_verify_install(&self, link_ids: impl Iterator) -> Result<()> { - let mut destination = DepotTree::default(); - for link_id in link_ids { - let link = &self.links[link_id]; - destination - .link_create(&link.destination, link_id) - .context("link destinations overlap")?; - } - Ok(()) - } - - pub fn link_view(&self, link_id: LinkID) -> LinkView { - LinkView { - link_id, - depot: self, - } - } - - pub fn read_dir(&self, path: impl AsRef) -> Result + '_> { - let path = path.as_ref(); - path_verify(path)?; - self.read_dir_unchecked(path) - } - - fn link_create_unchecked(&mut self, origin: &Path, destination: &Path) -> Result { - let node_id = self.origin.link_create(origin, Default::default())?; - let link_id = self.links.insert(Link { - origin: origin.to_owned(), - destination: destination.to_owned(), - origin_id: node_id, - }); - self.origin.link_update_id(node_id, link_id); - Ok(link_id) - } - - fn link_move_unchecked(&mut self, link_id: LinkID, destination: &Path) -> Result<()> { - let link = &self.links[link_id]; - if link.origin == destination { - return Ok(()); - } - let node_id = link.origin_id; - self.origin.link_move(node_id, destination)?; - self.links[link_id].origin = destination.to_owned(); - Ok(()) - } - - fn link_search_unchecked(&self, path: &Path) -> SearchResult { - self.origin.link_search(path) - } - - fn link_find_unchecked(&self, path: &Path) -> Option { - match self.link_search_unchecked(path) { - SearchResult::Found(link_id) => Some(link_id), - _ => None, - } - } - - fn links_under_unchecked(&self, path: &Path) -> impl Iterator + '_ { - self.origin.links_under(path) - } - - fn has_links_under_unchecked(&self, path: &Path) -> bool { - self.origin.has_links_under(path) - } - - fn read_dir_unchecked(&self, path: &Path) -> Result + '_> { - self.origin.read_dir(path) - } -} - -/// a verified link path is a path that: -/// + is not empty -/// + is relative -/// + does not contain Prefix/RootDir/ParentDir -fn path_verify_link(path: &Path) -> Result<()> { - // make sure the path is not empty - if path.components().next().is_none() { - return Err(DepotError::InvalidLinkPath.into()); - } - path_verify(path).map_err(|_| DepotError::InvalidLinkPath.into()) -} - -/// a verified path is a path that: -/// + is not empty -/// + is relative -/// + does not contain Prefix/RootDir/ParentDir -fn path_verify(path: &Path) -> Result<()> { - // make sure the path is relative - // make sure the path does not contain '.' or '..' - for component in path.components() { - match component { - std::path::Component::Prefix(_) - | std::path::Component::RootDir - | std::path::Component::CurDir - | std::path::Component::ParentDir => return Err(DepotError::InvalidPath.into()), - std::path::Component::Normal(_) => {} - } - } - Ok(()) -} - -fn path_parent_or_empty(path: &Path) -> &Path { - path.parent().unwrap_or_else(|| Path::new("")) -} - -/// Iterate over the components of a path. -/// # Pre -/// The path can only have "Normal" components. -fn path_iter_comps(path: &Path) -> impl Iterator { - debug_assert!(path_verify(path).is_ok()); - path.components().map(|component| match component { - std::path::Component::Normal(comp) => comp, - _ => unreachable!(), - }) -} - -mod disk { - use std::path::{Path, PathBuf}; - - use anyhow::Context; - use serde::{Deserialize, Serialize}; - - use super::Depot; - - #[derive(Debug, Serialize, Deserialize)] - struct DiskLink { - origin: PathBuf, - destination: PathBuf, - } - - #[derive(Debug, Serialize, Deserialize)] - struct DiskLinks { - links: Vec, - } - - pub fn read(path: &Path) -> anyhow::Result { - let contents = std::fs::read_to_string(path).context("Failed to read depot file")?; - let disk_links = toml::from_str::(&contents) - .context("Failed to parse depot file")? - .links; - let mut depot = Depot::default(); - for disk_link in disk_links { - depot - .link_create(disk_link.origin, disk_link.destination) - .context("Failed to build depot from file. File is in an invalid state")?; - } - Ok(depot) - } - - pub fn write(path: &Path, depot: &Depot) -> anyhow::Result<()> { - let mut links = Vec::with_capacity(depot.links.len()); - for (_, link) in depot.links.iter() { - links.push(DiskLink { - origin: link.origin.clone(), - destination: link.destination.clone(), - }); - } - let contents = - toml::to_string_pretty(&DiskLinks { links }).context("Failed to serialize depot")?; - std::fs::write(path, contents).context("Failed to write depot to file")?; - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_depot_link_create() { - let mut depot = Depot::default(); - let f1 = depot.link_create("f1", "f1").unwrap(); - let f2 = depot.link_create("f2", "f2").unwrap(); - let f3 = depot.link_create("d1/f3", "d1/f3").unwrap(); - let f4 = depot.link_create("d1/d2/f4", "d1/d2/d4").unwrap(); - - assert_eq!(depot.link_find("f1").unwrap(), Some(f1)); - assert_eq!(depot.link_find("f2").unwrap(), Some(f2)); - assert_eq!(depot.link_find("d1/f3").unwrap(), Some(f3)); - assert_eq!(depot.link_find("d1/d2/f4").unwrap(), Some(f4)); - - depot.link_create("f2", "").unwrap_err(); - depot.link_create("", "d4").unwrap_err(); - depot.link_create("f1/f3", "f3").unwrap_err(); - } - - #[test] - fn test_depot_link_remove() { - let mut depot = Depot::default(); - let f1 = depot.link_create("d1/f1", "d1/f1").unwrap(); - let f2 = depot.link_create("d1/f2", "d1/f2").unwrap(); - let _f3 = depot.link_create("d1/f3", "d1/f3").unwrap(); - let f4 = depot.link_create("d1/d2/f4", "d2/f4").unwrap(); - let d3 = depot.link_create("d3", "d3").unwrap(); - - depot.link_remove(f2); - assert_eq!(depot.link_find("d1/f1").unwrap(), Some(f1)); - assert_eq!(depot.link_find("d1/f2").unwrap(), None); - depot.link_remove(f4); - assert_eq!(depot.link_find("d1/d2/f4").unwrap(), None); - depot.link_remove(d3); - assert_eq!(depot.link_find("d3").unwrap(), None); - } - - #[test] - fn test_depot_link_move() { - let mut depot = Depot::default(); - let f1 = depot.link_create("d1/f1", "d1/f1").unwrap(); - let _f2 = depot.link_create("d1/f2", "d1/f2").unwrap(); - - depot.link_move(f1, "").unwrap_err(); - depot.link_move(f1, "d1/f2/f1").unwrap_err(); - depot.link_move(f1, "d1/f2").unwrap_err(); - - depot.link_move(f1, "f1").unwrap(); - assert_eq!(depot.link_view(f1).origin(), Path::new("f1")); - depot.link_move(f1, "f2").unwrap(); - assert_eq!(depot.link_view(f1).origin(), Path::new("f2")); - assert_eq!(depot.link_find("f2").unwrap(), Some(f1)); - } - - #[test] - fn test_depot_link_search() { - let mut depot = Depot::default(); - let f1 = depot.link_create("d1/f1", "d1/f1").unwrap(); - let _f2 = depot.link_create("d1/f2", "d1/f2").unwrap(); - let _f3 = depot.link_create("d1/f3", "d1/f3").unwrap(); - let _f4 = depot.link_create("d1/d2/f4", "d2/f4").unwrap(); - let _d3 = depot.link_create("d3", "d3").unwrap(); - - assert_eq!(depot.link_search("d1/f1").unwrap(), SearchResult::Found(f1)); - assert_eq!( - depot.link_search("d1/f1/f5").unwrap(), - SearchResult::Ancestor(f1) - ); - assert_eq!(depot.link_search("d1").unwrap(), SearchResult::NotFound); - assert_eq!( - depot.link_search("d1/d2/f5").unwrap(), - SearchResult::NotFound - ); - } - - #[test] - fn test_depot_link_find() { - let mut depot = Depot::default(); - let f1 = depot.link_create("d1/f1", "d1/f1").unwrap(); - let _f2 = depot.link_create("d1/f2", "d1/f2").unwrap(); - let f3 = depot.link_create("d1/f3", "d1/f3").unwrap(); - let f4 = depot.link_create("d1/d2/f4", "d2/f4").unwrap(); - let d3 = depot.link_create("d3", "d3").unwrap(); - - assert_eq!(depot.link_find("d1/f1").unwrap(), Some(f1)); - assert_eq!(depot.link_find("d1/f3").unwrap(), Some(f3)); - assert_eq!(depot.link_find("d1/d2/f4").unwrap(), Some(f4)); - assert_eq!(depot.link_find("d3").unwrap(), Some(d3)); - - assert_eq!(depot.link_find("d5").unwrap(), None); - assert_eq!(depot.link_find("d3/d5").unwrap(), None); - assert_eq!(depot.link_find("d1/d2/f5").unwrap(), None); - } - - #[test] - fn test_depot_links_under() { - let mut depot = Depot::default(); - let f1 = depot.link_create("d1/f1", "d1/f1").unwrap(); - let f2 = depot.link_create("d1/f2", "d1/f2").unwrap(); - let f3 = depot.link_create("d1/f3", "d1/f3").unwrap(); - let f4 = depot.link_create("d1/d2/f4", "d2/f4").unwrap(); - let d3 = depot.link_create("d3", "d3").unwrap(); - - let under_f1 = depot.links_under("d1/f1").unwrap().collect::>(); - assert_eq!(under_f1, vec![f1]); - - let under_d1 = depot.links_under("d1").unwrap().collect::>(); - let expected_under_d1 = vec![f1, f2, f3, f4]; - assert!( - under_d1.len() == expected_under_d1.len() - && expected_under_d1.iter().all(|x| under_d1.contains(x)) - ); - - let under_d2 = depot.links_under("d2").unwrap().collect::>(); - assert_eq!(under_d2, vec![]); - - let under_d3 = depot.links_under("d3").unwrap().collect::>(); - assert_eq!(under_d3, vec![d3]); - - let under_root = depot.links_under("").unwrap().collect::>(); - let expected_under_root = vec![f1, f2, f3, f4, d3]; - assert!( - under_root.len() == expected_under_root.len() - && expected_under_root.iter().all(|x| under_root.contains(x)) - ); - } - - #[test] - fn test_depot_has_links_under() { - let mut depot = Depot::default(); - let _f1 = depot.link_create("d1/f1", "d1/f1").unwrap(); - let _f2 = depot.link_create("d1/f2", "d1/f2").unwrap(); - let _f3 = depot.link_create("d1/f3", "d1/f3").unwrap(); - let _f4 = depot.link_create("d1/d2/f4", "d2/f4").unwrap(); - let _d3 = depot.link_create("d3", "d3").unwrap(); - - assert!(depot.has_links_under("").unwrap()); - assert!(depot.has_links_under("d1").unwrap()); - assert!(depot.has_links_under("d3").unwrap()); - assert!(depot.has_links_under("d1/f1").unwrap()); - assert!(depot.has_links_under("d1/d2").unwrap()); - assert!(depot.has_links_under("d1/d2/f4").unwrap()); - - assert!(!depot.has_links_under("d2").unwrap()); - assert!(!depot.has_links_under("d4").unwrap()); - assert!(!depot.has_links_under("d1/d2/f4/f5").unwrap()); - } - - #[test] - fn test_depot_links_verify_install() { - let mut depot = Depot::default(); - let f1 = depot.link_create("nvim", ".config/nvim").unwrap(); - let f2 = depot.link_create("alacritty", ".config/alacritty").unwrap(); - let f3 = depot.link_create("bash/.bashrc", ".bashrc").unwrap(); - let f4 = depot.link_create("bash_laptop/.bashrc", ".bashrc").unwrap(); - - depot - .links_verify_install(vec![f1, f2, f3].into_iter()) - .unwrap(); - depot - .links_verify_install(vec![f1, f2, f3, f4].into_iter()) - .unwrap_err(); - } - - #[test] - fn test_depot_read_dir() { - let mut depot = Depot::default(); - let f1 = depot.link_create("d1/f1", "d1/f1").unwrap(); - let f2 = depot.link_create("d1/f2", "d1/f2").unwrap(); - let f3 = depot.link_create("d1/f3", "d1/f3").unwrap(); - let _f4 = depot.link_create("d1/d2/f4", "d2/f4").unwrap(); - let _d3 = depot.link_create("d3", "d3").unwrap(); - - let read_dir = depot.read_dir("d1").unwrap().collect::>(); - let expected_read_dir = vec![ - DirNode::Link(f1), - DirNode::Link(f2), - DirNode::Link(f3), - DirNode::Directory(PathBuf::from("d1/d2")), - ]; - assert!( - read_dir.len() == expected_read_dir.len() - && expected_read_dir.iter().all(|x| read_dir.contains(x)) - ); - } - - #[test] - fn test_path_verify() { - path_verify(Path::new("")).unwrap(); - path_verify(Path::new("f1")).unwrap(); - path_verify(Path::new("d1/f1")).unwrap(); - path_verify(Path::new("d1/f1.txt")).unwrap(); - path_verify(Path::new("d1/./f1.txt")).unwrap(); - - path_verify(Path::new("/")).unwrap_err(); - path_verify(Path::new("./f1")).unwrap_err(); - path_verify(Path::new("/d1/f1")).unwrap_err(); - path_verify(Path::new("d1/../f1.txt")).unwrap_err(); - path_verify(Path::new("/d1/../f1.txt")).unwrap_err(); - } - - #[test] - fn test_path_verify_link() { - path_verify_link(Path::new("f1")).unwrap(); - path_verify_link(Path::new("d1/f1")).unwrap(); - path_verify_link(Path::new("d1/f1.txt")).unwrap(); - path_verify_link(Path::new("d1/./f1.txt")).unwrap(); - - path_verify_link(Path::new("")).unwrap_err(); - path_verify_link(Path::new("/")).unwrap_err(); - path_verify_link(Path::new("./f1")).unwrap_err(); - path_verify_link(Path::new("/d1/f1")).unwrap_err(); - path_verify_link(Path::new("d1/../f1.txt")).unwrap_err(); - path_verify_link(Path::new("/d1/../f1.txt")).unwrap_err(); - } - - #[test] - fn test_path_iter_comps() { - let path = Path::new("comp1/comp2/./comp3/file.txt"); - let mut iter = path_iter_comps(path); - assert_eq!(iter.next(), Some(OsStr::new("comp1"))); - assert_eq!(iter.next(), Some(OsStr::new("comp2"))); - assert_eq!(iter.next(), Some(OsStr::new("comp3"))); - assert_eq!(iter.next(), Some(OsStr::new("file.txt"))); - assert_eq!(iter.next(), None); - } -} -- cgit