Convert between Packet and individual packet structs

This commit is contained in:
Joscha 2022-06-22 10:47:26 +02:00
parent 21010fc48a
commit 49169a1b62
8 changed files with 341 additions and 187 deletions

View file

@ -15,6 +15,7 @@ parking_lot = "0.12.1"
rusqlite = { version = "0.27.0", features = ["chrono"] }
serde = { version = "1.0.137", features = ["derive"] }
serde_json = "1.0.81"
thiserror = "1.0.31"
tokio = { version = "1.19.2", features = ["full"] }
tokio-tungstenite = "0.17.1"
toss = { git = "https://github.com/Garmelon/toss.git", rev = "761519c1a7cdc950eab70fd6539c71bf22919a50" }

View file

@ -1,24 +1,13 @@
//! Models the euphoria API at <http://api.euphoria.io/>.
mod events;
mod packet;
mod room_cmds;
mod session_cmds;
mod types;
use serde::{Deserialize, Serialize};
use serde_json::Value;
pub use events::*;
pub use packet::*;
pub use room_cmds::*;
pub use session_cmds::*;
pub use types::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Packet {
pub id: Option<String>,
pub r#type: PacketType,
pub data: Option<Value>,
pub error: Option<String>,
pub throttled: Option<bool>,
pub throttled_reason: Option<String>,
}

View file

@ -2,7 +2,10 @@
use serde::{Deserialize, Serialize};
use super::{AuthOption, Message, PersonalAccountView, SessionView, Snowflake, Time, UserId};
use super::{
has_packet_type, AuthOption, HasPacketType, Message, PacketType, PersonalAccountView,
SessionView, Snowflake, Time, UserId,
};
/// Indicates that access to a room is denied.
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -17,6 +20,8 @@ pub struct BounceEvent {
pub ip: Option<String>,
}
has_packet_type!(BounceEvent);
/// Indicates that the session is being closed. The client will subsequently be
/// disconnected.
///
@ -28,6 +33,8 @@ pub struct DisconnectEvent {
pub reason: String,
}
has_packet_type!(DisconnectEvent);
/// Sent by the server to the client when a session is started.
///
/// It includes information about the client's authentication and associated
@ -51,10 +58,14 @@ pub struct HelloEvent {
pub version: String,
}
has_packet_type!(HelloEvent);
/// Indicates a session just joined the room.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JoinEvent(pub SessionView);
has_packet_type!(JoinEvent);
/// Sent to all sessions of an agent when that agent is logged in (except for
/// the session that issued the login command).
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -62,11 +73,15 @@ pub struct LoginEvent {
pub account_id: Snowflake,
}
has_packet_type!(LoginEvent);
/// Sent to all sessions of an agent when that agent is logged out (except for
/// the session that issued the logout command).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogoutEvent;
has_packet_type!(LogoutEvent);
/// Indicates some server-side event that impacts the presence of sessions in a
/// room.
///
@ -82,6 +97,8 @@ pub struct NetworkEvent {
pub server_era: String,
}
has_packet_type!(NetworkEvent);
/// Announces a nick change by another session in the room.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NickEvent {
@ -95,6 +112,8 @@ pub struct NickEvent {
pub to: String,
}
has_packet_type!(NickEvent);
/// Indicates that a message in the room has been modified or deleted.
///
/// If the client offers a user interface and the indicated message is currently
@ -110,10 +129,14 @@ pub struct EditMessageEvent {
pub message: Message,
}
has_packet_type!(EditMessageEvent);
/// Indicates a session just disconnected from the room.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PartEvent(pub SessionView);
has_packet_type!(PartEvent);
/// Represents a server-to-client ping.
///
/// The client should send back a ping-reply with the same value for the time
@ -127,6 +150,8 @@ pub struct PingEvent {
pub next: Time,
}
has_packet_type!(PingEvent);
/// Informs the client that another user wants to chat with them privately.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PmInitiateEvent {
@ -140,10 +165,14 @@ pub struct PmInitiateEvent {
pub pm_id: Snowflake,
}
has_packet_type!(PmInitiateEvent);
/// Indicates a message received by the room from another session.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SendEvent(pub Message);
has_packet_type!(SendEvent);
/// Indicates that a session has successfully joined a room.
///
/// It also offers a snapshot of the rooms state and recent history.
@ -168,3 +197,5 @@ pub struct SnapshotEvent {
/// If given, this room is for private chat with the given user.
pub pm_with_user_id: Option<String>,
}
has_packet_type!(SnapshotEvent);

View file

@ -0,0 +1,270 @@
use std::fmt;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json::Value;
/// The type of a packet.
///
/// Not all of these types have their corresponding data modeled as a struct.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum PacketType {
// Asynchronous events
/// See [`BounceEvent`](super::BounceEvent).
BounceEvent,
/// See [`DisconnectEvent`](super::DisconnectEvent).
DisconnectEvent,
/// See [`HelloEvent`](super::HelloEvent).
HelloEvent,
/// See [`JoinEvent`](super::JoinEvent).
JoinEvent,
/// See [`LoginEvent`](super::LoginEvent).
LoginEvent,
/// See [`LogoutEvent`](super::LogoutEvent).
LogoutEvent,
/// See [`NetworkEvent`](super::NetworkEvent).
NetworkEvent,
/// See [`NickEvent`](super::NickEvent).
NickEvent,
/// See [`EditMessageEvent`](super::EditMessageEvent).
EditMessageEvent,
/// See [`PartEvent`](super::PartEvent).
PartEvent,
/// See [`PingEvent`](super::PingEvent).
PingEvent,
/// See [`PmInitiateEvent`](super::PmInitiateEvent).
PmInitiateEvent,
/// See [`SendEvent`](super::SendEvent).
SendEvent,
/// See [`SnapshotEvent`](super::SnapshotEvent).
SnapshotEvent,
// Session commands
/// See [`Auth`](super::Auth).
Auth,
/// See [`AuthReply`](super::AuthReply).
AuthReply,
/// See [`Ping`](super::Ping).
Ping,
/// See [`PingReply`](super::PingReply).
PingReply,
// Chat room commands
/// See [`GetMessage`](super::GetMessage).
GetMessage,
/// See [`GetMessageReply`](super::GetMessageReply).
GetMessageReply,
/// See [`Log`](super::Log).
Log,
/// See [`LogReply`](super::LogReply).
LogReply,
/// See [`Nick`](super::Nick).
Nick,
/// See [`NickReply`](super::NickReply).
NickReply,
/// See [`PmInitiate`](super::PmInitiate).
PmInitiate,
/// See [`PmInitiateReply`](super::PmInitiateReply).
PmInitiateReply,
/// See [`Send`](super::Send).
Send,
/// See [`SendReply`](super::SendReply).
SendReply,
/// See [`Who`](super::Who).
Who,
/// See [`WhoReply`](super::WhoReply).
WhoReply,
// Account commands
/// Not implemented.
ChangeEmail,
/// Not implemented.
ChangeEmailReply,
/// Not implemented.
ChangeName,
/// Not implemented.
ChangeNameReply,
/// Not implemented.
ChangePassword,
/// Not implemented.
ChangePasswordReply,
/// Not implemented.
Login,
/// Not implemented.
LoginReply,
/// Not implemented.
Logout,
/// Not implemented.
LogoutReply,
/// Not implemented.
RegisterAccount,
/// Not implemented.
RegisterAccountReply,
/// Not implemented.
ResendVerificationEmail,
/// Not implemented.
ResendVerificationEmailReply,
/// Not implemented.
ResetPassword,
/// Not implemented.
ResetPasswordReply,
// Room host commands
/// Not implemented.
Ban,
/// Not implemented.
BanReply,
/// Not implemented.
EditMessage,
/// Not implemented.
EditMessageReply,
/// Not implemented.
GrantAccess,
/// Not implemented.
GrantAccessReply,
/// Not implemented.
GrantManager,
/// Not implemented.
GrantManagerReply,
/// Not implemented.
RevokeAccess,
/// Not implemented.
RevokeAccessReply,
/// Not implemented.
RevokeManager,
/// Not implemented.
RevokeManagerReply,
/// Not implemented.
Unban,
/// Not implemented.
UnbanReply,
// Staff commands
/// Not implemented.
StaffCreateRoom,
/// Not implemented.
StaffCreateRoomReply,
/// Not implemented.
StaffEnrollOtp,
/// Not implemented.
StaffEnrollOtpReply,
/// Not implemented.
StaffGrantManager,
/// Not implemented.
StaffGrantManagerReply,
/// Not implemented.
StaffInvade,
/// Not implemented.
StaffInvadeReply,
/// Not implemented.
StaffLockRoom,
/// Not implemented.
StaffLockRoomReply,
/// Not implemented.
StaffRevokeAccess,
/// Not implemented.
StaffRevokeAccessReply,
/// Not implemented.
StaffValidateOtp,
/// Not implemented.
StaffValidateOtpReply,
/// Not implemented.
UnlockStaffCapability,
/// Not implemented.
UnlockStaffCapabilityReply,
}
impl fmt::Display for PacketType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match serde_json::to_value(self) {
Ok(Value::String(s)) => write!(f, "{}", s),
_ => Err(fmt::Error),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Packet {
pub id: Option<String>,
pub r#type: PacketType,
pub data: Option<Value>,
pub error: Option<String>,
#[serde(default)]
pub throttled: bool,
pub throttled_reason: Option<String>,
}
pub trait HasPacketType {
fn packet_type() -> PacketType;
}
macro_rules! has_packet_type {
($name:ident) => {
impl HasPacketType for $name {
fn packet_type() -> PacketType {
PacketType::$name
}
}
};
}
pub(crate) use has_packet_type;
pub trait ToPacket {
fn to_packet(self, id: Option<String>) -> Packet;
}
impl<T: HasPacketType + Serialize> ToPacket for T {
fn to_packet(self, id: Option<String>) -> Packet {
Packet {
id,
r#type: Self::packet_type(),
data: Some(serde_json::to_value(self).expect("malformed packet")),
error: None,
throttled: false,
throttled_reason: None,
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum DecodeError {
#[error("incorrect packet type: expected {expected}, got {actual}")]
IncorrectType {
expected: PacketType,
actual: PacketType,
},
#[error("throttled: {0}")]
Throttled(String),
#[error("error: {0}")]
Error(String),
#[error("no data")]
NoData,
#[error("{0}")]
SerdeJson(#[from] serde_json::Error),
}
pub trait FromPacket: Sized {
fn from_packet(packet: Packet) -> Result<Self, DecodeError>;
}
impl<T: HasPacketType + DeserializeOwned> FromPacket for T {
fn from_packet(packet: Packet) -> Result<Self, DecodeError> {
if packet.r#type != Self::packet_type() {
Err(DecodeError::IncorrectType {
expected: Self::packet_type(),
actual: packet.r#type,
})
} else if packet.throttled {
let reason = packet
.throttled_reason
.unwrap_or_else(|| "no reason given".to_string());
Err(DecodeError::Throttled(reason))
} else if let Some(error) = packet.error {
Err(DecodeError::Error(error))
} else {
let data = packet.data.unwrap_or_default();
Ok(serde_json::from_value(data)?)
}
}
}

View file

@ -2,7 +2,7 @@
use serde::{Deserialize, Serialize};
use super::{Message, SessionView, Snowflake, UserId};
use super::{has_packet_type, HasPacketType, Message, PacketType, SessionView, Snowflake, UserId};
/// Retrieve the full content of a single message in the room.
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -11,10 +11,14 @@ pub struct GetMessage {
pub id: Snowflake,
}
has_packet_type!(GetMessage);
/// The message retrieved by [`GetMessage`].
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetMessageReply(pub Message);
has_packet_type!(GetMessageReply);
/// Request messages from the room's message log.
///
/// This can be used to supplement the log provided by snapshot-event (for
@ -27,6 +31,8 @@ pub struct Log {
pub before: Option<Snowflake>,
}
has_packet_type!(Log);
/// List of messages from the room's message log.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogReply {
@ -36,6 +42,8 @@ pub struct LogReply {
pub before: Option<Snowflake>,
}
has_packet_type!(LogReply);
/// Set the name you present to the room.
///
/// This name applies to all messages sent during this session, until the nick
@ -46,6 +54,8 @@ pub struct Nick {
pub name: String,
}
has_packet_type!(Nick);
/// Confirms the [`Nick`] command.
///
/// Returns the session's former and new names (the server may modify the
@ -62,6 +72,8 @@ pub struct NickReply {
pub to: String,
}
has_packet_type!(NickReply);
/// Constructs a virtual room for private messaging between the client and the
/// given [`UserId`].
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -70,6 +82,8 @@ pub struct PmInitiate {
pub user_id: UserId,
}
has_packet_type!(PmInitiate);
/// Provides the PMID for the requested private messaging room.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PmInitiateReply {
@ -79,6 +93,8 @@ pub struct PmInitiateReply {
pub to_nick: String,
}
has_packet_type!(PmInitiateReply);
/// Send a message to a room.
///
/// The session must be successfully joined with the room. This message will be
@ -98,19 +114,27 @@ pub struct Send {
pub parent: Option<Snowflake>,
}
has_packet_type!(Send);
/// The message that was sent.
///
/// this includes the message id, which was populated by the server.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SendReply(pub Message);
has_packet_type!(SendReply);
/// Request a list of sessions currently joined in the room.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Who;
has_packet_type!(Who);
/// Lists the sessions currently joined in the room.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WhoReply {
/// A list of session views.
listing: Vec<SessionView>,
}
has_packet_type!(WhoReply);

View file

@ -2,7 +2,7 @@
use serde::{Deserialize, Serialize};
use super::{AuthOption, Time};
use super::{has_packet_type, AuthOption, HasPacketType, PacketType, Time};
/// Attempt to join a private room.
///
@ -16,6 +16,8 @@ pub struct Auth {
pub passcode: Option<String>,
}
has_packet_type!(Auth);
/// Reports whether the [`Auth`] command succeeded.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthReply {
@ -25,6 +27,8 @@ pub struct AuthReply {
pub reason: Option<String>,
}
has_packet_type!(AuthReply);
/// Initiate a client-to-server ping.
///
/// The server will send back a [`PingReply`] with the same timestamp as soon as
@ -35,9 +39,13 @@ pub struct Ping {
pub time: Time,
}
has_packet_type!(Ping);
/// Response to a [`Ping`] command or [`PingEvent`](super::PingEvent).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PingReply {
/// The timestamp of the ping being replied to.
pub time: Option<Time>,
}
has_packet_type!(PingReply);

View file

@ -10,6 +10,7 @@ use std::fmt;
use chrono::{DateTime, Utc};
use serde::{de, ser, Deserialize, Serialize};
use serde_json::Value;
/// Describes an account and its preferred name.
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -61,177 +62,6 @@ pub struct Message {
pub truncated: bool,
}
/// The type of a packet.
///
/// Not all of these types have their corresponding data modeled as a struct.
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum PacketType {
// Asynchronous events
/// See [`BounceEvent`](super::BounceEvent).
BounceEvent,
/// See [`DisconnectEvent`](super::DisconnectEvent).
DisconnectEvent,
/// See [`HelloEvent`](super::HelloEvent).
HelloEvent,
/// See [`JoinEvent`](super::JoinEvent).
JoinEvent,
/// See [`LoginEvent`](super::LoginEvent).
LoginEvent,
/// See [`LogoutEvent`](super::LogoutEvent).
LogoutEvent,
/// See [`NetworkEvent`](super::NetworkEvent).
NetworkEvent,
/// See [`NickEvent`](super::NickEvent).
NickEvent,
/// See [`EditMessageEvent`](super::EditMessageEvent).
EditMessageEvent,
/// See [`PartEvent`](super::PartEvent).
PartEvent,
/// See [`PingEvent`](super::PingEvent).
PingEvent,
/// See [`PmInitiateEvent`](super::PmInitiateEvent).
PmInitiateEvent,
/// See [`SendEvent`](super::SendEvent).
SendEvent,
/// See [`SnapshotEvent`](super::SnapshotEvent).
SnapshotEvent,
// Session commands
/// See [`Auth`](super::Auth).
Auth,
/// See [`AuthReply`](super::AuthReply).
AuthReply,
/// See [`Ping`](super::Ping).
Ping,
/// See [`PingReply`](super::PingReply).
PingReply,
// Chat room commands
/// See [`GetMessage`](super::GetMessage).
GetMessage,
/// See [`GetMessageReply`](super::GetMessageReply).
GetMessageReply,
/// See [`Log`](super::Log).
Log,
/// See [`LogReply`](super::LogReply).
LogReply,
/// See [`Nick`](super::Nick).
Nick,
/// See [`NickReply`](super::NickReply).
NickReply,
/// See [`PmInitiate`](super::PmInitiate).
PmInitiate,
/// See [`PmInitiateReply`](super::PmInitiateReply).
PmInitiateReply,
/// See [`Send`](super::Send).
Send,
/// See [`SendReply`](super::SendReply).
SendReply,
/// See [`Who`](super::Who).
Who,
/// See [`WhoReply`](super::WhoReply).
WhoReply,
// Account commands
/// Not implemented.
ChangeEmail,
/// Not implemented.
ChangeEmailReply,
/// Not implemented.
ChangeName,
/// Not implemented.
ChangeNameReply,
/// Not implemented.
ChangePassword,
/// Not implemented.
ChangePasswordReply,
/// Not implemented.
Login,
/// Not implemented.
LoginReply,
/// Not implemented.
Logout,
/// Not implemented.
LogoutReply,
/// Not implemented.
RegisterAccount,
/// Not implemented.
RegisterAccountReply,
/// Not implemented.
ResendVerificationEmail,
/// Not implemented.
ResendVerificationEmailReply,
/// Not implemented.
ResetPassword,
/// Not implemented.
ResetPasswordReply,
// Room host commands
/// Not implemented.
Ban,
/// Not implemented.
BanReply,
/// Not implemented.
EditMessage,
/// Not implemented.
EditMessageReply,
/// Not implemented.
GrantAccess,
/// Not implemented.
GrantAccessReply,
/// Not implemented.
GrantManager,
/// Not implemented.
GrantManagerReply,
/// Not implemented.
RevokeAccess,
/// Not implemented.
RevokeAccessReply,
/// Not implemented.
RevokeManager,
/// Not implemented.
RevokeManagerReply,
/// Not implemented.
Unban,
/// Not implemented.
UnbanReply,
// Staff commands
/// Not implemented.
StaffCreateRoom,
/// Not implemented.
StaffCreateRoomReply,
/// Not implemented.
StaffEnrollOtp,
/// Not implemented.
StaffEnrollOtpReply,
/// Not implemented.
StaffGrantManager,
/// Not implemented.
StaffGrantManagerReply,
/// Not implemented.
StaffInvade,
/// Not implemented.
StaffInvadeReply,
/// Not implemented.
StaffLockRoom,
/// Not implemented.
StaffLockRoomReply,
/// Not implemented.
StaffRevokeAccess,
/// Not implemented.
StaffRevokeAccessReply,
/// Not implemented.
StaffValidateOtp,
/// Not implemented.
StaffValidateOtpReply,
/// Not implemented.
UnlockStaffCapability,
/// Not implemented.
UnlockStaffCapabilityReply,
}
/// Describes an account to its owner.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PersonalAccountView {