Skip to main content

matrix_sdk/widget/machine/
driver_req.rs

1// Copyright 2023 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! A high-level API for requests that we send to the Matrix driver.
16
17use std::{collections::BTreeMap, marker::PhantomData};
18
19use ruma::{
20    OwnedMxcUri, OwnedUserId,
21    api::client::{account::request_openid_token, delayed_events::update_delayed_event},
22    events::{AnyStateEvent, AnyTimelineEvent, AnyToDeviceEventContent},
23    serde::Raw,
24    to_device::DeviceIdOrAllDevices,
25};
26use serde::Deserialize;
27use serde_json::value::RawValue as RawJsonValue;
28use tracing::error;
29
30use super::{
31    Action, MatrixDriverRequestMeta, SendToDeviceEventResponse, WidgetMachine,
32    from_widget::SendEventResponse, incoming::MatrixDriverResponse,
33};
34use crate::widget::{Capabilities, StateKeySelector, machine::from_widget::DownloadFileResponse};
35
36#[derive(Clone, Debug)]
37pub(crate) enum MatrixDriverRequestData {
38    /// Acquire capabilities from the user given the set of desired
39    /// capabilities.
40    ///
41    /// Must eventually be answered with
42    /// [`MatrixDriverResponse::CapabilitiesAcquired`].
43    AcquireCapabilities(AcquireCapabilities),
44
45    /// Get OpenId token for a given request ID.
46    GetOpenId,
47
48    /// Read events from the timeline.
49    ReadEvents(ReadEventsRequest),
50
51    /// Read room state entries.
52    ReadState(ReadStateRequest),
53
54    /// Send Matrix event that corresponds to the given description.
55    SendEvent(SendEventRequest),
56
57    /// Send a to-device message over the Matrix homeserver.
58    SendToDeviceEvent(SendToDeviceRequest),
59
60    /// Data for sending a UpdateDelayedEvent client server api request.
61    UpdateDelayedEvent(UpdateDelayedEventRequest),
62
63    /// Request a download of a file.
64    DownloadFile(DownloadFileRequest),
65}
66
67/// A handle to a pending `toWidget` request.
68pub(crate) struct MatrixDriverRequestHandle<'m, T> {
69    request_meta: &'m mut MatrixDriverRequestMeta,
70    _phantom: PhantomData<fn() -> T>,
71}
72
73impl<'m, T> MatrixDriverRequestHandle<'m, T>
74where
75    T: FromMatrixDriverResponse,
76{
77    pub(crate) fn new(request_meta: &'m mut MatrixDriverRequestMeta) -> Self {
78        Self { request_meta, _phantom: PhantomData }
79    }
80
81    /// Setup a callback function that will be called once the Matrix driver has
82    /// processed the request.
83    pub(crate) fn add_response_handler(
84        self,
85        response_handler: impl FnOnce(Result<T, crate::Error>, &mut WidgetMachine) -> Vec<Action>
86        + Send
87        + 'static,
88    ) {
89        self.request_meta.response_fn = Some(Box::new(move |response, machine| {
90            if let Some(response_data) = response.map(T::from_response).transpose() {
91                response_handler(response_data, machine)
92            } else {
93                Vec::new()
94            }
95        }));
96    }
97}
98
99/// Represents a request that the widget API state machine can send.
100pub(crate) trait MatrixDriverRequest: Into<MatrixDriverRequestData> {
101    type Response: FromMatrixDriverResponse;
102}
103
104pub(crate) trait FromMatrixDriverResponse: Sized {
105    fn from_response(_: MatrixDriverResponse) -> Option<Self>;
106}
107
108impl<T> FromMatrixDriverResponse for T
109where
110    MatrixDriverResponse: TryInto<T>,
111{
112    fn from_response(response: MatrixDriverResponse) -> Option<Self> {
113        // Delegates to the existing TryInto implementation
114        response.try_into().ok()
115    }
116}
117
118/// Ask the client (capability provider) to acquire given capabilities
119/// from the user. The client must eventually respond with granted capabilities.
120#[derive(Clone, Debug)]
121pub(crate) struct AcquireCapabilities {
122    pub(crate) desired_capabilities: Capabilities,
123}
124
125impl From<AcquireCapabilities> for MatrixDriverRequestData {
126    fn from(value: AcquireCapabilities) -> Self {
127        MatrixDriverRequestData::AcquireCapabilities(value)
128    }
129}
130
131impl MatrixDriverRequest for AcquireCapabilities {
132    type Response = Capabilities;
133}
134
135impl FromMatrixDriverResponse for Capabilities {
136    fn from_response(ev: MatrixDriverResponse) -> Option<Self> {
137        match ev {
138            MatrixDriverResponse::CapabilitiesAcquired(response) => Some(response),
139            _ => {
140                error!("bug in MatrixDriver, received wrong event response");
141                None
142            }
143        }
144    }
145}
146
147/// Request open ID from the Matrix client.
148#[derive(Debug)]
149pub(crate) struct RequestOpenId;
150
151impl From<RequestOpenId> for MatrixDriverRequestData {
152    fn from(_: RequestOpenId) -> Self {
153        MatrixDriverRequestData::GetOpenId
154    }
155}
156
157impl MatrixDriverRequest for RequestOpenId {
158    type Response = request_openid_token::v3::Response;
159}
160
161impl FromMatrixDriverResponse for request_openid_token::v3::Response {
162    fn from_response(ev: MatrixDriverResponse) -> Option<Self> {
163        match ev {
164            MatrixDriverResponse::OpenIdReceived(response) => Some(response),
165            _ => {
166                error!("bug in MatrixDriver, received wrong event response");
167                None
168            }
169        }
170    }
171}
172
173/// Ask the client to read Matrix events that correspond to the given
174/// description and return a list of events as a response.
175#[derive(Clone, Debug)]
176pub(crate) struct ReadEventsRequest {
177    /// The event type to read.
178    // TODO: This wants to be `MessageLikeEventType`` but we need a type which supports `as_str()`
179    // as soon as ruma supports `as_str()` on `MessageLikeEventType` we can use it here.
180    pub(crate) event_type: String,
181
182    /// The `state_key` to read. If None, this will read events regardless of
183    /// whether they are state events. If `Some(Any)`, this will only read state
184    /// events of the given type. If set to a specific state key, this will only
185    /// read state events of the given type matching that state key.
186    pub(crate) state_key: Option<StateKeySelector>,
187
188    /// The maximum number of events to return.
189    pub(crate) limit: u32,
190}
191
192impl From<ReadEventsRequest> for MatrixDriverRequestData {
193    fn from(value: ReadEventsRequest) -> Self {
194        MatrixDriverRequestData::ReadEvents(value)
195    }
196}
197
198impl MatrixDriverRequest for ReadEventsRequest {
199    type Response = Vec<Raw<AnyTimelineEvent>>;
200}
201
202impl FromMatrixDriverResponse for Vec<Raw<AnyTimelineEvent>> {
203    fn from_response(ev: MatrixDriverResponse) -> Option<Self> {
204        match ev {
205            MatrixDriverResponse::EventsRead(response) => Some(response),
206            _ => {
207                error!("bug in MatrixDriver, received wrong event response");
208                None
209            }
210        }
211    }
212}
213
214/// Ask the client to read Matrix room state entries corresponding to the given
215/// description and return a list of state events as a response.
216#[derive(Clone, Debug)]
217pub(crate) struct ReadStateRequest {
218    /// The event type to read.
219    // TODO: This wants to be `TimelineEventType` but we need a type which supports `as_str()`
220    // as soon as ruma supports `as_str()` on `TimelineEventType` we can use it here.
221    pub(crate) event_type: String,
222
223    /// The `state_key` to read, or `Any` to receive any/all room state entries
224    /// of the given type, regardless of their `state_key`.
225    pub(crate) state_key: StateKeySelector,
226}
227
228impl From<ReadStateRequest> for MatrixDriverRequestData {
229    fn from(value: ReadStateRequest) -> Self {
230        MatrixDriverRequestData::ReadState(value)
231    }
232}
233
234impl MatrixDriverRequest for ReadStateRequest {
235    type Response = Vec<Raw<AnyStateEvent>>;
236}
237
238impl FromMatrixDriverResponse for Vec<Raw<AnyStateEvent>> {
239    fn from_response(ev: MatrixDriverResponse) -> Option<Self> {
240        match ev {
241            MatrixDriverResponse::StateRead(response) => Some(response),
242            _ => {
243                error!("bug in MatrixDriver, received wrong event response");
244                None
245            }
246        }
247    }
248}
249
250/// Ask the client to send Matrix event that corresponds to the given
251/// description and returns an event ID (or a delay ID,
252/// see [MSC4140](https://github.com/matrix-org/matrix-spec-proposals/pull/4140)) as a response.
253#[derive(Clone, Debug, Deserialize)]
254pub(crate) struct SendEventRequest {
255    /// The type of the event.
256    // TODO: This wants to be `TimelineEventType` but we need a type which supports `as_str()`
257    // as soon as ruma supports `as_str()` on `TimelineEventType` we can use it here.
258    #[serde(rename = "type")]
259    pub(crate) event_type: String,
260    /// State key of an event (if it's a state event).
261    pub(crate) state_key: Option<String>,
262    /// Raw content of an event.
263    pub(crate) content: Box<RawJsonValue>,
264    /// The optional delay (in ms) to send the event at.
265    /// If provided, the response will contain a delay_id instead of a event_id.
266    /// Defined by [MSC4157](https://github.com/matrix-org/matrix-spec-proposals/pull/4157)
267    pub(crate) delay: Option<u64>,
268}
269
270impl From<SendEventRequest> for MatrixDriverRequestData {
271    fn from(value: SendEventRequest) -> Self {
272        MatrixDriverRequestData::SendEvent(value)
273    }
274}
275
276impl MatrixDriverRequest for SendEventRequest {
277    type Response = SendEventResponse;
278}
279
280impl FromMatrixDriverResponse for SendEventResponse {
281    fn from_response(ev: MatrixDriverResponse) -> Option<Self> {
282        match ev {
283            MatrixDriverResponse::EventSent(response) => Some(response),
284            _ => {
285                error!("bug in MatrixDriver, received wrong event response");
286                None
287            }
288        }
289    }
290}
291
292/// Ask the client to send a to-device message that corresponds to the given
293/// description.
294#[derive(Clone, Debug, Deserialize)]
295pub(crate) struct SendToDeviceRequest {
296    /// The type of the to-device message.
297    #[serde(rename = "type")]
298    pub(crate) event_type: String,
299    /// The messages to be sent.
300    /// They are organized in a map of user ID -> device ID -> content like the
301    /// cs api request.
302    pub(crate) messages:
303        BTreeMap<OwnedUserId, BTreeMap<DeviceIdOrAllDevices, Raw<AnyToDeviceEventContent>>>,
304}
305
306impl From<SendToDeviceRequest> for MatrixDriverRequestData {
307    fn from(value: SendToDeviceRequest) -> Self {
308        MatrixDriverRequestData::SendToDeviceEvent(value)
309    }
310}
311
312impl MatrixDriverRequest for SendToDeviceRequest {
313    type Response = SendToDeviceEventResponse;
314}
315
316/// Ask the client to send a UpdateDelayedEventRequest with the given `delay_id`
317/// and `action`. Defined by [MSC4157](https://github.com/matrix-org/matrix-spec-proposals/pull/4157)
318#[derive(Deserialize, Debug, Clone)]
319pub(crate) struct UpdateDelayedEventRequest {
320    pub(crate) action: update_delayed_event::unstable::UpdateAction,
321    pub(crate) delay_id: String,
322}
323
324impl From<UpdateDelayedEventRequest> for MatrixDriverRequestData {
325    fn from(value: UpdateDelayedEventRequest) -> Self {
326        MatrixDriverRequestData::UpdateDelayedEvent(value)
327    }
328}
329
330impl MatrixDriverRequest for UpdateDelayedEventRequest {
331    type Response = update_delayed_event::unstable::Response;
332}
333
334impl FromMatrixDriverResponse for update_delayed_event::unstable::Response {
335    fn from_response(ev: MatrixDriverResponse) -> Option<Self> {
336        match ev {
337            MatrixDriverResponse::DelayedEventUpdated(response) => Some(response),
338            _ => {
339                error!("bug in MatrixDriver, received wrong event response");
340                None
341            }
342        }
343    }
344}
345
346#[derive(Deserialize, Debug, Clone)]
347pub(crate) struct DownloadFileRequest {
348    // The MXC url of the file to download.
349    pub(crate) content_uri: OwnedMxcUri,
350}
351
352impl MatrixDriverRequest for DownloadFileRequest {
353    type Response = DownloadFileResponse;
354}
355
356impl From<DownloadFileRequest> for MatrixDriverRequestData {
357    fn from(req: DownloadFileRequest) -> Self {
358        MatrixDriverRequestData::DownloadFile(req)
359    }
360}