diff --git a/Cargo.lock b/Cargo.lock index d4b2512..ad25c33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -761,6 +761,7 @@ dependencies = [ "hyper 1.0.0-rc.3", "lazy_static", "nom", + "nonempty", "prometheus", "quick-xml", "regex", @@ -887,6 +888,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonempty" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeaf4ad7403de93e699c191202f017118df734d3850b01e13a3a8b2e6953d3c9" + [[package]] name = "nu-ansi-term" version = "0.46.0" diff --git a/Cargo.toml b/Cargo.toml index 7c72368..7b855e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ prometheus = { version = "0.13.3", default-features = false } regex = "1.7.1" lazy_static = "1.4.0" nom = "7.1.3" +nonempty = "0.8.1" tokio-rustls = "0.24.1" rustls-pemfile = "1.0.2" quick-xml = { version = "0.30.0", features = ["async-tokio"] } diff --git a/src/core/player.rs b/src/core/player.rs index 3457a88..1533941 100644 --- a/src/core/player.rs +++ b/src/core/player.rs @@ -302,7 +302,7 @@ impl Player { player_id, connections: AnonTable::new(), my_rooms: HashMap::new(), - banned_from: HashSet::from([RoomId::from("empty").unwrap()]), + banned_from: HashSet::from([RoomId::from("Empty").unwrap()]), rx, handle, rooms, diff --git a/src/projections/irc/mod.rs b/src/projections/irc/mod.rs index fc490fd..43e580b 100644 --- a/src/projections/irc/mod.rs +++ b/src/projections/irc/mod.rs @@ -2,6 +2,8 @@ use std::collections::HashMap; use std::net::SocketAddr; use futures_util::future::join_all; +use nonempty::nonempty; +use nonempty::NonEmpty; use prometheus::{IntCounter, IntGauge, Registry as MetricsRegistry}; use serde::Deserialize; use tokio::io::{AsyncBufReadExt, AsyncWrite, AsyncWriteExt, BufReader, BufWriter}; @@ -16,6 +18,7 @@ use crate::prelude::*; use crate::protos::irc::client::{client_message, ClientMessage}; use crate::protos::irc::server::{AwayStatus, ServerMessage, ServerMessageBody}; use crate::protos::irc::{Chan, Recipient}; +use crate::protos::irc::user::PrefixedNick; use crate::util::Terminator; #[cfg(test)] @@ -666,22 +669,16 @@ async fn produce_on_join_cmd_messages( } .write_async(writer) .await?; - let mut members: String = if let Some(head) = room_info.members.first() { - head.as_inner().clone() - } else { - user.nickname.clone() - }.as_ref().into(); - for i in &room_info.members[1..] { - members.push(' '); - members.push_str(i.as_inner()); - } + let prefixed_members: Vec = room_info.members.iter().map(|member| PrefixedNick::from_player_id(member.clone())).collect(); + let non_empty_members: NonEmpty = NonEmpty::from_vec(prefixed_members).unwrap_or(nonempty![PrefixedNick::from_str(user.nickname.clone())]); + ServerMessage { tags: vec![], sender: Some(config.server_name.clone()), body: ServerMessageBody::N353NamesReply { client: user.nickname.clone(), chan: chan.clone(), - members: members.into(), + members: non_empty_members.into(), }, } .write_async(writer) diff --git a/src/protos/irc/mod.rs b/src/protos/irc/mod.rs index 4d65efd..0997a79 100644 --- a/src/protos/irc/mod.rs +++ b/src/protos/irc/mod.rs @@ -1,6 +1,7 @@ //! Client-to-Server IRC protocol. pub mod client; pub mod server; +pub mod user; use std::io::Result; use crate::prelude::Str; diff --git a/src/protos/irc/server.rs b/src/protos/irc/server.rs index b2efcd4..194c756 100644 --- a/src/protos/irc/server.rs +++ b/src/protos/irc/server.rs @@ -1,7 +1,9 @@ +use nonempty::NonEmpty; use tokio::io::AsyncWrite; use tokio::io::AsyncWriteExt; use super::*; +use crate::protos::irc::user::PrefixedNick; /// Server-to-client message. #[derive(Clone, Debug, PartialEq, Eq)] @@ -121,7 +123,7 @@ pub enum ServerMessageBody { N353NamesReply { client: Str, chan: Chan, - members: Str, // TODO make this a non-empty list with prefixes + members: NonEmpty, }, N366NamesReplyEnd { client: Str, @@ -284,7 +286,11 @@ impl ServerMessageBody { writer.write_all(b" = ").await?; chan.write_async(writer).await?; writer.write_all(b" :").await?; - writer.write_all(members.as_bytes()).await?; + for member in members { + writer.write_all(member.prefix.to_string().as_bytes()).await?; + writer.write_all(member.nick.as_bytes()).await?; + writer.write_all(b" ").await?; + } } ServerMessageBody::N366NamesReplyEnd { client, chan } => { writer.write_all(b"366 ").await?; diff --git a/src/protos/irc/user.rs b/src/protos/irc/user.rs new file mode 100644 index 0000000..e426812 --- /dev/null +++ b/src/protos/irc/user.rs @@ -0,0 +1,32 @@ +use super::*; +use std::fmt; +use crate::core::player::PlayerId; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Prefix { + Empty, +} + +impl fmt::Display for Prefix { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Prefix::Empty => write!(f, ""), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PrefixedNick { + pub prefix: Prefix, + pub nick: Str, +} + +impl PrefixedNick { + pub fn from_str(nick: Str) -> PrefixedNick { + PrefixedNick { prefix: Prefix::Empty, nick } + } + + pub fn from_player_id(id: PlayerId) -> PrefixedNick { + PrefixedNick { prefix: Prefix::Empty, nick: id.into_inner() } + } +} \ No newline at end of file diff --git a/src/util/table.rs b/src/util/table.rs index 2d93866..92d2ad7 100644 --- a/src/util/table.rs +++ b/src/util/table.rs @@ -21,7 +21,7 @@ impl AnonTable { pub fn insert(&mut self, value: V) -> Key { let id = self.next; self.next += 1; - self.inner.insert(id, value); // should be always empty + self.inner.insert(id, value); // should be always Empty Key(id) }