From 1c51800f17ef0726ae8e7e07697ac5b1f3f140d0 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Thu, 28 May 2020 15:06:45 +0200 Subject: [PATCH] Replace MatchList type with a trait. Signed-off-by: Wolfgang Bumiller --- src/lib.rs | 10 ++- src/match_list.rs | 172 ++++++++++++++++++++-------------------------- 2 files changed, 80 insertions(+), 102 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 97a8ff2..cb89917 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,11 +25,11 @@ //! b"/things/shop/bananas/other.txt", //! ]; //! -//! let mut list = MatchList::new(vec![ +//! let mut list = vec![ //! MatchEntry::include(Pattern::path("shop")?), //! MatchEntry::exclude(Pattern::path("bananas")?), //! MatchEntry::include(Pattern::path("bananas/curved.*")?), -//! ]); +//! ]; //! //! assert_eq!(list.matches("/things", None), None); //! assert_eq!(list.matches("/things/shop", None), Some(MatchType::Include)); @@ -53,9 +53,7 @@ //! list.pop(); //! //! // let's check some patterns, anything starting with a 'c', 'f' or 's': -//! let mut list = MatchList::new(vec![ -//! MatchEntry::include(Pattern::path("[cfs]*")?), -//! ]); +//! let mut list = vec![MatchEntry::include(Pattern::path("[cfs]*")?)]; //! assert_eq!(list.matches("/things", None), None); //! assert_eq!(list.matches("/things/file1.dat", None), Some(MatchType::Include)); //! assert_eq!(list.matches("/things/file2.dat", None), Some(MatchType::Include)); @@ -94,7 +92,7 @@ mod match_list; mod pattern; #[doc(inline)] -pub use match_list::{MatchEntry, MatchFlag, MatchList, MatchListRef, MatchPattern, MatchType}; +pub use match_list::{MatchEntry, MatchFlag, MatchList, MatchPattern, MatchType}; #[doc(inline)] pub use pattern::{ParseError, Pattern, PatternFlag}; diff --git a/src/match_list.rs b/src/match_list.rs index ee456dc..27f4faf 100644 --- a/src/match_list.rs +++ b/src/match_list.rs @@ -305,103 +305,71 @@ impl MatchEntry { } } -/// Convenience type for an ordered list of `MatchEntry`s. This is just a `Vec`. -#[derive(Clone, Debug, Default)] -pub struct MatchList { - list: Vec, +#[doc(hidden)] +pub trait MatchListEntry { + fn entry_matches(&self, path: &[u8], file_mode: Option) -> Option; + fn entry_matches_exact(&self, path: &[u8], file_mode: Option) -> Option; } -impl MatchList { - pub fn new>>(list: T) -> Self { - Self { list: list.into() } - } - - /// Create a new empty list with a specified maximum capacity. - pub fn with_capacity(capacity: usize) -> Self { - Self { - list: Vec::with_capacity(capacity), +impl MatchListEntry for &'_ MatchEntry { + fn entry_matches(&self, path: &[u8], file_mode: Option) -> Option { + if self.matches(path, file_mode) { + Some(self.match_type()) + } else { + None } } - /// Add another entry. - pub fn push(&mut self, entry: MatchEntry) { - self.list.push(entry) - } - - /// Remove the list entry. - pub fn pop(&mut self) -> Option { - self.list.pop() + fn entry_matches_exact(&self, path: &[u8], file_mode: Option) -> Option { + if self.matches_exact(path, file_mode) { + Some(self.match_type()) + } else { + None + } } } -impl From> for MatchList { - fn from(list: Vec) -> Self { - Self { list } - } -} - -impl Into> for MatchList { - fn into(self) -> Vec { - self.list - } -} - -impl std::ops::Deref for MatchList { - type Target = MatchListRef; - - fn deref(&self) -> &Self::Target { - (&self.list[..]).into() - } -} - -/// Helper to provide the `matches` method on slices of `MatchEntry`s. -#[repr(transparent)] -pub struct MatchListRef([MatchEntry]); - -impl std::ops::Deref for MatchListRef { - type Target = [MatchEntry]; - - fn deref(&self) -> &Self::Target { - &self.0[..] - } -} - -impl<'a> From<&'a [MatchEntry]> for &'a MatchListRef { - fn from(entries: &'a [MatchEntry]) -> &'a MatchListRef { - unsafe { &*(entries as *const [MatchEntry] as *const MatchListRef) } - } -} - -impl MatchListRef { +pub trait MatchList: Sized { /// Check whether this list contains anything matching a prefix of the specified path, and the /// specified file mode. - pub fn matches>(&self, path: T, file_mode: Option) -> Option { + fn matches>(self, path: T, file_mode: Option) -> Option { self.matches_do(path.as_ref(), file_mode) } - fn matches_do(&self, path: &[u8], file_mode: Option) -> Option { - for m in self.iter().rev() { - if m.matches(path, file_mode) { - return Some(m.match_type()); - } - } - - None - } + fn matches_do(self, path: &[u8], file_mode: Option) -> Option; /// Check whether this list contains anything exactly matching the path and mode. - pub fn matches_exact>( - &self, + fn matches_exact>( + self, path: T, file_mode: Option, ) -> Option { self.matches_exact_do(path.as_ref(), file_mode) } - fn matches_exact_do(&self, path: &[u8], file_mode: Option) -> Option { - for m in self.iter().rev() { - if m.matches_exact(path, file_mode) { - return Some(m.match_type()); + fn matches_exact_do(self, path: &[u8], file_mode: Option) -> Option; +} + +impl MatchList for T +where + T: IntoIterator, + ::IntoIter: DoubleEndedIterator, + ::Item: MatchListEntry, +{ + fn matches_do(self, path: &[u8], file_mode: Option) -> Option { + for m in self.into_iter().rev() { + if let Some(mt) = m.entry_matches(path, file_mode) { + return Some(mt); + } + } + + None + } + + fn matches_exact_do(self, path: &[u8], file_mode: Option) -> Option { + for m in self.into_iter().rev() { + if let Some(mt) = m.entry_matches_exact(path, file_mode) { + return Some(mt); } } @@ -409,20 +377,35 @@ impl MatchListRef { } } +#[test] +fn assert_containers_implement_match_list() { + use std::iter::FromIterator; + + let vec = vec![MatchEntry::include(crate::Pattern::path("a*").unwrap())]; + assert_eq!(vec.matches("asdf", None), Some(MatchType::Include)); + + // FIXME: ideally we can make this work as well! + let vd = std::collections::VecDeque::::from_iter(vec.clone()); + assert_eq!(vd.matches("asdf", None), Some(MatchType::Include)); + + let list: &[MatchEntry] = &vec[..]; + assert_eq!(list.matches("asdf", None), Some(MatchType::Include)); + + let list: Vec<&MatchEntry> = vec.iter().collect(); + assert_eq!(list.matches("asdf", None), Some(MatchType::Include)); +} + #[test] fn test_file_type_matches() { - let matchlist = MatchList::new( - [ - MatchEntry::parse_pattern("a_dir/", PatternFlag::PATH_NAME, MatchType::Include) - .unwrap(), - MatchEntry::parse_pattern("!a_file", PatternFlag::PATH_NAME, MatchType::Include) - .unwrap() - .flags(MatchFlag::MATCH_REGULAR_FILES), - MatchEntry::parse_pattern("!another_dir//", PatternFlag::PATH_NAME, MatchType::Include) - .unwrap(), - ] - .as_ref(), - ); + let matchlist = vec![ + MatchEntry::parse_pattern("a_dir/", PatternFlag::PATH_NAME, MatchType::Include) + .unwrap(), + MatchEntry::parse_pattern("!a_file", PatternFlag::PATH_NAME, MatchType::Include) + .unwrap() + .flags(MatchFlag::MATCH_REGULAR_FILES), + MatchEntry::parse_pattern("!another_dir//", PatternFlag::PATH_NAME, MatchType::Include) + .unwrap(), + ]; assert_eq!( matchlist.matches("a_dir", Some(libc::S_IFDIR)), Some(MatchType::Include) @@ -450,14 +433,11 @@ fn test_file_type_matches() { fn test_anchored_matches() { use crate::Pattern; - let matchlist = MatchList::new( - [ - MatchEntry::new(Pattern::path("file-a").unwrap(), MatchType::Include), - MatchEntry::new(Pattern::path("some/path").unwrap(), MatchType::Include) - .flags(MatchFlag::ANCHORED), - ] - .as_ref(), - ); + let matchlist = vec![ + MatchEntry::new(Pattern::path("file-a").unwrap(), MatchType::Include), + MatchEntry::new(Pattern::path("some/path").unwrap(), MatchType::Include) + .flags(MatchFlag::ANCHORED), + ]; assert_eq!(matchlist.matches("file-a", None), Some(MatchType::Include)); assert_eq!(