ruma_client_api/push/
set_pushrule.rs

1//! `PUT /_matrix/client/*/pushrules/global/{kind}/{ruleId}`
2//!
3//! This endpoint allows the creation and modification of push rules for this user ID.
4
5pub mod v3 {
6    //! `/v3/` ([spec])
7    //!
8    //! [spec]: https://spec.matrix.org/latest/client-server-api/#put_matrixclientv3pushrulesglobalkindruleid
9
10    use ruma_common::{
11        api::{auth_scheme::AccessToken, response},
12        metadata,
13        push::{Action, NewPushRule, PushCondition},
14    };
15
16    metadata! {
17        method: PUT,
18        rate_limited: true,
19        authentication: AccessToken,
20        history: {
21            1.0 => "/_matrix/client/r0/pushrules/global/{kind}/{rule_id}",
22            1.1 => "/_matrix/client/v3/pushrules/global/{kind}/{rule_id}",
23        }
24    }
25
26    /// Request type for the `set_pushrule` endpoint.
27    #[derive(Clone, Debug)]
28    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
29    pub struct Request {
30        /// The rule.
31        pub rule: NewPushRule,
32
33        /// Use 'before' with a rule_id as its value to make the new rule the next-most important
34        /// rule with respect to the given user defined rule.
35        pub before: Option<String>,
36
37        /// This makes the new rule the next-less important rule relative to the given user defined
38        /// rule.
39        pub after: Option<String>,
40    }
41
42    /// Response type for the `set_pushrule` endpoint.
43    #[response(error = crate::Error)]
44    #[derive(Default)]
45    pub struct Response {}
46
47    impl Request {
48        /// Creates a new `Request` with the given rule.
49        pub fn new(rule: NewPushRule) -> Self {
50            Self { rule, before: None, after: None }
51        }
52    }
53
54    impl Response {
55        /// Creates an empty `Response`.
56        pub fn new() -> Self {
57            Self {}
58        }
59    }
60
61    #[cfg(feature = "client")]
62    impl ruma_common::api::OutgoingRequest for Request {
63        type EndpointError = crate::Error;
64        type IncomingResponse = Response;
65
66        fn try_into_http_request<T: Default + bytes::BufMut + AsRef<[u8]>>(
67            self,
68            base_url: &str,
69            access_token: ruma_common::api::auth_scheme::SendAccessToken<'_>,
70            considering: std::borrow::Cow<'_, ruma_common::api::SupportedVersions>,
71        ) -> Result<http::Request<T>, ruma_common::api::error::IntoHttpError> {
72            use ruma_common::api::{Metadata, auth_scheme::AuthScheme};
73
74            fn serialize_rule<T: Default + bytes::BufMut>(
75                rule: NewPushRule,
76            ) -> serde_json::Result<T> {
77                match rule {
78                    NewPushRule::Override(r) | NewPushRule::Underride(r) => {
79                        let body =
80                            ConditionalRequestBody { actions: r.actions, conditions: r.conditions };
81                        ruma_common::serde::json_to_buf(&body)
82                    }
83                    NewPushRule::Content(r) => {
84                        let body = PatternedRequestBody { actions: r.actions, pattern: r.pattern };
85                        ruma_common::serde::json_to_buf(&body)
86                    }
87                    NewPushRule::Room(r) => {
88                        let body = SimpleRequestBody { actions: r.actions };
89                        ruma_common::serde::json_to_buf(&body)
90                    }
91                    NewPushRule::Sender(r) => {
92                        let body = SimpleRequestBody { actions: r.actions };
93                        ruma_common::serde::json_to_buf(&body)
94                    }
95                    #[cfg(not(ruma_unstable_exhaustive_types))]
96                    _ => unreachable!(
97                        "variant added to NewPushRule not serializable to request body"
98                    ),
99                }
100            }
101
102            let query_string = serde_html_form::to_string(RequestQuery {
103                before: self.before,
104                after: self.after,
105            })?;
106
107            let url = Self::make_endpoint_url(
108                considering,
109                base_url,
110                &[&self.rule.kind(), &self.rule.rule_id()],
111                &query_string,
112            )?;
113
114            let mut http_request = http::Request::builder()
115                .method(Self::METHOD)
116                .uri(url)
117                .header(http::header::CONTENT_TYPE, ruma_common::http_headers::APPLICATION_JSON)
118                .body(serialize_rule(self.rule)?)?;
119
120            Self::Authentication::add_authentication(&mut http_request, access_token).map_err(
121                |error| ruma_common::api::error::IntoHttpError::Authentication(error.into()),
122            )?;
123
124            Ok(http_request)
125        }
126    }
127
128    #[cfg(feature = "server")]
129    impl ruma_common::api::IncomingRequest for Request {
130        type EndpointError = crate::Error;
131        type OutgoingResponse = Response;
132
133        fn try_from_http_request<B, S>(
134            request: http::Request<B>,
135            path_args: &[S],
136        ) -> Result<Self, ruma_common::api::error::FromHttpRequestError>
137        where
138            B: AsRef<[u8]>,
139            S: AsRef<str>,
140        {
141            use ruma_common::push::{
142                NewConditionalPushRule, NewPatternedPushRule, NewSimplePushRule,
143            };
144
145            // Exhaustive enum to fail deserialization on unknown variants.
146            #[derive(Debug, serde::Deserialize)]
147            #[serde(rename_all = "lowercase")]
148            enum RuleKind {
149                Override,
150                Underride,
151                Sender,
152                Room,
153                Content,
154            }
155
156            Self::check_request_method(request.method())?;
157
158            let (kind, rule_id): (RuleKind, String) =
159                serde::Deserialize::deserialize(serde::de::value::SeqDeserializer::<
160                    _,
161                    serde::de::value::Error,
162                >::new(
163                    path_args.iter().map(::std::convert::AsRef::as_ref),
164                ))?;
165
166            let RequestQuery { before, after } =
167                serde_html_form::from_str(request.uri().query().unwrap_or(""))?;
168
169            let rule = match kind {
170                RuleKind::Override => {
171                    let ConditionalRequestBody { actions, conditions } =
172                        serde_json::from_slice(request.body().as_ref())?;
173                    NewPushRule::Override(NewConditionalPushRule::new(rule_id, conditions, actions))
174                }
175                RuleKind::Underride => {
176                    let ConditionalRequestBody { actions, conditions } =
177                        serde_json::from_slice(request.body().as_ref())?;
178                    NewPushRule::Underride(NewConditionalPushRule::new(
179                        rule_id, conditions, actions,
180                    ))
181                }
182                RuleKind::Sender => {
183                    let SimpleRequestBody { actions } =
184                        serde_json::from_slice(request.body().as_ref())?;
185                    let rule_id = rule_id.try_into()?;
186                    NewPushRule::Sender(NewSimplePushRule::new(rule_id, actions))
187                }
188                RuleKind::Room => {
189                    let SimpleRequestBody { actions } =
190                        serde_json::from_slice(request.body().as_ref())?;
191                    let rule_id = rule_id.try_into()?;
192                    NewPushRule::Room(NewSimplePushRule::new(rule_id, actions))
193                }
194                RuleKind::Content => {
195                    let PatternedRequestBody { actions, pattern } =
196                        serde_json::from_slice(request.body().as_ref())?;
197                    NewPushRule::Content(NewPatternedPushRule::new(rule_id, pattern, actions))
198                }
199            };
200
201            Ok(Self { rule, before, after })
202        }
203    }
204
205    #[derive(Debug)]
206    #[cfg_attr(feature = "client", derive(serde::Serialize))]
207    #[cfg_attr(feature = "server", derive(serde::Deserialize))]
208    struct RequestQuery {
209        #[serde(skip_serializing_if = "Option::is_none")]
210        before: Option<String>,
211
212        #[serde(skip_serializing_if = "Option::is_none")]
213        after: Option<String>,
214    }
215
216    #[derive(Debug)]
217    #[cfg_attr(feature = "client", derive(serde::Serialize))]
218    #[cfg_attr(feature = "server", derive(serde::Deserialize))]
219    struct SimpleRequestBody {
220        actions: Vec<Action>,
221    }
222
223    #[derive(Debug)]
224    #[cfg_attr(feature = "client", derive(serde::Serialize))]
225    #[cfg_attr(feature = "server", derive(serde::Deserialize))]
226    struct PatternedRequestBody {
227        actions: Vec<Action>,
228
229        pattern: String,
230    }
231
232    #[derive(Debug)]
233    #[cfg_attr(feature = "client", derive(serde::Serialize))]
234    #[cfg_attr(feature = "server", derive(serde::Deserialize))]
235    struct ConditionalRequestBody {
236        actions: Vec<Action>,
237
238        conditions: Vec<PushCondition>,
239    }
240}