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