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