From 7458eac9312b4f97122f7609bbade2607c04c3a5 Mon Sep 17 00:00:00 2001 From: Joscha Date: Fri, 11 Feb 2022 20:32:27 +0100 Subject: [PATCH] Add essential packets --- cove-core/src/id.rs | 11 +++- cove-core/src/lib.rs | 41 ++------------- cove-core/src/macros.rs | 57 +++++++++++++++++--- cove-core/src/message.rs | 16 +++--- cove-core/src/packets.rs | 110 +++++++++++++++++++++++++++++++++++++++ cove-core/src/user.rs | 10 ++++ 6 files changed, 190 insertions(+), 55 deletions(-) create mode 100644 cove-core/src/packets.rs create mode 100644 cove-core/src/user.rs diff --git a/cove-core/src/id.rs b/cove-core/src/id.rs index 76b63e6..95112c2 100644 --- a/cove-core/src/id.rs +++ b/cove-core/src/id.rs @@ -4,13 +4,15 @@ use hex::ToHex; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; +use crate::macros::id_alias; + // TODO Use base64 representation instead #[derive(Debug, Clone, Copy, Deserialize, Serialize)] -pub struct Id(#[serde(with = "hex")] [u8; 32]); +struct Id(#[serde(with = "hex")] [u8; 32]); impl Id { - pub fn of(str: &str) -> Self { + fn of(str: &str) -> Self { let mut hasher = Sha256::new(); hasher.update(str); Self(hasher.finalize().into()) @@ -22,3 +24,8 @@ impl fmt::Display for Id { write!(f, "{}", self.0.encode_hex::()) } } + +// Prevent misuse of one id as another by only making the aliases public. +id_alias!(MessageId); +id_alias!(SessionId); +id_alias!(Identity); diff --git a/cove-core/src/lib.rs b/cove-core/src/lib.rs index dac9ef7..c2bdeb4 100644 --- a/cove-core/src/lib.rs +++ b/cove-core/src/lib.rs @@ -1,44 +1,9 @@ mod id; mod macros; mod message; - -use serde::{Deserialize, Serialize}; +pub mod packets; +mod user; pub use self::id::*; -use self::macros::packets; pub use self::message::*; - -#[derive(Debug, Deserialize, Serialize)] -pub struct HelloCmd { - pub id: Id, - pub name: String, -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(tag = "type")] -pub enum HelloRpl { - Success { id: Id }, - InvalidName { reason: String }, - NameAlreadyUsed, -} - -// Create a Cmd enum for all commands and a Rpl enum for all replies, as well as -// TryFrom impls for the individual command and reply structs. -packets! { - Hello(HelloCmd, HelloRpl), -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(tag = "type")] -pub enum Packet { - Cmd { - id: u64, - #[serde(flatten)] - cmd: Cmd, - }, - Rpl { - id: u64, - #[serde(flatten)] - rpl: Rpl, - }, -} +pub use self::user::*; diff --git a/cove-core/src/macros.rs b/cove-core/src/macros.rs index aa9b737..cd21ea8 100644 --- a/cove-core/src/macros.rs +++ b/cove-core/src/macros.rs @@ -1,9 +1,36 @@ +// Use `pub(crate) use ` to make a macro importable from elsewhere. +// See https://stackoverflow.com/a/31749071 + +macro_rules! id_alias { + ($name:ident) => { + #[derive(Debug, Clone, Copy, Deserialize, Serialize)] + pub struct $name(Id); + + impl $name { + pub fn of(str: &str) -> Self { + Self(Id::of(str)) + } + } + + impl fmt::Display for $name { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } + } + }; +} + +pub(crate) use id_alias; + macro_rules! packets { - ( $( $name:ident($cmd:ident, $rpl:ident), )* ) => { + ( + $( cmd $cmdName:ident($cmd:ident, $rpl:ident), )* // Commands with reply + $( ntf $ntfName:ident($ntf:ident), )* // Notifications + ) => { #[derive(Debug, Deserialize, Serialize)] #[serde(tag = "name", content = "data")] pub enum Cmd { - $( $name($cmd), )* + $( $cmdName($cmd), )* } $( @@ -11,7 +38,7 @@ macro_rules! packets { type Error = (); fn try_from(cmd: Cmd) -> Result { match cmd { - Cmd::$name(val) => Ok(val), + Cmd::$cmdName(val) => Ok(val), _ => Err(()), } } @@ -21,7 +48,7 @@ macro_rules! packets { #[derive(Debug, Deserialize, Serialize)] #[serde(tag = "name", content = "data")] pub enum Rpl { - $( $name($rpl), )* + $( $cmdName($rpl), )* } $( @@ -29,7 +56,25 @@ macro_rules! packets { type Error = (); fn try_from(rpl: Rpl) -> Result { match rpl { - Rpl::$name(val) => Ok(val), + Rpl::$cmdName(val) => Ok(val), + _ => Err(()), + } + } + } + )* + + #[derive(Debug, Deserialize, Serialize)] + #[serde(tag = "name", content = "data")] + pub enum Ntf { + $( $ntfName($ntf), )* + } + + $( + impl std::convert::TryFrom for $ntf { + type Error = (); + fn try_from(ntf: Ntf) -> Result { + match ntf { + Ntf::$ntfName(val) => Ok(val), _ => Err(()), } } @@ -38,6 +83,4 @@ macro_rules! packets { }; } -// Make macro importable from elsewhere -// See https://stackoverflow.com/a/31749071 pub(crate) use packets; diff --git a/cove-core/src/message.rs b/cove-core/src/message.rs index f683b01..5751d91 100644 --- a/cove-core/src/message.rs +++ b/cove-core/src/message.rs @@ -1,18 +1,18 @@ use serde::{Deserialize, Serialize}; -use crate::Id; +use crate::{ Identity, MessageId}; #[derive(Debug, Deserialize, Serialize)] pub struct Message { - pub pred: Option, - pub parent: Option, - pub identity: Id, + pub pred: Option, + pub parent: Option, + pub identity: Identity, pub nick: String, pub content: String, } impl Message { - pub fn id(&self) -> Id { + pub fn id(&self) -> MessageId { let pred = match self.pred { Some(id) => format!("{id}"), None => "none".to_string(), @@ -22,9 +22,9 @@ impl Message { None => "none".to_string(), }; let identity = self.identity; - let nick = Id::of(&self.nick); - let content = Id::of(&self.content); + let nick = MessageId::of(&self.nick); + let content = MessageId::of(&self.content); let str = format!("message {pred} {parent} {identity} {nick} {content}"); - Id::of(&str) + MessageId::of(&str) } } diff --git a/cove-core/src/packets.rs b/cove-core/src/packets.rs new file mode 100644 index 0000000..da6e36e --- /dev/null +++ b/cove-core/src/packets.rs @@ -0,0 +1,110 @@ +use serde::{Deserialize, Serialize}; + +use crate::macros::packets; +use crate::{Message, MessageId, User}; + +#[derive(Debug, Deserialize, Serialize)] +pub struct HelloCmd { + pub nick: String, + pub identity: String, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(tag = "type")] +pub enum HelloRpl { + Success { + you: User, + others: Vec, + last_message: MessageId, + }, + NickTooLong, + IdentityTooLong, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct NickCmd { + pub nick: String, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(tag = "type")] +pub enum NickRpl { + Success { you: User }, + NickTooLong, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct SendCmd { + pub parent: Option, + pub nick: Option, + pub content: String, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(tag = "type")] +pub enum SendRpl { + Success { message: Message }, + NickTooLong, + ContentTooLong, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct WhoCmd; + +#[derive(Debug, Deserialize, Serialize)] +pub struct WhoRpl { + you: User, + others: Vec, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct JoinNtf { + user: User, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct NickNtf { + user: User, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct PartNtf { + user: User, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct SendNtf { + message: Message, +} + +// Create a Cmd enum for all commands, a Rpl enum for all replies and a Ntf enum +// for all notifications, as well as TryFrom impls for the individual structs. +packets! { + cmd Hello(HelloCmd, HelloRpl), + cmd Nick(NickCmd, NickRpl), + cmd Send(SendCmd, SendRpl), + cmd Who(WhoCmd, WhoRpl), + ntf Join(JoinNtf), + ntf Nick(NickNtf), + ntf Part(PartNtf), + ntf Send(SendNtf), +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(tag = "type")] +pub enum Packet { + Cmd { + id: u64, + #[serde(flatten)] + cmd: Cmd, + }, + Rpl { + id: u64, + #[serde(flatten)] + rpl: Rpl, + }, + Ntf { + #[serde(flatten)] + ntf: Ntf, + }, +} diff --git a/cove-core/src/user.rs b/cove-core/src/user.rs new file mode 100644 index 0000000..e3e0aa1 --- /dev/null +++ b/cove-core/src/user.rs @@ -0,0 +1,10 @@ +use serde::{Deserialize, Serialize}; + +use crate::{Identity, SessionId}; + +#[derive(Debug, Deserialize, Serialize)] +pub struct User { + nick: String, + identity: Identity, + sid: SessionId, +}