summary refs log tree commit diff
path: root/crates/messenger_common
diff options
context:
space:
mode:
authorSophie Forrest <git@sophieforrest.com>2024-08-30 23:13:20 +1200
committerSophie Forrest <git@sophieforrest.com>2024-08-30 23:13:44 +1200
commite3cb82a3b33bd2a2e49c58ce18d1258fb505869e (patch)
tree2375279182fb4f90f5c28560a08cda90591f608b /crates/messenger_common
chore: initial commit (codeberg upload) HEAD main
Diffstat (limited to '')
-rw-r--r--crates/messenger_common/Cargo.toml9
-rw-r--r--crates/messenger_common/src/client.rs15
-rw-r--r--crates/messenger_common/src/lib.rs77
-rw-r--r--crates/messenger_common/src/server.rs107
4 files changed, 208 insertions, 0 deletions
diff --git a/crates/messenger_common/Cargo.toml b/crates/messenger_common/Cargo.toml
new file mode 100644
index 0000000..d6b44d1
--- /dev/null
+++ b/crates/messenger_common/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "messenger_common"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+time = { features = ["serde-human-readable"], version = "0.3.25" }
+thiserror = "1.0.44"
+serde = { features = ["derive"], version = "1.0.183" }
diff --git a/crates/messenger_common/src/client.rs b/crates/messenger_common/src/client.rs
new file mode 100644
index 0000000..69a7fd6
--- /dev/null
+++ b/crates/messenger_common/src/client.rs
@@ -0,0 +1,15 @@
+//! Messages sent from the client to the server by the messenger.
+
+use serde::{Deserialize, Serialize};
+
+/// Represents the type of message being sent between the client and the server
+#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub enum MessageType {
+	/// Sent when a user joins the chat. Contains the username they wish to pick
+	SetUsername(String),
+
+	/// Sent when a user sends a message in the chat. Contains their message as
+	/// a string
+	UserMessage(String),
+}
diff --git a/crates/messenger_common/src/lib.rs b/crates/messenger_common/src/lib.rs
new file mode 100644
index 0000000..c05e870
--- /dev/null
+++ b/crates/messenger_common/src/lib.rs
@@ -0,0 +1,77 @@
+#![feature(custom_inner_attributes)]
+#![feature(lint_reasons)]
+#![feature(never_type)]
+#![feature(lazy_cell)]
+#![clippy::msrv = "1.69.0"]
+#![deny(clippy::nursery)]
+#![deny(clippy::pedantic)]
+#![deny(clippy::alloc_instead_of_core)]
+#![deny(clippy::as_underscore)]
+#![deny(clippy::clone_on_ref_ptr)]
+#![deny(clippy::create_dir)]
+#![warn(clippy::dbg_macro)]
+#![deny(clippy::default_numeric_fallback)]
+#![deny(clippy::default_union_representation)]
+#![deny(clippy::deref_by_slicing)]
+#![deny(clippy::else_if_without_else)]
+#![deny(clippy::empty_structs_with_brackets)]
+#![deny(clippy::exit)]
+#![deny(clippy::filetype_is_file)]
+#![deny(clippy::fn_to_numeric_cast)]
+#![deny(clippy::format_push_string)]
+#![deny(clippy::get_unwrap)]
+#![deny(clippy::if_then_some_else_none)]
+#![allow(
+	clippy::implicit_return,
+	reason = "returns should be done implicitly, not explicitly"
+)]
+#![deny(clippy::indexing_slicing)]
+#![deny(clippy::large_include_file)]
+#![deny(clippy::let_underscore_must_use)]
+#![deny(clippy::lossy_float_literal)]
+#![deny(clippy::map_err_ignore)]
+#![deny(clippy::mem_forget)]
+#![deny(clippy::missing_docs_in_private_items)]
+#![deny(clippy::missing_trait_methods)]
+#![deny(clippy::multiple_inherent_impl)]
+#![deny(clippy::needless_return)]
+#![deny(clippy::non_ascii_literal)]
+#![deny(clippy::panic_in_result_fn)]
+#![deny(clippy::pattern_type_mismatch)]
+#![deny(clippy::rc_buffer)]
+#![deny(clippy::rc_mutex)]
+#![deny(clippy::rest_pat_in_fully_bound_structs)]
+#![deny(clippy::same_name_method)]
+#![deny(clippy::separated_literal_suffix)]
+#![deny(clippy::str_to_string)]
+#![deny(clippy::string_add)]
+#![deny(clippy::string_slice)]
+#![deny(clippy::string_to_string)]
+#![allow(
+	clippy::tabs_in_doc_comments,
+	reason = "tabs are preferred for this project"
+)]
+#![deny(clippy::try_err)]
+#![deny(clippy::undocumented_unsafe_blocks)]
+#![deny(clippy::unnecessary_self_imports)]
+#![deny(clippy::unneeded_field_pattern)]
+#![deny(clippy::unwrap_in_result)]
+#![deny(clippy::unwrap_used)]
+#![warn(clippy::use_debug)]
+#![deny(clippy::verbose_file_reads)]
+#![deny(clippy::wildcard_dependencies)]
+#![deny(clippy::wildcard_enum_match_arm)]
+#![deny(clippy::missing_panics_doc)]
+#![deny(missing_copy_implementations)]
+#![deny(missing_debug_implementations)]
+#![deny(missing_docs)]
+#![deny(single_use_lifetimes)]
+#![deny(unsafe_code)]
+#![deny(unused)]
+
+//! # Messenger Common
+//!
+//! Exports common resources used by the messenger client and server.
+
+pub mod client;
+pub mod server;
diff --git a/crates/messenger_common/src/server.rs b/crates/messenger_common/src/server.rs
new file mode 100644
index 0000000..678d812
--- /dev/null
+++ b/crates/messenger_common/src/server.rs
@@ -0,0 +1,107 @@
+//! Messages sent from the server to the client by the messenger.
+
+use std::collections::HashSet;
+
+use serde::{Deserialize, Serialize};
+use thiserror::Error;
+
+/// Represents the type of message being sent between the client and the server
+#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub enum MessageType {
+	/// Sent to the client to indicate an error made by the client
+	Error(Error),
+
+	/// Sent to a client on the initial join, indicating the online users
+	OnlineUsers(HashSet<String>),
+
+	/// Sent when a user joins the chat. Contains their username
+	UserJoined(String),
+
+	/// Sent when a user disconnects from the chat. Contains their username
+	UserLeft(String),
+
+	/// Sent when a user sends a message in the chat. Contains their message
+	UserMessage(UserMessage),
+}
+
+/// Errors the server may respond with when performing operations
+#[derive(Clone, Debug, Deserialize, Eq, Error, PartialEq, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub enum Error {
+	/// Cannot process the sent message
+	#[error("server cannot process the received message")]
+	CannotProcess,
+
+	/// Message sent was invalid
+	#[error("server received invalid message")]
+	InvalidMessage,
+
+	/// Expected different message than received
+	#[error("server received unexpected message (expected {expected:?}, received {received:?}")]
+	UnexpectedMessage {
+		/// Message the server expected to receive
+		expected: crate::client::MessageType,
+
+		/// Message the server received
+		received: crate::client::MessageType,
+	},
+
+	/// Username chosen is not available
+	#[error("chosen username ({chosen_username}) has already been taken")]
+	UsernameNotAvailable {
+		/// Username that was chosen, but is not available
+		chosen_username: String,
+	},
+}
+
+/// Represents a message sent through a websocket
+#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct UserMessage {
+	/// Content of the message
+	content: String,
+
+	/// The name of the message sender
+	sender: String,
+
+	/// Timestamp indicating when this message was sent
+	#[serde(with = "time::serde::iso8601")]
+	timestamp: time::OffsetDateTime,
+}
+
+impl UserMessage {
+	/// Constructs a new instance of the [`UserMessage`].
+	#[must_use]
+	pub const fn new(content: String, sender: String, timestamp: time::OffsetDateTime) -> Self {
+		Self {
+			content,
+			sender,
+			timestamp,
+		}
+	}
+
+	/// Retrieves a reference to the content of the [`UserMessage`].
+	#[must_use]
+	pub const fn content(&self) -> &String {
+		&self.content
+	}
+
+	/// Retrieves a reference to the sender of the [`UserMessage`].
+	#[must_use]
+	pub const fn sender(&self) -> &String {
+		&self.sender
+	}
+
+	/// Retrieves a timestamp to the content of the [`UserMessage`].
+	#[must_use]
+	pub const fn timestamp(&self) -> &time::OffsetDateTime {
+		&self.timestamp
+	}
+}
+
+impl From<UserMessage> for MessageType {
+	fn from(val: UserMessage) -> Self {
+		Self::UserMessage(val)
+	}
+}