//! `PUT /_matrix/client/*/pushrules/global/{kind}/{ruleId}` //! //! This endpoint allows the creation and modification of push rules for this user ID. pub mod v3 { //! `/v3/` ([spec]) //! //! [spec]: https://spec.matrix.org/latest/client-server-api/#put_matrixclientv3pushrulesglobalkindruleid use ruma_common::{ api::{response, Metadata}, metadata, push::{Action, NewPushRule, PushCondition}, }; use serde::{Deserialize, Serialize}; const METADATA: Metadata = metadata! { method: PUT, rate_limited: true, authentication: AccessToken, history: { 1.0 => "/_matrix/client/r0/pushrules/global/:kind/:rule_id", 1.1 => "/_matrix/client/v3/pushrules/global/:kind/:rule_id", } }; /// Request type for the `set_pushrule` endpoint. #[derive(Clone, Debug)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] pub struct Request { /// The rule. pub rule: NewPushRule, /// Use 'before' with a rule_id as its value to make the new rule the next-most important /// rule with respect to the given user defined rule. pub before: Option, /// This makes the new rule the next-less important rule relative to the given user defined /// rule. pub after: Option, } /// Response type for the `set_pushrule` endpoint. #[response(error = crate::Error)] #[derive(Default)] pub struct Response {} impl Request { /// Creates a new `Request` with the given rule. pub fn new(rule: NewPushRule) -> Self { Self { rule, before: None, after: None } } } impl Response { /// Creates an empty `Response`. pub fn new() -> Self { Self {} } } #[cfg(feature = "client")] impl ruma_common::api::OutgoingRequest for Request { type EndpointError = crate::Error; type IncomingResponse = Response; const METADATA: Metadata = METADATA; fn try_into_http_request( self, base_url: &str, access_token: ruma_common::api::SendAccessToken<'_>, considering_versions: &[ruma_common::api::MatrixVersion], ) -> Result, ruma_common::api::error::IntoHttpError> { use http::header; let query_string = serde_html_form::to_string(RequestQuery { before: self.before, after: self.after, })?; let url = METADATA.make_endpoint_url( considering_versions, base_url, &[&self.rule.kind(), &self.rule.rule_id()], &query_string, )?; let body: RequestBody = self.rule.into(); http::Request::builder() .method(METADATA.method) .uri(url) .header(header::CONTENT_TYPE, "application/json") .header( header::AUTHORIZATION, format!( "Bearer {}", access_token .get_required_for_endpoint() .ok_or(ruma_common::api::error::IntoHttpError::NeedsAuthentication)?, ), ) .body(ruma_common::serde::json_to_buf(&body)?) .map_err(Into::into) } } #[cfg(feature = "server")] impl ruma_common::api::IncomingRequest for Request { type EndpointError = crate::Error; type OutgoingResponse = Response; const METADATA: Metadata = METADATA; fn try_from_http_request( request: http::Request, path_args: &[S], ) -> Result where B: AsRef<[u8]>, S: AsRef, { use ruma_common::push::{ NewConditionalPushRule, NewPatternedPushRule, NewSimplePushRule, }; // Exhaustive enum to fail deserialization on unknown variants. #[derive(Debug, Deserialize)] #[serde(rename_all = "lowercase")] enum RuleKind { Override, Underride, Sender, Room, Content, } #[derive(Deserialize)] struct IncomingRequestQuery { before: Option, after: Option, } let (kind, rule_id): (RuleKind, String) = Deserialize::deserialize(serde::de::value::SeqDeserializer::< _, serde::de::value::Error, >::new( path_args.iter().map(::std::convert::AsRef::as_ref) ))?; let IncomingRequestQuery { before, after } = serde_html_form::from_str(request.uri().query().unwrap_or(""))?; let rule = match kind { RuleKind::Override => { let ConditionalRequestBody { actions, conditions } = serde_json::from_slice(request.body().as_ref())?; NewPushRule::Override(NewConditionalPushRule::new(rule_id, conditions, actions)) } RuleKind::Underride => { let ConditionalRequestBody { actions, conditions } = serde_json::from_slice(request.body().as_ref())?; NewPushRule::Underride(NewConditionalPushRule::new( rule_id, conditions, actions, )) } RuleKind::Sender => { let SimpleRequestBody { actions } = serde_json::from_slice(request.body().as_ref())?; let rule_id = rule_id.try_into()?; NewPushRule::Sender(NewSimplePushRule::new(rule_id, actions)) } RuleKind::Room => { let SimpleRequestBody { actions } = serde_json::from_slice(request.body().as_ref())?; let rule_id = rule_id.try_into()?; NewPushRule::Room(NewSimplePushRule::new(rule_id, actions)) } RuleKind::Content => { let PatternedRequestBody { actions, pattern } = serde_json::from_slice(request.body().as_ref())?; NewPushRule::Content(NewPatternedPushRule::new(rule_id, pattern, actions)) } }; Ok(Self { rule, before, after }) } } #[derive(Debug, Serialize)] struct RequestQuery { #[serde(skip_serializing_if = "Option::is_none")] before: Option, #[serde(skip_serializing_if = "Option::is_none")] after: Option, } #[derive(Debug, Serialize)] #[serde(untagged)] enum RequestBody { Simple(SimpleRequestBody), Patterned(PatternedRequestBody), Conditional(ConditionalRequestBody), } #[derive(Debug, Serialize, Deserialize)] struct SimpleRequestBody { actions: Vec, } #[derive(Debug, Serialize, Deserialize)] struct PatternedRequestBody { actions: Vec, pattern: String, } #[derive(Debug, Serialize, Deserialize)] struct ConditionalRequestBody { actions: Vec, conditions: Vec, } impl From for RequestBody { fn from(rule: NewPushRule) -> Self { match rule { NewPushRule::Override(r) => RequestBody::Conditional(ConditionalRequestBody { actions: r.actions, conditions: r.conditions, }), NewPushRule::Content(r) => RequestBody::Patterned(PatternedRequestBody { actions: r.actions, pattern: r.pattern, }), NewPushRule::Room(r) => { RequestBody::Simple(SimpleRequestBody { actions: r.actions }) } NewPushRule::Sender(r) => { RequestBody::Simple(SimpleRequestBody { actions: r.actions }) } NewPushRule::Underride(r) => RequestBody::Conditional(ConditionalRequestBody { actions: r.actions, conditions: r.conditions, }), #[cfg(not(feature = "unstable-exhaustive-types"))] _ => unreachable!("variant added to NewPushRule not covered by RequestBody"), } } } }