Replace MatchList type with a trait.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2020-05-28 15:06:45 +02:00
parent 8e38a91c57
commit 1c51800f17
2 changed files with 80 additions and 102 deletions

View File

@ -25,11 +25,11 @@
//! b"/things/shop/bananas/other.txt", //! b"/things/shop/bananas/other.txt",
//! ]; //! ];
//! //!
//! let mut list = MatchList::new(vec![ //! let mut list = vec![
//! MatchEntry::include(Pattern::path("shop")?), //! MatchEntry::include(Pattern::path("shop")?),
//! MatchEntry::exclude(Pattern::path("bananas")?), //! MatchEntry::exclude(Pattern::path("bananas")?),
//! MatchEntry::include(Pattern::path("bananas/curved.*")?), //! MatchEntry::include(Pattern::path("bananas/curved.*")?),
//! ]); //! ];
//! //!
//! assert_eq!(list.matches("/things", None), None); //! assert_eq!(list.matches("/things", None), None);
//! assert_eq!(list.matches("/things/shop", None), Some(MatchType::Include)); //! assert_eq!(list.matches("/things/shop", None), Some(MatchType::Include));
@ -53,9 +53,7 @@
//! list.pop(); //! list.pop();
//! //!
//! // let's check some patterns, anything starting with a 'c', 'f' or 's': //! // let's check some patterns, anything starting with a 'c', 'f' or 's':
//! let mut list = MatchList::new(vec![ //! let mut list = vec![MatchEntry::include(Pattern::path("[cfs]*")?)];
//! MatchEntry::include(Pattern::path("[cfs]*")?),
//! ]);
//! assert_eq!(list.matches("/things", None), None); //! assert_eq!(list.matches("/things", None), None);
//! assert_eq!(list.matches("/things/file1.dat", None), Some(MatchType::Include)); //! assert_eq!(list.matches("/things/file1.dat", None), Some(MatchType::Include));
//! assert_eq!(list.matches("/things/file2.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; mod pattern;
#[doc(inline)] #[doc(inline)]
pub use match_list::{MatchEntry, MatchFlag, MatchList, MatchListRef, MatchPattern, MatchType}; pub use match_list::{MatchEntry, MatchFlag, MatchList, MatchPattern, MatchType};
#[doc(inline)] #[doc(inline)]
pub use pattern::{ParseError, Pattern, PatternFlag}; pub use pattern::{ParseError, Pattern, PatternFlag};

View File

@ -305,103 +305,71 @@ impl MatchEntry {
} }
} }
/// Convenience type for an ordered list of `MatchEntry`s. This is just a `Vec<MatchEntry>`. #[doc(hidden)]
#[derive(Clone, Debug, Default)] pub trait MatchListEntry {
pub struct MatchList { fn entry_matches(&self, path: &[u8], file_mode: Option<u32>) -> Option<MatchType>;
list: Vec<MatchEntry>, fn entry_matches_exact(&self, path: &[u8], file_mode: Option<u32>) -> Option<MatchType>;
} }
impl MatchList { impl MatchListEntry for &'_ MatchEntry {
pub fn new<T: Into<Vec<MatchEntry>>>(list: T) -> Self { fn entry_matches(&self, path: &[u8], file_mode: Option<u32>) -> Option<MatchType> {
Self { list: list.into() } if self.matches(path, file_mode) {
} Some(self.match_type())
} else {
/// Create a new empty list with a specified maximum capacity. None
pub fn with_capacity(capacity: usize) -> Self {
Self {
list: Vec::with_capacity(capacity),
} }
} }
/// Add another entry. fn entry_matches_exact(&self, path: &[u8], file_mode: Option<u32>) -> Option<MatchType> {
pub fn push(&mut self, entry: MatchEntry) { if self.matches_exact(path, file_mode) {
self.list.push(entry) Some(self.match_type())
} else {
None
} }
/// Remove the list entry.
pub fn pop(&mut self) -> Option<MatchEntry> {
self.list.pop()
} }
} }
impl From<Vec<MatchEntry>> for MatchList { pub trait MatchList: Sized {
fn from(list: Vec<MatchEntry>) -> Self {
Self { list }
}
}
impl Into<Vec<MatchEntry>> for MatchList {
fn into(self) -> Vec<MatchEntry> {
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 {
/// Check whether this list contains anything matching a prefix of the specified path, and the /// Check whether this list contains anything matching a prefix of the specified path, and the
/// specified file mode. /// specified file mode.
pub fn matches<T: AsRef<[u8]>>(&self, path: T, file_mode: Option<u32>) -> Option<MatchType> { fn matches<T: AsRef<[u8]>>(self, path: T, file_mode: Option<u32>) -> Option<MatchType> {
self.matches_do(path.as_ref(), file_mode) self.matches_do(path.as_ref(), file_mode)
} }
fn matches_do(&self, path: &[u8], file_mode: Option<u32>) -> Option<MatchType> { fn matches_do(self, path: &[u8], file_mode: Option<u32>) -> Option<MatchType>;
for m in self.iter().rev() {
if m.matches(path, file_mode) {
return Some(m.match_type());
}
}
None
}
/// Check whether this list contains anything exactly matching the path and mode. /// Check whether this list contains anything exactly matching the path and mode.
pub fn matches_exact<T: AsRef<[u8]>>( fn matches_exact<T: AsRef<[u8]>>(
&self, self,
path: T, path: T,
file_mode: Option<u32>, file_mode: Option<u32>,
) -> Option<MatchType> { ) -> Option<MatchType> {
self.matches_exact_do(path.as_ref(), file_mode) self.matches_exact_do(path.as_ref(), file_mode)
} }
fn matches_exact_do(&self, path: &[u8], file_mode: Option<u32>) -> Option<MatchType> { fn matches_exact_do(self, path: &[u8], file_mode: Option<u32>) -> Option<MatchType>;
for m in self.iter().rev() { }
if m.matches_exact(path, file_mode) {
return Some(m.match_type()); impl<T> MatchList for T
where
T: IntoIterator,
<T as IntoIterator>::IntoIter: DoubleEndedIterator,
<T as IntoIterator>::Item: MatchListEntry,
{
fn matches_do(self, path: &[u8], file_mode: Option<u32>) -> Option<MatchType> {
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<u32>) -> Option<MatchType> {
for m in self.into_iter().rev() {
if let Some(mt) = m.entry_matches_exact(path, file_mode) {
return Some(mt);
} }
} }
@ -409,10 +377,27 @@ 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::<MatchEntry>::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] #[test]
fn test_file_type_matches() { fn test_file_type_matches() {
let matchlist = MatchList::new( let matchlist = vec![
[
MatchEntry::parse_pattern("a_dir/", PatternFlag::PATH_NAME, MatchType::Include) MatchEntry::parse_pattern("a_dir/", PatternFlag::PATH_NAME, MatchType::Include)
.unwrap(), .unwrap(),
MatchEntry::parse_pattern("!a_file", PatternFlag::PATH_NAME, MatchType::Include) MatchEntry::parse_pattern("!a_file", PatternFlag::PATH_NAME, MatchType::Include)
@ -420,9 +405,7 @@ fn test_file_type_matches() {
.flags(MatchFlag::MATCH_REGULAR_FILES), .flags(MatchFlag::MATCH_REGULAR_FILES),
MatchEntry::parse_pattern("!another_dir//", PatternFlag::PATH_NAME, MatchType::Include) MatchEntry::parse_pattern("!another_dir//", PatternFlag::PATH_NAME, MatchType::Include)
.unwrap(), .unwrap(),
] ];
.as_ref(),
);
assert_eq!( assert_eq!(
matchlist.matches("a_dir", Some(libc::S_IFDIR)), matchlist.matches("a_dir", Some(libc::S_IFDIR)),
Some(MatchType::Include) Some(MatchType::Include)
@ -450,14 +433,11 @@ fn test_file_type_matches() {
fn test_anchored_matches() { fn test_anchored_matches() {
use crate::Pattern; use crate::Pattern;
let matchlist = MatchList::new( let matchlist = vec![
[
MatchEntry::new(Pattern::path("file-a").unwrap(), MatchType::Include), MatchEntry::new(Pattern::path("file-a").unwrap(), MatchType::Include),
MatchEntry::new(Pattern::path("some/path").unwrap(), MatchType::Include) MatchEntry::new(Pattern::path("some/path").unwrap(), MatchType::Include)
.flags(MatchFlag::ANCHORED), .flags(MatchFlag::ANCHORED),
] ];
.as_ref(),
);
assert_eq!(matchlist.matches("file-a", None), Some(MatchType::Include)); assert_eq!(matchlist.matches("file-a", None), Some(MatchType::Include));
assert_eq!( assert_eq!(