//! Data types and constants for Chat Server interaction. use std::fmt; use serde::{Deserialize, Serialize}; use url::Url; use crate::msg::{Id, MemberPermission, RoomAttrs, SignedChatMsgWithId}; use crate::PubKey; /// The response object returned as body on HTTP error status. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct ErrorResponse { /// The error object. pub error: ErrorObject, } /// The response object of `/_blah/user/me` endpoint on HTTP error status. /// It contains additional registration information. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct ErrorResponseWithChallenge { /// The error object. pub error: ErrorObject, /// The challenge metadata returned by the `/_blah/user/me` endpoint for registration. #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "utoipa", schema(nullable = false))] pub register_challenge: Option, } /// The error object. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct ErrorObject { /// A machine-readable error code string. #[cfg_attr(feature = "utoipa", schema(value_type = String, example = "user_not_found"))] pub code: S, /// A human-readable error message. #[cfg_attr(feature = "utoipa", schema(value_type = String, example = "the user does not exist"))] pub message: S, } impl fmt::Display for ErrorObject { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "api error ({}): {}", self.code, self.message) } } impl std::error::Error for ErrorObject {} /// Metadata about the version and capabilities of a Chat Server. /// /// It should be relatively stable and do not change very often. /// It may contains extra fields and clients should ignore them for future compatibility. /// Chat Servers can also include any custom fields here as long they have a `_` prefix. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct ServerMetadata { /// A server-defined version string indicating its implementation name and the version. /// /// It is expected to be in form `/` but not mandatory. #[cfg_attr(feature = "utoipa", schema(example = "blahd/0.0.1"))] pub server: String, /// The URL to the source code of the Chat Server. /// /// It is expected to be a public accessible maybe-compressed tarball link without /// access control. #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "utoipa", schema(nullable = false))] pub src_url: Option, /// The server capabilities set. pub capabilities: ServerCapabilities, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct ServerCapabilities { /// Whether registration is open to public. pub allow_public_register: bool, } /// Registration challenge information. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[serde(rename_all = "snake_case")] pub enum UserRegisterChallenge { /// Proof-of-work (PoW) challenge. Pow { nonce: u32, difficulty: u8 }, /// A catch-all unknown challenge type. #[serde(other, skip_serializing)] Unknown, } /// Response to list rooms. #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct RoomList { /// Result list of rooms. pub rooms: Vec, /// The skip-token to fetch the next page. #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "utoipa", schema(nullable = false))] pub skip_token: Option, } /// The metadata of a room. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct RoomMetadata { /// Room id. pub rid: Id, /// Plain text room title. None for peer chat. #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "utoipa", schema(nullable = false))] pub title: Option, /// Room attributes. pub attrs: RoomAttrs, // Extra information is only available for some APIs. /// The last message in the room. #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "utoipa", schema(nullable = false))] pub last_msg: Option, /// The current user's last seen message's `cid`. #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "utoipa", schema(nullable = false))] pub last_seen_cid: Option, /// The number of unseen messages, ie. the number of messages from `last_seen_cid` to /// `last_msg.cid`. /// This may or may not be a precise number. #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "utoipa", schema(nullable = false))] pub unseen_cnt: Option, /// The member permission of current user in the room, or `None` if it is not a member. /// Only available with authentication. #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "utoipa", schema(nullable = false))] pub member_permission: Option, /// The peer user, if this is a peer chat room. #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "utoipa", schema(nullable = false))] pub peer_user: Option, } /// Response to list room msgs. #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct RoomMsgs { /// Result list of msgs. pub msgs: Vec, /// The skip-token to fetch the next page. #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "utoipa", schema(nullable = false))] pub skip_token: Option, } /// Response to list room members. #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct RoomMemberList { /// Result list of members. pub members: Vec, /// The skip-token to fetch the next page. #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "utoipa", schema(nullable = false))] pub skip_token: Option, } /// The description of a room member. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct RoomMember { /// The identity key of the member user. pub id_key: PubKey, /// The user permission in the room. pub permission: MemberPermission, /// The user's last seen message `cid` in the room. #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "utoipa", schema(nullable = false))] pub last_seen_cid: Option, } /// A server-to-client event. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[serde(rename_all = "snake_case")] pub enum ServerEvent { /// A message from a joined room. Msg(SignedChatMsgWithId), /// The receiver is too slow to receive and some events and are dropped. // FIXME: Should we indefinitely buffer them or just disconnect the client instead? Lagged, } /// A client-to-server event. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[serde(rename_all = "snake_case")] pub enum ClientEvent {}