1pub mod v3 {
6 use ruma_common::{
11 api::{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>(
68 self,
69 base_url: &str,
70 access_token: ruma_common::api::SendAccessToken<'_>,
71 considering: &ruma_common::api::SupportedVersions,
72 ) -> Result<http::Request<T>, ruma_common::api::error::IntoHttpError> {
73 use http::header;
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 http::Request::builder()
90 .method(Self::METHOD)
91 .uri(url)
92 .header(header::CONTENT_TYPE, "application/json")
93 .header(
94 header::AUTHORIZATION,
95 format!(
96 "Bearer {}",
97 access_token
98 .get_required_for_endpoint()
99 .ok_or(ruma_common::api::error::IntoHttpError::NeedsAuthentication)?,
100 ),
101 )
102 .body(ruma_common::serde::json_to_buf(&body)?)
103 .map_err(Into::into)
104 }
105 }
106
107 #[cfg(feature = "server")]
108 impl ruma_common::api::IncomingRequest for Request {
109 type EndpointError = crate::Error;
110 type OutgoingResponse = Response;
111
112 fn try_from_http_request<B, S>(
113 request: http::Request<B>,
114 path_args: &[S],
115 ) -> Result<Self, ruma_common::api::error::FromHttpRequestError>
116 where
117 B: AsRef<[u8]>,
118 S: AsRef<str>,
119 {
120 use ruma_common::push::{
121 NewConditionalPushRule, NewPatternedPushRule, NewSimplePushRule,
122 };
123
124 #[derive(Debug, Deserialize)]
126 #[serde(rename_all = "lowercase")]
127 enum RuleKind {
128 Override,
129 Underride,
130 Sender,
131 Room,
132 Content,
133 }
134
135 #[derive(Deserialize)]
136 struct IncomingRequestQuery {
137 before: Option<String>,
138 after: Option<String>,
139 }
140
141 let (kind, rule_id): (RuleKind, String) =
142 Deserialize::deserialize(serde::de::value::SeqDeserializer::<
143 _,
144 serde::de::value::Error,
145 >::new(
146 path_args.iter().map(::std::convert::AsRef::as_ref)
147 ))?;
148
149 let IncomingRequestQuery { before, after } =
150 serde_html_form::from_str(request.uri().query().unwrap_or(""))?;
151
152 let rule = match kind {
153 RuleKind::Override => {
154 let ConditionalRequestBody { actions, conditions } =
155 serde_json::from_slice(request.body().as_ref())?;
156 NewPushRule::Override(NewConditionalPushRule::new(rule_id, conditions, actions))
157 }
158 RuleKind::Underride => {
159 let ConditionalRequestBody { actions, conditions } =
160 serde_json::from_slice(request.body().as_ref())?;
161 NewPushRule::Underride(NewConditionalPushRule::new(
162 rule_id, conditions, actions,
163 ))
164 }
165 RuleKind::Sender => {
166 let SimpleRequestBody { actions } =
167 serde_json::from_slice(request.body().as_ref())?;
168 let rule_id = rule_id.try_into()?;
169 NewPushRule::Sender(NewSimplePushRule::new(rule_id, actions))
170 }
171 RuleKind::Room => {
172 let SimpleRequestBody { actions } =
173 serde_json::from_slice(request.body().as_ref())?;
174 let rule_id = rule_id.try_into()?;
175 NewPushRule::Room(NewSimplePushRule::new(rule_id, actions))
176 }
177 RuleKind::Content => {
178 let PatternedRequestBody { actions, pattern } =
179 serde_json::from_slice(request.body().as_ref())?;
180 NewPushRule::Content(NewPatternedPushRule::new(rule_id, pattern, actions))
181 }
182 };
183
184 Ok(Self { rule, before, after })
185 }
186 }
187
188 #[derive(Debug, Serialize)]
189 struct RequestQuery {
190 #[serde(skip_serializing_if = "Option::is_none")]
191 before: Option<String>,
192
193 #[serde(skip_serializing_if = "Option::is_none")]
194 after: Option<String>,
195 }
196
197 #[derive(Debug, Serialize)]
198 #[serde(untagged)]
199 enum RequestBody {
200 Simple(SimpleRequestBody),
201
202 Patterned(PatternedRequestBody),
203
204 Conditional(ConditionalRequestBody),
205 }
206
207 #[derive(Debug, Serialize, Deserialize)]
208 struct SimpleRequestBody {
209 actions: Vec<Action>,
210 }
211
212 #[derive(Debug, Serialize, Deserialize)]
213 struct PatternedRequestBody {
214 actions: Vec<Action>,
215
216 pattern: String,
217 }
218
219 #[derive(Debug, Serialize, Deserialize)]
220 struct ConditionalRequestBody {
221 actions: Vec<Action>,
222
223 conditions: Vec<PushCondition>,
224 }
225
226 impl From<NewPushRule> for RequestBody {
227 fn from(rule: NewPushRule) -> Self {
228 match rule {
229 NewPushRule::Override(r) => RequestBody::Conditional(ConditionalRequestBody {
230 actions: r.actions,
231 conditions: r.conditions,
232 }),
233 NewPushRule::Content(r) => RequestBody::Patterned(PatternedRequestBody {
234 actions: r.actions,
235 pattern: r.pattern,
236 }),
237 NewPushRule::Room(r) => {
238 RequestBody::Simple(SimpleRequestBody { actions: r.actions })
239 }
240 NewPushRule::Sender(r) => {
241 RequestBody::Simple(SimpleRequestBody { actions: r.actions })
242 }
243 NewPushRule::Underride(r) => RequestBody::Conditional(ConditionalRequestBody {
244 actions: r.actions,
245 conditions: r.conditions,
246 }),
247 #[cfg(not(ruma_unstable_exhaustive_types))]
248 _ => unreachable!("variant added to NewPushRule not covered by RequestBody"),
249 }
250 }
251 }
252}