Import upstream v0.16.22, stripped
Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f Enterprise-only files removed or emptied: 63 Enterprise-only snippets removed: 117 in 50 files Dangling module declarations removed: 5 Cargo edits turning enterprise off: 14 Verification: clean Enterprise feature gates left for rebuilt features: 19 in 18 files Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
This commit is contained in:
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use ahash::{AHashMap, AHashSet};
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum MatchType {
|
||||
Equal(String),
|
||||
StartsWith(String),
|
||||
EndsWith(String),
|
||||
Matches(GlobPattern),
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct GlobPattern {
|
||||
pattern: Vec<PatternChar>,
|
||||
to_lower: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum PatternChar {
|
||||
WildcardMany { num: usize, match_pos: usize },
|
||||
WildcardSingle { match_pos: usize },
|
||||
Char { char: char, match_pos: usize },
|
||||
}
|
||||
|
||||
impl GlobPattern {
|
||||
pub fn compile(pattern: &str, to_lower: bool) -> Self {
|
||||
let mut chars = Vec::new();
|
||||
let mut is_escaped = false;
|
||||
let mut str = pattern.chars().peekable();
|
||||
|
||||
while let Some(char) = str.next() {
|
||||
match char {
|
||||
'*' if !is_escaped => {
|
||||
let mut num = 1;
|
||||
while let Some('*') = str.peek() {
|
||||
num += 1;
|
||||
str.next();
|
||||
}
|
||||
chars.push(PatternChar::WildcardMany { num, match_pos: 0 });
|
||||
}
|
||||
'?' if !is_escaped => {
|
||||
chars.push(PatternChar::WildcardSingle { match_pos: 0 });
|
||||
}
|
||||
'\\' if !is_escaped => {
|
||||
is_escaped = true;
|
||||
continue;
|
||||
}
|
||||
_ => {
|
||||
if is_escaped {
|
||||
is_escaped = false;
|
||||
}
|
||||
if to_lower && char.is_uppercase() {
|
||||
for char in char.to_lowercase() {
|
||||
chars.push(PatternChar::Char { char, match_pos: 0 });
|
||||
}
|
||||
} else {
|
||||
chars.push(PatternChar::Char { char, match_pos: 0 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GlobPattern {
|
||||
pattern: chars,
|
||||
to_lower,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_compile(pattern: &str, to_lower: bool) -> Result<Self, String> {
|
||||
// Detect if the key is a glob pattern
|
||||
let mut last_ch = '\0';
|
||||
let mut has_escape = false;
|
||||
let mut is_glob = false;
|
||||
for ch in pattern.chars() {
|
||||
match ch {
|
||||
'\\' => {
|
||||
has_escape = true;
|
||||
}
|
||||
'*' | '?' if last_ch != '\\' => {
|
||||
is_glob = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
last_ch = ch;
|
||||
}
|
||||
|
||||
if is_glob {
|
||||
Ok(GlobPattern::compile(pattern, to_lower))
|
||||
} else {
|
||||
Err(if has_escape {
|
||||
pattern.replace('\\', "")
|
||||
} else {
|
||||
pattern.to_string()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Credits: Algorithm ported from https://research.swtch.com/glob
|
||||
pub fn matches(&self, value: &str) -> bool {
|
||||
let value = if self.to_lower {
|
||||
value.to_lowercase().chars().collect::<Vec<_>>()
|
||||
} else {
|
||||
value.chars().collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
let mut px = 0;
|
||||
let mut nx = 0;
|
||||
let mut next_px = 0;
|
||||
let mut next_nx = 0;
|
||||
|
||||
while px < self.pattern.len() || nx < value.len() {
|
||||
match self.pattern.get(px) {
|
||||
Some(PatternChar::Char { char, .. }) => {
|
||||
if matches!(value.get(nx), Some(nc) if nc == char ) {
|
||||
px += 1;
|
||||
nx += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Some(PatternChar::WildcardSingle { .. }) if nx < value.len() => {
|
||||
px += 1;
|
||||
nx += 1;
|
||||
continue;
|
||||
}
|
||||
Some(PatternChar::WildcardMany { .. }) => {
|
||||
next_px = px;
|
||||
next_nx = nx + 1;
|
||||
px += 1;
|
||||
continue;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
if 0 < next_nx && next_nx <= value.len() {
|
||||
px = next_px;
|
||||
nx = next_nx;
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct GlobSet {
|
||||
entries: AHashSet<String>,
|
||||
patterns: Vec<GlobPattern>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GlobMap<V> {
|
||||
entries: AHashMap<String, V>,
|
||||
patterns: Vec<(GlobPattern, V)>,
|
||||
}
|
||||
|
||||
impl GlobSet {
|
||||
pub fn new() -> Self {
|
||||
GlobSet::default()
|
||||
}
|
||||
|
||||
pub fn insert_pattern(&mut self, pattern: &str) {
|
||||
match GlobPattern::try_compile(pattern, false) {
|
||||
Ok(glob) => {
|
||||
self.patterns.push(glob);
|
||||
}
|
||||
Err(entry) => {
|
||||
self.entries.insert(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_entry(&mut self, entry: String) {
|
||||
self.entries.insert(entry);
|
||||
}
|
||||
|
||||
pub fn contains(&self, key: &str) -> bool {
|
||||
self.entries.contains(key) || self.patterns.iter().any(|pattern| pattern.matches(key))
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> GlobMap<V> {
|
||||
pub fn new() -> Self {
|
||||
GlobMap {
|
||||
entries: AHashMap::new(),
|
||||
patterns: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_pattern(&mut self, pattern: &str, value: V) {
|
||||
match GlobPattern::try_compile(pattern, false) {
|
||||
Ok(glob) => {
|
||||
self.patterns.push((glob, value));
|
||||
}
|
||||
Err(entry) => {
|
||||
self.entries.insert(entry, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_entry(&mut self, entry: String, value: V) {
|
||||
self.entries.insert(entry, value);
|
||||
}
|
||||
|
||||
pub fn get(&self, key: &str) -> Option<&V> {
|
||||
self.entries.get(key).or_else(|| {
|
||||
self.patterns
|
||||
.iter()
|
||||
.find_map(|(pattern, value)| pattern.matches(key).then_some(value))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Default for GlobMap<V> {
|
||||
fn default() -> Self {
|
||||
GlobMap::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl MatchType {
|
||||
pub fn parse(value: &str) -> Self {
|
||||
if value == "*" {
|
||||
MatchType::All
|
||||
} else if let Some(value) = value.strip_suffix('*') {
|
||||
MatchType::StartsWith(value.to_string())
|
||||
} else if let Some(value) = value.strip_prefix('*') {
|
||||
MatchType::EndsWith(value.to_string())
|
||||
} else if value.contains('*') {
|
||||
MatchType::Matches(GlobPattern::compile(value, false))
|
||||
} else {
|
||||
MatchType::Equal(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn matches(&self, value: &str) -> bool {
|
||||
match self {
|
||||
MatchType::Equal(pattern) => value == pattern,
|
||||
MatchType::StartsWith(pattern) => value.starts_with(pattern),
|
||||
MatchType::EndsWith(pattern) => value.ends_with(pattern),
|
||||
MatchType::Matches(pattern) => pattern.matches(value),
|
||||
MatchType::All => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for GlobPattern {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Ok(GlobPattern::compile(
|
||||
<Cow<&str>>::deserialize(deserializer)?.as_ref(),
|
||||
true,
|
||||
))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user