Add optional regex flags to matches filter

This commit is contained in:
NGnius (Graham) 2022-03-25 15:58:15 -04:00
parent 0dbcf8d8d0
commit 04efebb7ca
3 changed files with 76 additions and 7 deletions

View file

@ -33,7 +33,7 @@ mps-player = { version = "0.7.0", path = "./mps-player", default-features = fals
mps-player = { version = "0.7.0", path = "./mps-player", features = ["mpris-player"] }
[profile.release]
debug = true
debug = false
strip = true
lto = true
codegen-units = 4

View file

@ -1,7 +1,7 @@
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use regex::Regex;
use regex::{Regex, RegexBuilder};
use super::field_filter::{FieldFilterErrorHandling, VariableOrValue};
use crate::lang::utility::{assert_name, assert_token, assert_token_raw, check_name};
@ -20,6 +20,7 @@ pub struct FieldRegexFilter {
field_errors: FieldFilterErrorHandling,
val: VariableOrValue,
regex_cache: Option<(String, Regex)>,
regex_options: u8,
}
impl Display for FieldRegexFilter {
@ -46,12 +47,21 @@ impl MpsFilterPredicate for FieldRegexFilter {
// non-string values will be stopped at parse-time, so this should never occur
_ => Err(RuntimeMsg("Value is not type String".to_string())),
}?;
let pattern = if let Some((_, regex_c)) = &self.regex_cache {
let pattern = if let Some((val, regex_c)) = &self.regex_cache {
if val == variable {
regex_c
} else {
let regex_c = Regex::new(variable)
// only rebuild regex when variable's value changes
let regex_c = build_regex(variable, self.regex_options)
.map_err(|e| RuntimeMsg(format!("Regex compile error: {}", e)))?;
self.regex_cache = Some((variable.clone(), regex_c));
self.regex_cache = Some((variable.to_owned(), regex_c));
&self.regex_cache.as_ref().unwrap().1
}
} else {
// build empty cache
let regex_c = build_regex(variable, self.regex_options)
.map_err(|e| RuntimeMsg(format!("Regex compile error: {}", e)))?;
self.regex_cache = Some((variable.to_owned(), regex_c));
&self.regex_cache.as_ref().unwrap().1
};
if let Some(field) = music_item_lut.field(&self.field_name) {
@ -74,6 +84,7 @@ impl MpsFilterPredicate for FieldRegexFilter {
}
fn reset(&mut self) -> Result<(), RuntimeMsg> {
//self.regex_cache = None;
Ok(())
}
}
@ -124,7 +135,8 @@ impl MpsFilterFactory<FieldRegexFilter> for FieldRegexFilterFactory {
MpsToken::Literal("regex_string".into()),
tokens,
)?;
let regex_c = Regex::new(&literal).map_err(|_| SyntaxError {
let re_flags = regex_flags(tokens)?;
let regex_c = build_regex(&literal, re_flags).map_err(|_| SyntaxError {
line: 0,
token: MpsToken::Literal("[valid regex]".to_string()),
got: Some(MpsToken::Literal(literal.clone())),
@ -137,6 +149,7 @@ impl MpsFilterFactory<FieldRegexFilter> for FieldRegexFilterFactory {
field_errors: error_handling,
val: value,
regex_cache: Some(compiled_cache),
regex_options: re_flags,
})
} else {
let variable = VariableOrValue::Variable(assert_token(
@ -153,11 +166,61 @@ impl MpsFilterFactory<FieldRegexFilter> for FieldRegexFilterFactory {
field_errors: FieldFilterErrorHandling::Error,
val: variable,
regex_cache: None,
regex_options: regex_flags(tokens)?,
})
}
}
}
#[inline]
fn regex_flags(tokens: &mut VecDeque<MpsToken>) -> Result<u8, SyntaxError> {
// syntax: , "flags"
let mut result = 0_u8;
if tokens.is_empty() {
Ok(result)
} else {
assert_token_raw(MpsToken::Comma, tokens)?;
let flags = assert_token(
|t| match t {
MpsToken::Literal(s) => Some(s),
_ => None,
},
MpsToken::Literal("[one or more of imsUux]".into()),
tokens,
)?;
// build flag byte
for c in flags.chars() {
match c {
'i' => result |= 1 << 0,
'm' => result |= 1 << 1,
's' => result |= 1 << 2,
'U' => result |= 1 << 3,
'u' => result |= 1 << 4,
'x' => result |= 1 << 5,
c => return Err(SyntaxError{
line: 0,
token: MpsToken::Literal("[one or more of imsUux]".to_string()),
got: Some(MpsToken::Literal(format!("{}", c))),
}),
}
}
Ok(result)
}
}
#[inline]
fn build_regex(pattern: &str, flags: u8) -> Result<Regex, regex::Error> {
println!("Compiling");
RegexBuilder::new(pattern)
.case_insensitive((flags & (1 << 0)) != 0)
.multi_line((flags & (1 << 1)) != 0)
.dot_matches_new_line((flags & (1 << 2)) != 0)
.swap_greed((flags & (1 << 3)) != 0)
.unicode((flags & (1 << 4)) != 0)
.ignore_whitespace((flags & (1 << 5)) != 0)
.build()
}
pub type FieldRegexFilterStatementFactory =
MpsFilterStatementFactory<FieldRegexFilter, FieldRegexFilterFactory>;

View file

@ -444,6 +444,12 @@ fn execute_regexfilter_line() -> Result<(), MpsError> {
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).(artist? matches `Bruno Mars`)",
false,
true,
)?;
// regex options
execute_single_line(
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).(artist? matches `bruno mars`, `i`)",
false,
true,
)
}