ruma-client-api 0.25.0

Types for the endpoints in the Matrix client-server API.
Documentation
//! `GET /_matrix/client/v1/summary/{roomIdOrAlias}`
//!
//! Returns a short description of the state of a room.

pub mod v1 {
    //! `v1` ([spec])
    //!
    //! [spec]: https://spec.matrix.org/v1.19/client-server-api/#get_matrixclientv1room_summaryroomidoralias

    use ruma_common::{
        OwnedRoomOrAliasId, OwnedServerName,
        api::{auth_scheme::AccessTokenOptional, request},
        metadata,
        room::RoomSummary,
    };
    use ruma_events::room::member::MembershipState;

    metadata! {
        method: GET,
        rate_limited: false,
        authentication: AccessTokenOptional,
        history: {
            unstable => "/_matrix/client/unstable/im.nheko.summary/rooms/{room_id_or_alias}/summary",
            1.15 => "/_matrix/client/v1/room_summary/{room_id_or_alias}",
        }
    }

    /// Request type for the `get_summary` endpoint.
    #[request]
    pub struct Request {
        /// Alias or ID of the room to be summarized.
        #[ruma_api(path)]
        pub room_id_or_alias: OwnedRoomOrAliasId,

        /// A list of servers the homeserver should attempt to use to peek at the room.
        ///
        /// Defaults to an empty `Vec`.
        #[serde(default, skip_serializing_if = "Vec::is_empty")]
        #[ruma_api(query)]
        pub via: Vec<OwnedServerName>,
    }

    impl Request {
        /// Creates a new `Request` with the given room or alias ID and via server names.
        pub fn new(room_id_or_alias: OwnedRoomOrAliasId, via: Vec<OwnedServerName>) -> Self {
            Self { room_id_or_alias, via }
        }
    }

    /// Response type for the `get_summary` endpoint.
    #[derive(Debug, Clone)]
    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
    pub struct Response {
        /// The summary of the room.
        pub summary: RoomSummary,

        /// The current membership of this user in the room.
        ///
        /// This field will not be present when called unauthenticated, but is required when called
        /// authenticated. It should be `leave` if the server doesn't know about the room, since
        /// for all other membership states the server would know about the room already.
        pub membership: Option<MembershipState>,
    }

    impl Response {
        /// Creates a new [`Response`] with the given summary.
        pub fn new(summary: RoomSummary) -> Self {
            Self { summary, membership: None }
        }
    }

    impl From<RoomSummary> for Response {
        fn from(value: RoomSummary) -> Self {
            Self::new(value)
        }
    }

    #[doc(hidden)]
    #[derive(ruma_common::serde::_FakeDeriveSerde)]
    #[cfg_attr(feature = "server", derive(serde::Serialize, ruma_common::api::OutgoingBodyJson))]
    pub struct ResponseBody {
        #[serde(flatten)]
        summary: RoomSummary,
        #[serde(skip_serializing_if = "Option::is_none")]
        membership: Option<MembershipState>,
    }

    #[cfg(feature = "client")]
    impl<'de> serde::Deserialize<'de> for ResponseBody {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: serde::Deserializer<'de>,
        {
            use ruma_common::serde::from_raw_json_value;
            use serde_json::value::RawValue as RawJsonValue;

            #[derive(serde::Deserialize)]
            struct ResponseBodyDeHelper {
                membership: Option<MembershipState>,
            }

            let json = Box::<RawJsonValue>::deserialize(deserializer)?;
            let summary = from_raw_json_value(&json)?;
            let membership = from_raw_json_value::<ResponseBodyDeHelper, _>(&json)?.membership;

            Ok(Self { membership, summary })
        }
    }

    #[cfg(feature = "server")]
    impl ruma_common::api::OutgoingResponse for Response {
        type Body = ResponseBody;

        fn try_into_http_response_inner(
            self,
        ) -> Result<http::Response<Self::Body>, ruma_common::api::error::IntoHttpError> {
            let Self { summary, membership } = self;

            http::Response::builder().body(ResponseBody { summary, membership }).map_err(Into::into)
        }
    }

    #[cfg(feature = "client")]
    impl ruma_common::api::IncomingResponse for Response {
        type EndpointError = ruma_common::api::error::Error;

        fn try_from_http_response_inner(
            response: http::Response<&[u8]>,
        ) -> Result<Self, ruma_common::api::error::DeserializationError> {
            let ResponseBody { summary, membership } = serde_json::from_slice(response.body())?;
            Ok(Self { summary, membership })
        }
    }
}

#[cfg(all(test, feature = "client"))]
mod tests {
    use ruma_common::api::IncomingResponseExt as _;
    use ruma_events::room::member::MembershipState;
    use serde_json::json;

    use super::v1::Response;

    #[test]
    fn deserialize_response() {
        let body = json!({
            "room_id": "!room:localhost",
            "num_joined_members": 5,
            "world_readable": false,
            "guest_can_join": false,
            "join_rule": "restricted",
            "allowed_room_ids": ["!otherroom:localhost"],
            "membership": "invite",
        })
        .to_string();
        let response = http::Response::new(body.as_bytes());
        let response = Response::try_from_http_response(response).unwrap();

        assert_eq!(response.summary.room_id, "!room:localhost");
        assert_eq!(response.membership, Some(MembershipState::Invite));
    }
}