push: Add method to insert a user push rule in a Ruleset
This commit is contained in:
parent
a50f5f5cb2
commit
f87714d73f
@ -35,7 +35,9 @@ Improvements:
|
||||
* Stabilize support for event replacements (edits)
|
||||
* Add support for read receipts for threads (MSC3771 / Matrix 1.4)
|
||||
* Add `push::PredefinedRuleId` and associated types as a list of predefined push rule IDs
|
||||
* Add `Ruleset::get` to access push rules.
|
||||
* Add convenience methods to `Ruleset`
|
||||
* `Ruleset::get` to access a push rule
|
||||
* `Ruleset::insert` to add or update user push rules
|
||||
|
||||
# 0.10.5
|
||||
|
||||
|
@ -21,10 +21,10 @@ default = ["client", "server"]
|
||||
client = []
|
||||
server = []
|
||||
|
||||
api = ["dep:http", "dep:thiserror"]
|
||||
api = ["dep:http"]
|
||||
canonical-json = []
|
||||
compat = ["ruma-macros/compat", "ruma-identifiers-validation/compat"]
|
||||
events = ["dep:thiserror"]
|
||||
events = []
|
||||
js = ["dep:js-sys", "getrandom?/js", "uuid?/js"]
|
||||
markdown = ["pulldown-cmark"]
|
||||
rand = ["dep:rand", "dep:uuid"]
|
||||
@ -35,7 +35,7 @@ unstable-msc2677 = []
|
||||
unstable-msc2746 = []
|
||||
unstable-msc2870 = []
|
||||
unstable-msc3245 = ["unstable-msc3246"]
|
||||
unstable-msc3246 = ["unstable-msc3551", "dep:thiserror"]
|
||||
unstable-msc3246 = ["unstable-msc3551"]
|
||||
unstable-msc3381 = ["unstable-msc1767"]
|
||||
unstable-msc3488 = ["unstable-msc1767"]
|
||||
unstable-msc3551 = ["unstable-msc1767"]
|
||||
@ -67,7 +67,7 @@ ruma-identifiers-validation = { version = "0.9.0", path = "../ruma-identifiers-v
|
||||
ruma-macros = { version = "0.10.5", path = "../ruma-macros" }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true, features = ["raw_value"] }
|
||||
thiserror = { workspace = true, optional = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true, features = ["attributes"] }
|
||||
url = "2.2.2"
|
||||
uuid = { version = "1.0.0", optional = true, features = ["v4"] }
|
||||
|
@ -20,6 +20,7 @@ use indexmap::{Equivalent, IndexSet};
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(feature = "unstable-unspecified")]
|
||||
use serde_json::Value as JsonValue;
|
||||
use thiserror::Error;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{
|
||||
@ -85,6 +86,92 @@ impl Ruleset {
|
||||
self.into_iter()
|
||||
}
|
||||
|
||||
/// Inserts a user-defined rule in the rule set.
|
||||
///
|
||||
/// If a rule with the same kind and `rule_id` exists, it will be replaced.
|
||||
///
|
||||
/// If `after` or `before` is set, the rule will be moved relative to the rule with the given
|
||||
/// ID. If both are set, the rule will become the next-most important rule with respect to
|
||||
/// `before`. If neither are set, and the rule is newly inserted, it will become the rule with
|
||||
/// the highest priority of its kind.
|
||||
///
|
||||
/// Returns an error if the parameters are invalid.
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
rule: NewPushRule,
|
||||
after: Option<&str>,
|
||||
before: Option<&str>,
|
||||
) -> Result<(), InsertPushRuleError> {
|
||||
let rule_id = rule.rule_id();
|
||||
if rule_id.starts_with('.') {
|
||||
return Err(InsertPushRuleError::ServerDefaultRuleId);
|
||||
}
|
||||
if rule_id.contains('/') {
|
||||
return Err(InsertPushRuleError::InvalidRuleId);
|
||||
}
|
||||
if rule_id.contains('\\') {
|
||||
return Err(InsertPushRuleError::InvalidRuleId);
|
||||
}
|
||||
if after.map_or(false, |s| s.starts_with('.')) {
|
||||
return Err(InsertPushRuleError::RelativeToServerDefaultRule);
|
||||
}
|
||||
if before.map_or(false, |s| s.starts_with('.')) {
|
||||
return Err(InsertPushRuleError::RelativeToServerDefaultRule);
|
||||
}
|
||||
|
||||
match rule {
|
||||
NewPushRule::Override(r) => {
|
||||
let mut rule = ConditionalPushRule::from(r);
|
||||
|
||||
if let Some(prev_rule) = self.override_.get(rule.rule_id.as_str()) {
|
||||
rule.enabled = prev_rule.enabled;
|
||||
}
|
||||
|
||||
// `m.rule.master` should always be the rule with the highest priority, so we insert
|
||||
// this one at most at the second place.
|
||||
let default_position = 1;
|
||||
|
||||
insert_and_move_rule(&mut self.override_, rule, default_position, after, before)
|
||||
}
|
||||
NewPushRule::Underride(r) => {
|
||||
let mut rule = ConditionalPushRule::from(r);
|
||||
|
||||
if let Some(prev_rule) = self.underride.get(rule.rule_id.as_str()) {
|
||||
rule.enabled = prev_rule.enabled;
|
||||
}
|
||||
|
||||
insert_and_move_rule(&mut self.underride, rule, 0, after, before)
|
||||
}
|
||||
NewPushRule::Content(r) => {
|
||||
let mut rule = PatternedPushRule::from(r);
|
||||
|
||||
if let Some(prev_rule) = self.content.get(rule.rule_id.as_str()) {
|
||||
rule.enabled = prev_rule.enabled;
|
||||
}
|
||||
|
||||
insert_and_move_rule(&mut self.content, rule, 0, after, before)
|
||||
}
|
||||
NewPushRule::Room(r) => {
|
||||
let mut rule = SimplePushRule::from(r);
|
||||
|
||||
if let Some(prev_rule) = self.room.get(rule.rule_id.as_str()) {
|
||||
rule.enabled = prev_rule.enabled;
|
||||
}
|
||||
|
||||
insert_and_move_rule(&mut self.room, rule, 0, after, before)
|
||||
}
|
||||
NewPushRule::Sender(r) => {
|
||||
let mut rule = SimplePushRule::from(r);
|
||||
|
||||
if let Some(prev_rule) = self.sender.get(rule.rule_id.as_str()) {
|
||||
rule.enabled = prev_rule.enabled;
|
||||
}
|
||||
|
||||
insert_and_move_rule(&mut self.sender, rule, 0, after, before)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the rule from the given kind and with the given `rule_id` in the rule set.
|
||||
pub fn get(&self, kind: RuleKind, rule_id: impl AsRef<str>) -> Option<AnyPushRuleRef<'_>> {
|
||||
let rule_id = rule_id.as_ref();
|
||||
@ -545,6 +632,13 @@ impl NewSimplePushRule {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NewSimplePushRule> for SimplePushRule {
|
||||
fn from(new_rule: NewSimplePushRule) -> Self {
|
||||
let NewSimplePushRule { rule_id, actions } = new_rule;
|
||||
Self { actions, default: false, enabled: true, rule_id }
|
||||
}
|
||||
}
|
||||
|
||||
/// A patterned push rule to update or create.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
@ -567,6 +661,13 @@ impl NewPatternedPushRule {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NewPatternedPushRule> for PatternedPushRule {
|
||||
fn from(new_rule: NewPatternedPushRule) -> Self {
|
||||
let NewPatternedPushRule { rule_id, pattern, actions } = new_rule;
|
||||
Self { actions, default: false, enabled: true, rule_id, pattern }
|
||||
}
|
||||
}
|
||||
|
||||
/// A conditional push rule to update or create.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
@ -592,6 +693,76 @@ impl NewConditionalPushRule {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NewConditionalPushRule> for ConditionalPushRule {
|
||||
fn from(new_rule: NewConditionalPushRule) -> Self {
|
||||
let NewConditionalPushRule { rule_id, conditions, actions } = new_rule;
|
||||
Self { actions, default: false, enabled: true, rule_id, conditions }
|
||||
}
|
||||
}
|
||||
|
||||
/// The error type returned when trying to insert a user-defined push rule into a `Ruleset`.
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum InsertPushRuleError {
|
||||
/// The rule ID starts with a dot (`.`), which is reserved for server-default rules.
|
||||
#[error("rule IDs starting with a dot are reserved for server-default rules")]
|
||||
ServerDefaultRuleId,
|
||||
|
||||
/// The rule ID contains an invalid character.
|
||||
#[error("invalid rule ID")]
|
||||
InvalidRuleId,
|
||||
|
||||
/// The rule is being placed relative to a server-default rule, which is forbidden.
|
||||
#[error("can't place rule relative to server-default rule")]
|
||||
RelativeToServerDefaultRule,
|
||||
|
||||
/// The `before` or `after` rule could not be found.
|
||||
#[error("The before or after rule could not be found")]
|
||||
UnknownRuleId,
|
||||
|
||||
/// `before` has a higher priority than `after`.
|
||||
#[error("before has a higher priority than after")]
|
||||
BeforeHigherThanAfter,
|
||||
}
|
||||
|
||||
/// Insert the rule in the given indexset and move it to the given position.
|
||||
pub fn insert_and_move_rule<T>(
|
||||
set: &mut IndexSet<T>,
|
||||
rule: T,
|
||||
default_position: usize,
|
||||
after: Option<&str>,
|
||||
before: Option<&str>,
|
||||
) -> Result<(), InsertPushRuleError>
|
||||
where
|
||||
T: Hash + Eq,
|
||||
str: Equivalent<T>,
|
||||
{
|
||||
let (from, replaced) = set.replace_full(rule);
|
||||
|
||||
let mut to = default_position;
|
||||
|
||||
if let Some(rule_id) = after {
|
||||
let idx = set.get_index_of(rule_id).ok_or(InsertPushRuleError::UnknownRuleId)?;
|
||||
to = idx + 1;
|
||||
}
|
||||
if let Some(rule_id) = before {
|
||||
let idx = set.get_index_of(rule_id).ok_or(InsertPushRuleError::UnknownRuleId)?;
|
||||
|
||||
if idx < to {
|
||||
return Err(InsertPushRuleError::BeforeHigherThanAfter);
|
||||
}
|
||||
|
||||
to = idx;
|
||||
}
|
||||
|
||||
// Only move the item if it's new or if it was positioned.
|
||||
if replaced.is_none() || after.is_some() || before.is_some() {
|
||||
set.move_index(from, to);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeMap;
|
||||
|
Loading…
x
Reference in New Issue
Block a user