diff --git a/Cargo.lock b/Cargo.lock index 69c4337..cd81860 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 ddabb38..d7f28fc 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..cb3d319 100644 --- a/src/core/player.rs +++ b/src/core/player.rs @@ -27,7 +27,7 @@ use crate::{ /// Opaque player identifier. Cannot contain spaces, must be shorter than 32. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)] -pub struct PlayerId(Str); +pub struct PlayerId(pub Str); impl PlayerId { pub fn from(str: impl Into) -> Result { let bytes = str.into(); @@ -46,6 +46,22 @@ impl PlayerId { self.0 } } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Prefix(pub Str); + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PrefixedNick { + pub prefix: Prefix, + pub nick: PlayerId, +} +impl PrefixedNick { + pub fn fromPlayerId(id: PlayerId) -> Result { + Ok(PrefixedNick { prefix: Prefix(Str::from("")), nick: id } ) + } + pub fn fromStr(nick: Str) -> Result { + Ok(PrefixedNick { prefix: Prefix(Str::from("")), nick: PlayerId(nick) } ) + } +} #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ConnectionId(pub AnonKey); diff --git a/src/projections/irc/mod.rs b/src/projections/irc/mod.rs index fc490fd..37fcd42 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}; @@ -666,22 +668,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 prefixedMembers: Vec = room_info.members.iter().map(|member| PrefixedNick::fromPlayerId(member.clone()).unwrap()).collect(); + let nonEmptyMembers: NonEmpty = NonEmpty::from_vec(prefixedMembers).unwrap_or(nonempty![PrefixedNick::fromStr(user.nickname.clone()).unwrap()]); + ServerMessage { tags: vec![], sender: Some(config.server_name.clone()), body: ServerMessageBody::N353NamesReply { client: user.nickname.clone(), chan: chan.clone(), - members: members.into(), + members: nonEmptyMembers.into(), }, } .write_async(writer) diff --git a/src/protos/irc/server.rs b/src/protos/irc/server.rs index b2efcd4..47ca9ac 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::core::player::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, @@ -279,12 +281,20 @@ impl ServerMessageBody { chan, members, } => { + let mut membersTail: Vec = members.tail.clone(); + membersTail.insert(0, members.clone().head); + + let membersStr: Vec = membersTail.iter().map(| prefixedNick| { + let pref: String = prefixedNick.prefix.0.clone().to_string(); + let nick: String = prefixedNick.nick.0.clone().to_string(); + pref + &nick + }).collect(); writer.write_all(b"353 ").await?; writer.write_all(client.as_bytes()).await?; writer.write_all(b" = ").await?; chan.write_async(writer).await?; writer.write_all(b" :").await?; - writer.write_all(members.as_bytes()).await?; + writer.write_all(membersStr.join(", ").as_bytes()).await?; } ServerMessageBody::N366NamesReplyEnd { client, chan } => { writer.write_all(b"366 ").await?;