Add optional regex flags to matches filter
This commit is contained in:
parent
0dbcf8d8d0
commit
04efebb7ca
3 changed files with 76 additions and 7 deletions
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
regex_c
|
||||
let pattern = if let Some((val, regex_c)) = &self.regex_cache {
|
||||
if val == variable {
|
||||
regex_c
|
||||
} else {
|
||||
// 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.to_owned(), regex_c));
|
||||
&self.regex_cache.as_ref().unwrap().1
|
||||
}
|
||||
} else {
|
||||
let regex_c = Regex::new(variable)
|
||||
// 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.clone(), regex_c));
|
||||
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>;
|
||||
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue