mirror of
https://github.com/Blah-IM/blahrs.git
synced 2025-07-07 06:35:34 +00:00
save: add utoipa for OAPI generation
This commit is contained in:
parent
71c5f038fa
commit
acaf0f955a
11 changed files with 212 additions and 610 deletions
|
@ -6,6 +6,11 @@ edition = "2021"
|
|||
[features]
|
||||
default = []
|
||||
unsafe_use_mock_instant_for_testing = ["dep:mock_instant"]
|
||||
utoipa = ["dep:utoipa"]
|
||||
|
||||
[[example]]
|
||||
name = "openapi"
|
||||
required-features = ["utoipa"]
|
||||
|
||||
[[bench]]
|
||||
name = "crypto_ops"
|
||||
|
@ -26,6 +31,7 @@ serde_json = "1"
|
|||
serde_with = "3"
|
||||
thiserror = "1"
|
||||
url = { version = "2", features = ["serde"] }
|
||||
utoipa = { workspace = true, optional = true, features = ["url"] } # Generics support.
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.5"
|
||||
|
|
8
blah-types/examples/openapi.rs
Normal file
8
blah-types/examples/openapi.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
#![expect(clippy::print_stdout, reason = "allowed to dump OAPI")]
|
||||
|
||||
fn main() {
|
||||
let json = blah_types::openapi()
|
||||
.to_pretty_json()
|
||||
.expect("serialization cannot fail");
|
||||
println!("{json}");
|
||||
}
|
|
@ -11,6 +11,7 @@ use rand::RngCore;
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct UserKey {
|
||||
pub id_key: PubKey,
|
||||
pub act_key: PubKey,
|
||||
|
@ -18,6 +19,7 @@ pub struct UserKey {
|
|||
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema), schema(value_type = String))]
|
||||
pub struct PubKey(#[serde(with = "hex::serde")] pub [u8; PUBLIC_KEY_LENGTH]);
|
||||
|
||||
impl FromStr for PubKey {
|
||||
|
@ -55,14 +57,17 @@ impl From<&VerifyingKey> for PubKey {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Signed<T> {
|
||||
#[serde(with = "hex::serde")]
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub sig: [u8; SIGNATURE_LENGTH],
|
||||
pub signee: Signee<T>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Signee<T> {
|
||||
pub nonce: u32,
|
||||
|
|
|
@ -12,6 +12,7 @@ use crate::{PubKey, Signed};
|
|||
/// User identity description structure.
|
||||
// TODO: Revise and shrink duplicates (pubkey fields).
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct UserIdentityDesc {
|
||||
/// User primary identity key, only for signing action keys.
|
||||
pub id_key: PubKey,
|
||||
|
@ -90,6 +91,7 @@ impl UserIdentityDesc {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename = "user_act_key")]
|
||||
pub struct UserActKeyDesc {
|
||||
pub act_key: PubKey,
|
||||
|
@ -98,6 +100,7 @@ pub struct UserActKeyDesc {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename = "user_profile")]
|
||||
pub struct UserProfile {
|
||||
pub preferred_chat_server_urls: Vec<Url>,
|
||||
|
@ -105,6 +108,7 @@ pub struct UserProfile {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema), schema(value_type = Url))]
|
||||
#[serde(try_from = "Url")]
|
||||
pub struct IdUrl(Url);
|
||||
|
||||
|
|
|
@ -10,3 +10,46 @@ pub mod crypto;
|
|||
pub mod identity;
|
||||
pub mod msg;
|
||||
pub mod server;
|
||||
|
||||
#[cfg(feature = "utoipa")]
|
||||
pub fn openapi() -> utoipa::openapi::OpenApi {
|
||||
use utoipa::OpenApi;
|
||||
|
||||
#[derive(OpenApi)]
|
||||
#[openapi(components(schemas(
|
||||
crypto::Signed::<msg::AuthPayload>,
|
||||
crypto::Signed::<msg::ChatPayload>,
|
||||
crypto::Signed::<msg::CreateRoomPayload>,
|
||||
crypto::Signed::<msg::DeleteRoomPayload>,
|
||||
crypto::Signed::<msg::RoomAdminPayload>,
|
||||
crypto::Signed::<msg::UserRegisterPayload>,
|
||||
identity::UserIdentityDesc,
|
||||
identity::UserProfile,
|
||||
msg::AuthPayload,
|
||||
msg::ChatPayload,
|
||||
msg::DeleteRoomPayload,
|
||||
msg::RichText,
|
||||
msg::RoomAdminPayload,
|
||||
msg::UserRegisterPayload,
|
||||
server::ClientEvent,
|
||||
server::ErrorResponse,
|
||||
server::ErrorResponseWithChallenge,
|
||||
server::RoomList,
|
||||
server::RoomMetadata,
|
||||
server::RoomMsgs,
|
||||
server::ServerCapabilities,
|
||||
server::ServerEvent,
|
||||
server::ServerMetadata,
|
||||
)))]
|
||||
struct ApiDoc;
|
||||
|
||||
ApiDoc::openapi()
|
||||
}
|
||||
|
||||
#[cfg(feature = "utoipa")]
|
||||
#[test]
|
||||
#[expect(clippy::print_stdout, reason = "allowed in tests")]
|
||||
fn test_openapi() {
|
||||
let json = crate::openapi().to_pretty_json().unwrap();
|
||||
println!("{json}");
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ use crate::{PubKey, Signed};
|
|||
/// It's currently serialized as a string for JavaScript's convenience.
|
||||
#[serde_as]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema), schema(value_type = String))]
|
||||
#[serde(transparent)]
|
||||
pub struct Id(#[serde_as(as = "DisplayFromStr")] pub i64);
|
||||
|
||||
|
@ -39,6 +40,7 @@ impl Id {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct WithMsgId<T> {
|
||||
pub cid: Id,
|
||||
#[serde(flatten)]
|
||||
|
@ -53,17 +55,20 @@ impl<T> WithMsgId<T> {
|
|||
|
||||
/// Register a user on a chat server.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename = "user_register")]
|
||||
pub struct UserRegisterPayload {
|
||||
pub server_url: Url,
|
||||
pub id_url: IdUrl,
|
||||
pub id_key: PubKey,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub challenge: Option<UserRegisterChallengeResponse>,
|
||||
}
|
||||
|
||||
/// The server-specific challenge data for registration.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum UserRegisterChallengeResponse {
|
||||
/// Proof of work challenge containing the same nonce from server challenge request.
|
||||
|
@ -73,6 +78,7 @@ pub enum UserRegisterChallengeResponse {
|
|||
|
||||
// FIXME: `deny_unknown_fields` breaks this.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename = "chat")]
|
||||
pub struct ChatPayload {
|
||||
pub rich_text: RichText,
|
||||
|
@ -81,6 +87,7 @@ pub struct ChatPayload {
|
|||
|
||||
/// Ref: <https://github.com/Blah-IM/Weblah/blob/a3fa0f265af54c846f8d65f42aa4409c8dba9dd9/src/lib/richText.ts>
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema), schema(value_type = Vec<RichTextPieceRaw>))]
|
||||
#[serde(transparent)]
|
||||
pub struct RichText(pub Vec<RichTextPiece>);
|
||||
|
||||
|
@ -103,8 +110,9 @@ impl Serialize for RichTextPiece {
|
|||
}
|
||||
}
|
||||
|
||||
/// The protocol representation of `RichTextPiece`.
|
||||
/// The representation on wire of `RichTextPiece`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(untagged)]
|
||||
enum RichTextPieceRaw {
|
||||
Text(String),
|
||||
|
@ -148,6 +156,7 @@ impl<'de> Deserialize<'de> for RichText {
|
|||
|
||||
// TODO: This protocol format is quite large. Could use bitflags for database.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct TextAttrs {
|
||||
#[serde(default, rename = "b", skip_serializing_if = "is_default")]
|
||||
pub bold: bool,
|
||||
|
@ -159,6 +168,7 @@ pub struct TextAttrs {
|
|||
pub italic: bool,
|
||||
// TODO: Should we validate and/or filter the URL.
|
||||
#[serde(default, skip_serializing_if = "is_default")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub link: Option<String>,
|
||||
#[serde(default, rename = "s", skip_serializing_if = "is_default")]
|
||||
pub strike: bool,
|
||||
|
@ -271,6 +281,7 @@ pub type SignedChatMsg = Signed<ChatPayload>;
|
|||
pub type SignedChatMsgWithId = WithMsgId<SignedChatMsg>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ")]
|
||||
pub enum CreateRoomPayload {
|
||||
#[serde(rename = "create_room")]
|
||||
|
@ -281,6 +292,7 @@ pub enum CreateRoomPayload {
|
|||
|
||||
/// Multi-user room.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct CreateGroup {
|
||||
pub attrs: RoomAttrs,
|
||||
pub title: String,
|
||||
|
@ -288,11 +300,13 @@ pub struct CreateGroup {
|
|||
|
||||
/// Peer-to-peer chat room with exactly two symmetric users.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct CreatePeerChat {
|
||||
pub peer: PubKey,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename = "delete_room")]
|
||||
pub struct DeleteRoomPayload {
|
||||
pub room: Id,
|
||||
|
@ -302,6 +316,7 @@ pub struct DeleteRoomPayload {
|
|||
/// 1. Sorted by userkeys.
|
||||
/// 2. No duplicated users.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(try_from = "Vec<RoomMember>")]
|
||||
pub struct RoomMemberList(pub Vec<RoomMember>);
|
||||
|
||||
|
@ -327,6 +342,7 @@ impl TryFrom<Vec<RoomMember>> for RoomMemberList {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct RoomMember {
|
||||
pub permission: MemberPermission,
|
||||
pub user: PubKey,
|
||||
|
@ -336,11 +352,13 @@ pub struct RoomMember {
|
|||
///
|
||||
/// TODO: Should we use JWT here instead?
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename = "auth")]
|
||||
pub struct AuthPayload {}
|
||||
|
||||
// FIXME: Remove this.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
// `typ` is provided by `RoomAdminOp`.
|
||||
pub struct RoomAdminPayload {
|
||||
#[serde(flatten)]
|
||||
|
@ -348,6 +366,7 @@ pub struct RoomAdminPayload {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename_all = "snake_case", rename = "remove_member")]
|
||||
pub struct RemoveMemberPayload {
|
||||
pub room: Id,
|
||||
|
@ -357,6 +376,7 @@ pub struct RemoveMemberPayload {
|
|||
|
||||
// TODO: Maybe disallow adding other user without consent?
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename_all = "snake_case", rename = "add_member")]
|
||||
pub struct AddMemberPayload {
|
||||
pub room: Id,
|
||||
|
@ -365,6 +385,7 @@ pub struct AddMemberPayload {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename_all = "snake_case", rename = "update_member")]
|
||||
pub struct UpdateMemberPayload {
|
||||
pub room: Id,
|
||||
|
@ -373,6 +394,7 @@ pub struct UpdateMemberPayload {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(untagged)]
|
||||
pub enum RoomAdminOp {
|
||||
AddMember(AddMemberPayload),
|
||||
|
@ -382,6 +404,7 @@ pub enum RoomAdminOp {
|
|||
bitflags::bitflags! {
|
||||
/// TODO: Is this a really all about permission, or is a generic `UserFlags`?
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema), schema(value_type = i32))]
|
||||
pub struct ServerPermission: i32 {
|
||||
const CREATE_ROOM = 1 << 0;
|
||||
|
||||
|
@ -391,6 +414,7 @@ bitflags::bitflags! {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema), schema(value_type = i32))]
|
||||
pub struct MemberPermission: i32 {
|
||||
const POST_CHAT = 1 << 0;
|
||||
const ADD_MEMBER = 1 << 1;
|
||||
|
@ -410,6 +434,7 @@ bitflags::bitflags! {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema), schema(value_type = i32))]
|
||||
pub struct RoomAttrs: i32 {
|
||||
// NB. Used by schema.
|
||||
const PUBLIC_READABLE = 1 << 0;
|
||||
|
|
|
@ -10,6 +10,7 @@ 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<S = String> {
|
||||
/// The error object.
|
||||
pub error: ErrorObject<S>,
|
||||
|
@ -18,21 +19,27 @@ pub struct ErrorResponse<S = String> {
|
|||
/// 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<S = String> {
|
||||
/// The error object.
|
||||
pub error: ErrorObject<S>,
|
||||
|
||||
/// 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<UserRegisterChallenge>,
|
||||
}
|
||||
|
||||
/// The error object.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct ErrorObject<S = String> {
|
||||
/// 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,
|
||||
}
|
||||
|
||||
|
@ -50,16 +57,20 @@ impl<S: fmt::Display + fmt::Debug> std::error::Error for ErrorObject<S> {}
|
|||
/// 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 `<server-name>/<server-version>` 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<Url>,
|
||||
|
||||
/// The server capabilities set.
|
||||
|
@ -67,6 +78,7 @@ pub struct ServerMetadata {
|
|||
}
|
||||
|
||||
#[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,
|
||||
|
@ -74,6 +86,7 @@ pub struct ServerCapabilities {
|
|||
|
||||
/// 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.
|
||||
|
@ -86,67 +99,82 @@ pub enum UserRegisterChallenge {
|
|||
|
||||
/// 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<RoomMetadata>,
|
||||
/// 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<Id>,
|
||||
}
|
||||
|
||||
/// 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<String>,
|
||||
/// Room attributes.
|
||||
pub attrs: RoomAttrs,
|
||||
|
||||
// Extra information is only available for some APIs.
|
||||
/// The last message in the room.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub last_msg: Option<SignedChatMsgWithId>,
|
||||
/// The current user's last seen message's `cid`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub last_seen_cid: Option<Id>,
|
||||
/// 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(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub unseen_cnt: Option<u32>,
|
||||
/// The member permission of current user in the room, or `None` if it is not a member.
|
||||
/// Only available with authentication.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub member_permission: Option<MemberPermission>,
|
||||
/// The peer user, if this is a peer chat room.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub peer_user: Option<PubKey>,
|
||||
}
|
||||
|
||||
/// 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<SignedChatMsgWithId>,
|
||||
/// The skip-token to fetch the next page.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub skip_token: Option<Id>,
|
||||
}
|
||||
|
||||
/// 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<RoomMember>,
|
||||
/// 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<Id>,
|
||||
}
|
||||
|
||||
/// 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,
|
||||
|
@ -154,11 +182,13 @@ pub struct RoomMember {
|
|||
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<Id>,
|
||||
}
|
||||
|
||||
/// 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.
|
||||
|
@ -170,5 +200,6 @@ pub enum ServerEvent {
|
|||
|
||||
/// 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 {}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue