feat: Possibility to put an array of expressions and they must all matches
Signed-off-by: Awiteb <a@4rs.nl>
This commit is contained in:
parent
c96b859931
commit
cc2f8a791b
2 changed files with 66 additions and 35 deletions
|
@ -51,40 +51,66 @@ where
|
||||||
Ok(url)
|
Ok(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialize a vector of strings into a vector of `regex::Regex`
|
/// Parse the `re` key in the table, which can be a string or an array of string
|
||||||
|
fn parse_re<'de, D>(toml_value: &Value) -> Result<Vec<String>, D::Error>
|
||||||
|
where
|
||||||
|
D: de::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
match toml_value {
|
||||||
|
Value::String(str_re) => Ok(vec![str_re.to_owned()]),
|
||||||
|
Value::Array(re_vec) => {
|
||||||
|
re_vec
|
||||||
|
.iter()
|
||||||
|
.map(|str_re| {
|
||||||
|
str_re.as_str().map(String::from).ok_or_else(|| {
|
||||||
|
<D::Error as de::Error>::custom(format!(
|
||||||
|
"expected an array of string, found `{str_re}`"
|
||||||
|
))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
value => {
|
||||||
|
Err(<D::Error as de::Error>::custom(format!(
|
||||||
|
"expected a string value or an array of string for `re`, found `{value}`"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse the vector of string regex to `Vec<Regex>`
|
||||||
|
fn parse_re_vec<'de, D>(re_vec: Vec<String>) -> Result<Vec<Regex>, D::Error>
|
||||||
|
where
|
||||||
|
D: de::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
re_vec
|
||||||
|
.into_iter()
|
||||||
|
.map(|re| re.parse().map_err(custom_de_err::<'de, D>))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialize `RegexReason`
|
||||||
fn deserialize_regex_reason<'de, D>(deserializer: D) -> Result<Vec<RegexReason>, D::Error>
|
fn deserialize_regex_reason<'de, D>(deserializer: D) -> Result<Vec<RegexReason>, D::Error>
|
||||||
where
|
where
|
||||||
D: de::Deserializer<'de>,
|
D: de::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let Ok(json_value) = Vec::<Value>::deserialize(deserializer) else {
|
let Ok(toml_value) = Vec::<Value>::deserialize(deserializer) else {
|
||||||
return Err(de::Error::custom(
|
return Err(de::Error::custom(
|
||||||
"expected an array of strings or tables with the keys `re` and optional `reason`",
|
"expected an array contains strings or arrays of string or tables with the keys `re` \
|
||||||
|
and optional `reason`",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
json_value
|
toml_value
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|value| {
|
.map(|value| {
|
||||||
if let Value::String(re) = value {
|
if let Value::Table(table) = value {
|
||||||
Ok(RegexReason::new(
|
let re_vec = table.get("re").map(parse_re::<D>).ok_or_else(|| {
|
||||||
re.parse().map_err(custom_de_err::<'de, D>)?,
|
<D::Error as de::Error>::custom(
|
||||||
None,
|
"The table must contain a `re` key with a string value or an array of \
|
||||||
))
|
string",
|
||||||
} else if let Value::Table(table) = value {
|
)
|
||||||
let re = table
|
})??;
|
||||||
.get("re")
|
|
||||||
.map(|re| {
|
|
||||||
re.as_str().map(String::from).ok_or_else(|| {
|
|
||||||
<D::Error as de::Error>::custom(format!(
|
|
||||||
"expected a string value for `re`, found `{re}`"
|
|
||||||
))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.ok_or_else(|| {
|
|
||||||
<D::Error as de::Error>::custom(
|
|
||||||
"The table must contain a `re` key with a string value",
|
|
||||||
)
|
|
||||||
})??;
|
|
||||||
let reason = table
|
let reason = table
|
||||||
.get("reason")
|
.get("reason")
|
||||||
.map(|reason| {
|
.map(|reason| {
|
||||||
|
@ -103,14 +129,16 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(RegexReason::new(parse_re_vec::<D>(re_vec)?, reason))
|
||||||
|
} else if matches!(value, Value::String(_) | Value::Array(_)) {
|
||||||
Ok(RegexReason::new(
|
Ok(RegexReason::new(
|
||||||
re.parse().map_err(custom_de_err::<'de, D>)?,
|
parse_re_vec::<D>(parse_re::<D>(&value)?)?,
|
||||||
reason,
|
None,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Err(de::Error::custom(format!(
|
Err(de::Error::custom(format!(
|
||||||
"unexpected value in the regex list, expected a string or a table with `re` \
|
"unexpected value in the regex list, expected a string or an array of string \
|
||||||
(string) and optional `reason` (string), found `{value}`"
|
or a table with `re` (string) and optional `reason` (string), found `{value}`"
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -149,7 +177,7 @@ pub struct Telegram {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RegexReason {
|
pub struct RegexReason {
|
||||||
/// The regular expression
|
/// The regular expression
|
||||||
pub re: Regex,
|
pub re_vec: Vec<Regex>,
|
||||||
/// Optional reason
|
/// Optional reason
|
||||||
pub reason: Option<String>,
|
pub reason: Option<String>,
|
||||||
}
|
}
|
||||||
|
@ -231,14 +259,16 @@ pub struct Config {
|
||||||
|
|
||||||
impl RegexReason {
|
impl RegexReason {
|
||||||
/// Create a new `RegexReason` instance
|
/// Create a new `RegexReason` instance
|
||||||
fn new(re: Regex, reason: Option<String>) -> Self {
|
fn new(re: Vec<Regex>, reason: Option<String>) -> Self {
|
||||||
Self { re, reason }
|
Self { re_vec: re, reason }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for RegexReason {
|
impl Display for RegexReason {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}", self.re).ok();
|
for re in &self.re_vec {
|
||||||
|
write!(f, "{re} ").ok();
|
||||||
|
}
|
||||||
if let Some(ref reason) = self.reason {
|
if let Some(ref reason) = self.reason {
|
||||||
write!(f, " ({reason})").ok();
|
write!(f, " ({reason})").ok();
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,9 +28,10 @@ pub trait ExprChecker {
|
||||||
impl ExprChecker for Expr {
|
impl ExprChecker for Expr {
|
||||||
fn is_match<'a>(&'a self, user: &ForgejoUser) -> Option<RegexReason> {
|
fn is_match<'a>(&'a self, user: &ForgejoUser) -> Option<RegexReason> {
|
||||||
let one_of = |hay: &str, exprs: &'a Vec<RegexReason>| {
|
let one_of = |hay: &str, exprs: &'a Vec<RegexReason>| {
|
||||||
exprs
|
exprs.iter().find(|re| {
|
||||||
.iter()
|
hay.split('\n')
|
||||||
.find(|re| hay.split('\n').any(|line| re.re.is_match(line.trim())))
|
.any(|line| re.re_vec.iter().all(|re| re.is_match(line.trim())))
|
||||||
|
})
|
||||||
};
|
};
|
||||||
[
|
[
|
||||||
one_of(&user.username, &self.usernames),
|
one_of(&user.username, &self.usernames),
|
||||||
|
|
Loading…
Reference in a new issue