Skip to main content

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/v1.19/client-server-api/#put_matrixclientv3pushrulesglobalkindruleid
9
10    use ruma_common::{
11        api::{auth_scheme::AccessToken, error::Error, 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]
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    #[doc(hidden)]
62    #[cfg(feature = "client")]
63    pub struct RequestBody(NewPushRule);
64
65    #[cfg(feature = "client")]
66    impl ruma_common::api::OutgoingBody for RequestBody {
67        type Error = serde_json::Error;
68
69        fn content_type(&self) -> Option<http::HeaderValue> {
70            Some(ruma_common::http_headers::APPLICATION_JSON)
71        }
72
73        fn try_into_buf<T: Default + bytes::BufMut + AsRef<[u8]>>(self) -> serde_json::Result<T> {
74            match self.0 {
75                NewPushRule::Override(r) | NewPushRule::Underride(r) => {
76                    let body =
77                        ConditionalRequestBody { actions: r.actions, conditions: r.conditions };
78                    ruma_common::serde::json_to_buf(&body)
79                }
80                NewPushRule::Content(r) => {
81                    let body = PatternedRequestBody { actions: r.actions, pattern: r.pattern };
82                    ruma_common::serde::json_to_buf(&body)
83                }
84                NewPushRule::Room(r) => {
85                    let body = SimpleRequestBody { actions: r.actions };
86                    ruma_common::serde::json_to_buf(&body)
87                }
88                NewPushRule::Sender(r) => {
89                    let body = SimpleRequestBody { actions: r.actions };
90                    ruma_common::serde::json_to_buf(&body)
91                }
92                #[cfg(not(ruma_unstable_exhaustive_types))]
93                _ => unreachable!("variant added to NewPushRule not serializable to request body"),
94            }
95        }
96    }
97
98    #[cfg(feature = "client")]
99    impl ruma_common::api::OutgoingRequest for Request {
100        type Body = RequestBody;
101        type EndpointError = Error;
102        type IncomingResponse = Response;
103
104        fn try_into_http_request_inner(
105            self,
106            base_url: &str,
107            considering: std::borrow::Cow<'_, ruma_common::api::SupportedVersions>,
108        ) -> Result<http::Request<RequestBody>, ruma_common::api::error::IntoHttpError> {
109            use ruma_common::api::Metadata;
110
111            let query_string = serde_html_form::to_string(RequestQuery {
112                before: self.before,
113                after: self.after,
114            })?;
115
116            let url = Self::make_endpoint_url(
117                considering,
118                base_url,
119                &[&self.rule.kind(), &self.rule.rule_id()],
120                &query_string,
121            )?;
122
123            let http_request = http::Request::builder()
124                .method(Self::METHOD)
125                .uri(url)
126                .body(RequestBody(self.rule))?;
127
128            Ok(http_request)
129        }
130    }
131
132    #[cfg(feature = "server")]
133    impl ruma_common::api::IncomingRequest for Request {
134        type EndpointError = Error;
135        type OutgoingResponse = Response;
136
137        fn try_from_http_request_inner(
138            request: http::Request<&[u8]>,
139            path_args: &[&str],
140        ) -> Result<Self, ruma_common::api::error::DeserializationError> {
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            let (kind, rule_id): (RuleKind, String) =
157                serde::Deserialize::deserialize(serde::de::value::SeqDeserializer::<
158                    _,
159                    serde::de::value::Error,
160                >::new(path_args.iter().copied()))?;
161
162            let RequestQuery { before, after } =
163                serde_html_form::from_str(request.uri().query().unwrap_or(""))?;
164
165            let rule = match kind {
166                RuleKind::Override => {
167                    let ConditionalRequestBody { actions, conditions } =
168                        serde_json::from_slice(request.body())?;
169                    NewPushRule::Override(NewConditionalPushRule::new(rule_id, conditions, actions))
170                }
171                RuleKind::Underride => {
172                    let ConditionalRequestBody { actions, conditions } =
173                        serde_json::from_slice(request.body())?;
174                    NewPushRule::Underride(NewConditionalPushRule::new(
175                        rule_id, conditions, actions,
176                    ))
177                }
178                RuleKind::Sender => {
179                    let SimpleRequestBody { actions } = serde_json::from_slice(request.body())?;
180                    let rule_id = rule_id.try_into()?;
181                    NewPushRule::Sender(NewSimplePushRule::new(rule_id, actions))
182                }
183                RuleKind::Room => {
184                    let SimpleRequestBody { actions } = serde_json::from_slice(request.body())?;
185                    let rule_id = rule_id.try_into()?;
186                    NewPushRule::Room(NewSimplePushRule::new(rule_id, actions))
187                }
188                RuleKind::Content => {
189                    let PatternedRequestBody { actions, pattern } =
190                        serde_json::from_slice(request.body())?;
191                    NewPushRule::Content(NewPatternedPushRule::new(rule_id, pattern, actions))
192                }
193            };
194
195            Ok(Self { rule, before, after })
196        }
197    }
198
199    #[derive(Debug)]
200    #[cfg_attr(feature = "client", derive(serde::Serialize))]
201    #[cfg_attr(feature = "server", derive(serde::Deserialize))]
202    struct RequestQuery {
203        #[serde(skip_serializing_if = "Option::is_none")]
204        before: Option<String>,
205
206        #[serde(skip_serializing_if = "Option::is_none")]
207        after: Option<String>,
208    }
209
210    #[derive(Debug)]
211    #[cfg_attr(feature = "client", derive(serde::Serialize))]
212    #[cfg_attr(feature = "server", derive(serde::Deserialize))]
213    struct SimpleRequestBody {
214        actions: Vec<Action>,
215    }
216
217    #[derive(Debug)]
218    #[cfg_attr(feature = "client", derive(serde::Serialize))]
219    #[cfg_attr(feature = "server", derive(serde::Deserialize))]
220    struct PatternedRequestBody {
221        actions: Vec<Action>,
222
223        pattern: String,
224    }
225
226    #[derive(Debug)]
227    #[cfg_attr(feature = "client", derive(serde::Serialize))]
228    #[cfg_attr(feature = "server", derive(serde::Deserialize))]
229    struct ConditionalRequestBody {
230        actions: Vec<Action>,
231
232        conditions: Vec<PushCondition>,
233    }
234}