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    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};
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
64/// A handle to a pending `toWidget` request.
65pub(crate) struct MatrixDriverRequestHandle<'m, T> {
66    request_meta: &'m mut MatrixDriverRequestMeta,
67    _phantom: PhantomData<fn() -> T>,
68}
69
70impl<'m, T> MatrixDriverRequestHandle<'m, T>
71where
72    T: FromMatrixDriverResponse,
73{
74    pub(crate) fn new(request_meta: &'m mut MatrixDriverRequestMeta) -> Self {
75        Self { request_meta, _phantom: PhantomData }
76    }
77
78    /// Setup a callback function that will be called once the Matrix driver has
79    /// processed the request.
80    pub(crate) fn add_response_handler(
81        self,
82        response_handler: impl FnOnce(Result<T, crate::Error>, &mut WidgetMachine) -> Vec<Action>
83        + Send
84        + 'static,
85    ) {
86        self.request_meta.response_fn = Some(Box::new(move |response, machine| {
87            if let Some(response_data) = response.map(T::from_response).transpose() {
88                response_handler(response_data, machine)
89            } else {
90                Vec::new()
91            }
92        }));
93    }
94}
95
96/// Represents a request that the widget API state machine can send.
97pub(crate) trait MatrixDriverRequest: Into<MatrixDriverRequestData> {
98    type Response: FromMatrixDriverResponse;
99}
100
101pub(crate) trait FromMatrixDriverResponse: Sized {
102    fn from_response(_: MatrixDriverResponse) -> Option<Self>;
103}
104
105impl<T> FromMatrixDriverResponse for T
106where
107    MatrixDriverResponse: TryInto<T>,
108{
109    fn from_response(response: MatrixDriverResponse) -> Option<Self> {
110        // Delegates to the existing TryInto implementation
111        response.try_into().ok()
112    }
113}
114
115/// Ask the client (capability provider) to acquire given capabilities
116/// from the user. The client must eventually respond with granted capabilities.
117#[derive(Clone, Debug)]
118pub(crate) struct AcquireCapabilities {
119    pub(crate) desired_capabilities: Capabilities,
120}
121
122impl From<AcquireCapabilities> for MatrixDriverRequestData {
123    fn from(value: AcquireCapabilities) -> Self {
124        MatrixDriverRequestData::AcquireCapabilities(value)
125    }
126}
127
128impl MatrixDriverRequest for AcquireCapabilities {
129    type Response = Capabilities;
130}
131
132impl FromMatrixDriverResponse for Capabilities {
133    fn from_response(ev: MatrixDriverResponse) -> Option<Self> {
134        match ev {
135            MatrixDriverResponse::CapabilitiesAcquired(response) => Some(response),
136            _ => {
137                error!("bug in MatrixDriver, received wrong event response");
138                None
139            }
140        }
141    }
142}
143
144/// Request open ID from the Matrix client.
145#[derive(Debug)]
146pub(crate) struct RequestOpenId;
147
148impl From<RequestOpenId> for MatrixDriverRequestData {
149    fn from(_: RequestOpenId) -> Self {
150        MatrixDriverRequestData::GetOpenId
151    }
152}
153
154impl MatrixDriverRequest for RequestOpenId {
155    type Response = request_openid_token::v3::Response;
156}
157
158impl FromMatrixDriverResponse for request_openid_token::v3::Response {
159    fn from_response(ev: MatrixDriverResponse) -> Option<Self> {
160        match ev {
161            MatrixDriverResponse::OpenIdReceived(response) => Some(response),
162            _ => {
163                error!("bug in MatrixDriver, received wrong event response");
164                None
165            }
166        }
167    }
168}
169
170/// Ask the client to read Matrix events that correspond to the given
171/// description and return a list of events as a response.
172#[derive(Clone, Debug)]
173pub(crate) struct ReadEventsRequest {
174    /// The event type to read.
175    // TODO: This wants to be `MessageLikeEventType`` but we need a type which supports `as_str()`
176    // as soon as ruma supports `as_str()` on `MessageLikeEventType` we can use it here.
177    pub(crate) event_type: String,
178
179    /// The `state_key` to read. If None, this will read events regardless of
180    /// whether they are state events. If `Some(Any)`, this will only read state
181    /// events of the given type. If set to a specific state key, this will only
182    /// read state events of the given type matching that state key.
183    pub(crate) state_key: Option<StateKeySelector>,
184
185    /// The maximum number of events to return.
186    pub(crate) limit: u32,
187}
188
189impl From<ReadEventsRequest> for MatrixDriverRequestData {
190    fn from(value: ReadEventsRequest) -> Self {
191        MatrixDriverRequestData::ReadEvents(value)
192    }
193}
194
195impl MatrixDriverRequest for ReadEventsRequest {
196    type Response = Vec<Raw<AnyTimelineEvent>>;
197}
198
199impl FromMatrixDriverResponse for Vec<Raw<AnyTimelineEvent>> {
200    fn from_response(ev: MatrixDriverResponse) -> Option<Self> {
201        match ev {
202            MatrixDriverResponse::EventsRead(response) => Some(response),
203            _ => {
204                error!("bug in MatrixDriver, received wrong event response");
205                None
206            }
207        }
208    }
209}
210
211/// Ask the client to read Matrix room state entries corresponding to the given
212/// description and return a list of state events as a response.
213#[derive(Clone, Debug)]
214pub(crate) struct ReadStateRequest {
215    /// The event type to read.
216    // TODO: This wants to be `TimelineEventType` but we need a type which supports `as_str()`
217    // as soon as ruma supports `as_str()` on `TimelineEventType` we can use it here.
218    pub(crate) event_type: String,
219
220    /// The `state_key` to read, or `Any` to receive any/all room state entries
221    /// of the given type, regardless of their `state_key`.
222    pub(crate) state_key: StateKeySelector,
223}
224
225impl From<ReadStateRequest> for MatrixDriverRequestData {
226    fn from(value: ReadStateRequest) -> Self {
227        MatrixDriverRequestData::ReadState(value)
228    }
229}
230
231impl MatrixDriverRequest for ReadStateRequest {
232    type Response = Vec<Raw<AnyStateEvent>>;
233}
234
235impl FromMatrixDriverResponse for Vec<Raw<AnyStateEvent>> {
236    fn from_response(ev: MatrixDriverResponse) -> Option<Self> {
237        match ev {
238            MatrixDriverResponse::StateRead(response) => Some(response),
239            _ => {
240                error!("bug in MatrixDriver, received wrong event response");
241                None
242            }
243        }
244    }
245}
246
247/// Ask the client to send Matrix event that corresponds to the given
248/// description and returns an event ID (or a delay ID,
249/// see [MSC4140](https://github.com/matrix-org/matrix-spec-proposals/pull/4140)) as a response.
250#[derive(Clone, Debug, Deserialize)]
251pub(crate) struct SendEventRequest {
252    /// The type of the event.
253    // TODO: This wants to be `TimelineEventType` but we need a type which supports `as_str()`
254    // as soon as ruma supports `as_str()` on `TimelineEventType` we can use it here.
255    #[serde(rename = "type")]
256    pub(crate) event_type: String,
257    /// State key of an event (if it's a state event).
258    pub(crate) state_key: Option<String>,
259    /// Raw content of an event.
260    pub(crate) content: Box<RawJsonValue>,
261    /// The optional delay (in ms) to send the event at.
262    /// If provided, the response will contain a delay_id instead of a event_id.
263    /// Defined by [MSC4157](https://github.com/matrix-org/matrix-spec-proposals/pull/4157)
264    pub(crate) delay: Option<u64>,
265}
266
267impl From<SendEventRequest> for MatrixDriverRequestData {
268    fn from(value: SendEventRequest) -> Self {
269        MatrixDriverRequestData::SendEvent(value)
270    }
271}
272
273impl MatrixDriverRequest for SendEventRequest {
274    type Response = SendEventResponse;
275}
276
277impl FromMatrixDriverResponse for SendEventResponse {
278    fn from_response(ev: MatrixDriverResponse) -> Option<Self> {
279        match ev {
280            MatrixDriverResponse::EventSent(response) => Some(response),
281            _ => {
282                error!("bug in MatrixDriver, received wrong event response");
283                None
284            }
285        }
286    }
287}
288
289/// Ask the client to send a to-device message that corresponds to the given
290/// description.
291#[derive(Clone, Debug, Deserialize)]
292pub(crate) struct SendToDeviceRequest {
293    /// The type of the to-device message.
294    #[serde(rename = "type")]
295    pub(crate) event_type: String,
296    /// The messages to be sent.
297    /// They are organized in a map of user ID -> device ID -> content like the
298    /// cs api request.
299    pub(crate) messages:
300        BTreeMap<OwnedUserId, BTreeMap<DeviceIdOrAllDevices, Raw<AnyToDeviceEventContent>>>,
301}
302
303impl From<SendToDeviceRequest> for MatrixDriverRequestData {
304    fn from(value: SendToDeviceRequest) -> Self {
305        MatrixDriverRequestData::SendToDeviceEvent(value)
306    }
307}
308
309impl MatrixDriverRequest for SendToDeviceRequest {
310    type Response = SendToDeviceEventResponse;
311}
312
313/// Ask the client to send a UpdateDelayedEventRequest with the given `delay_id`
314/// and `action`. Defined by [MSC4157](https://github.com/matrix-org/matrix-spec-proposals/pull/4157)
315#[derive(Deserialize, Debug, Clone)]
316pub(crate) struct UpdateDelayedEventRequest {
317    pub(crate) action: update_delayed_event::unstable::UpdateAction,
318    pub(crate) delay_id: String,
319}
320
321impl From<UpdateDelayedEventRequest> for MatrixDriverRequestData {
322    fn from(value: UpdateDelayedEventRequest) -> Self {
323        MatrixDriverRequestData::UpdateDelayedEvent(value)
324    }
325}
326
327impl MatrixDriverRequest for UpdateDelayedEventRequest {
328    type Response = update_delayed_event::unstable::Response;
329}
330
331impl FromMatrixDriverResponse for update_delayed_event::unstable::Response {
332    fn from_response(ev: MatrixDriverResponse) -> Option<Self> {
333        match ev {
334            MatrixDriverResponse::DelayedEventUpdated(response) => Some(response),
335            _ => {
336                error!("bug in MatrixDriver, received wrong event response");
337                None
338            }
339        }
340    }
341}