mirror of
https://git.proxmox.com/git/pathpatterns
synced 2025-07-27 06:03:03 +00:00
Replace MatchList type with a trait.
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
8e38a91c57
commit
1c51800f17
10
src/lib.rs
10
src/lib.rs
@ -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};
|
||||||
|
@ -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,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::<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)
|
.unwrap()
|
||||||
.unwrap()
|
.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!(
|
||||||
|
Loading…
Reference in New Issue
Block a user