diff --git a/ruma-client-api/src/r0/state/send_state_event_for_empty_key.rs b/ruma-client-api/src/r0/state/send_state_event_for_empty_key.rs index 0ab429f9..1fb87852 100644 --- a/ruma-client-api/src/r0/state/send_state_event_for_empty_key.rs +++ b/ruma-client-api/src/r0/state/send_state_event_for_empty_key.rs @@ -1,40 +1,189 @@ //! [PUT /_matrix/client/r0/rooms/{roomId}/state/{eventType}](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-rooms-roomid-state-eventtype) -use ruma_api::ruma_api; -use ruma_events::EventType; +use std::convert::TryFrom; + +use ruma_api::{ + error::{ + FromHttpRequestError, FromHttpResponseError, IntoHttpError, RequestDeserializationError, + ResponseDeserializationError, ServerError, + }, + EndpointError, Metadata, Outgoing, +}; +use ruma_events::{AnyStateEventContent, EventContent as _}; use ruma_identifiers::{EventId, RoomId}; +use serde::{Deserialize, Serialize}; use serde_json::value::RawValue as RawJsonValue; -ruma_api! { - metadata: { - description: "Send a state event to a room associated with the empty state key.", - method: PUT, - name: "create_state_event_for_empty_key", - path: "/_matrix/client/r0/rooms/:room_id/state/:event_type", - rate_limited: false, - requires_authentication: true, - } +/// Data for a request to the `send_state_event_for_empty_key` API endpoint. +/// +/// Send a state event to a room associated with the empty state key. +#[derive(Clone, Debug, Outgoing)] +#[non_exhaustive] +#[incoming_no_deserialize] +pub struct Request<'a> { + /// The room to set the state in. + pub room_id: &'a RoomId, - request: { - /// The room to set the state in. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The type of event to send. - #[ruma_api(path)] - pub event_type: EventType, - - /// The event's content. The type for this field will be updated in a - /// future release, until then you can create a value using - /// `serde_json::value::to_raw_value`. - #[ruma_api(body)] - pub data: Box, - } - - response: { - /// A unique identifier for the event. - pub event_id: EventId, - } - - error: crate::Error + /// The event content to send. + pub content: &'a AnyStateEventContent, +} + +impl<'a> Request<'a> { + /// Creates a new `Request` with the given room id and event content. + pub fn new(room_id: &'a RoomId, content: &'a AnyStateEventContent) -> Self { + Self { room_id, content } + } +} + +/// Data in the response from the `send_state_event_for_empty_key` API endpoint. +#[derive(Clone, Debug, Outgoing)] +#[non_exhaustive] +#[incoming_no_deserialize] +pub struct Response { + /// A unique identifier for the event. + pub event_id: EventId, +} + +impl Response { + /// Creates a new `Response` with the given event id. + pub fn new(event_id: EventId) -> Self { + Self { event_id } + } +} + +const METADATA: Metadata = Metadata { + description: "Send a state event to a room associated with the empty state key.", + method: http::Method::PUT, + name: "send_state_event_for_empty_key", + path: "/_matrix/client/r0/rooms/:room_id/state/:event_type", + rate_limited: false, + requires_authentication: true, +}; + +impl TryFrom>> for IncomingRequest { + type Error = FromHttpRequestError; + + fn try_from(request: http::Request>) -> Result { + let path_segments: Vec<&str> = request.uri().path()[1..].split('/').collect(); + + let room_id = { + let decoded = + match percent_encoding::percent_decode(path_segments[4].as_bytes()).decode_utf8() { + Ok(val) => val, + Err(err) => return Err(RequestDeserializationError::new(err, request).into()), + }; + + match RoomId::try_from(&*decoded) { + Ok(val) => val, + Err(err) => return Err(RequestDeserializationError::new(err, request).into()), + } + }; + + let content = { + let request_body: Box = + match serde_json::from_slice(request.body().as_slice()) { + Ok(val) => val, + Err(err) => return Err(RequestDeserializationError::new(err, request).into()), + }; + + let event_type = { + match percent_encoding::percent_decode(path_segments[6].as_bytes()).decode_utf8() { + Ok(val) => val, + Err(err) => return Err(RequestDeserializationError::new(err, request).into()), + } + }; + + match AnyStateEventContent::from_parts(&event_type, request_body) { + Ok(content) => content, + Err(err) => return Err(RequestDeserializationError::new(err, request).into()), + } + }; + + Ok(Self { room_id, content }) + } +} + +/// Data in the response body. +#[derive(Debug, Deserialize, Serialize)] +struct ResponseBody { + /// A unique identifier for the event. + event_id: EventId, +} + +impl TryFrom for http::Response> { + type Error = IntoHttpError; + + fn try_from(response: Response) -> Result { + let response = http::Response::builder() + .header(http::header::CONTENT_TYPE, "application/json") + .body(serde_json::to_vec(&ResponseBody { event_id: response.event_id })?) + .unwrap(); + + Ok(response) + } +} + +impl TryFrom>> for Response { + type Error = FromHttpResponseError; + + fn try_from(response: http::Response>) -> Result { + if response.status().as_u16() < 400 { + let response_body: ResponseBody = + match serde_json::from_slice(response.body().as_slice()) { + Ok(val) => val, + Err(err) => return Err(ResponseDeserializationError::new(err, response).into()), + }; + + Ok(Self { event_id: response_body.event_id }) + } else { + match ::try_from_response(response) { + Ok(err) => Err(ServerError::Known(err).into()), + Err(response_err) => Err(ServerError::Unknown(response_err).into()), + } + } + } +} + +impl<'a> ruma_api::OutgoingRequest for Request<'a> { + type EndpointError = crate::Error; + type IncomingResponse = Response; + + /// Metadata for the `send_message_event` endpoint. + const METADATA: Metadata = METADATA; + + fn try_into_http_request( + self, + base_url: &str, + access_token: Option<&str>, + ) -> Result>, IntoHttpError> { + use http::header::{HeaderValue, AUTHORIZATION}; + use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; + + let http_request = http::Request::builder() + .method(http::Method::PUT) + .uri(format!( + "{}/_matrix/client/r0/rooms/{}/state/{}", + base_url.strip_suffix("/").unwrap_or(base_url), + utf8_percent_encode(self.room_id.as_str(), NON_ALPHANUMERIC), + utf8_percent_encode(self.content.event_type(), NON_ALPHANUMERIC), + )) + .header( + AUTHORIZATION, + HeaderValue::from_str(&format!( + "Bearer {}", + access_token.ok_or(IntoHttpError::NeedsAuthentication)? + ))?, + ) + .body(serde_json::to_vec(&self.content)?)?; + + Ok(http_request) + } +} + +impl ruma_api::IncomingRequest for IncomingRequest { + type EndpointError = crate::Error; + type OutgoingResponse = Response; + + /// Metadata for the `send_message_event` endpoint. + const METADATA: Metadata = METADATA; }