Create message log struct

This commit is contained in:
Sijmen 2023-11-09 18:17:25 +01:00
parent ad5d2f3b37
commit 8dddabc0e6
3 changed files with 165 additions and 138 deletions

21
src/irc_message.rs Normal file
View file

@ -0,0 +1,21 @@
pub enum IrcMessage {
Join {
nickname: String,
},
Part {
nickname: String,
reason: Option<String>,
},
Nick {
old: String,
new: String,
},
Quit {
nickname: String,
reason: Option<String>,
},
Privmsg {
nickname: String,
message: String,
},
}

View file

@ -1,5 +1,9 @@
mod irc_message;
mod message_log;
use crate::irc_message::IrcMessage;
use crate::message_log::MessageLog;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use futures::StreamExt; use futures::StreamExt;
@ -66,38 +70,6 @@ async fn main() -> Result<()> {
Ok(()) Ok(())
} }
enum IrcMessage {
Join {
nickname: String,
},
Part {
nickname: String,
reason: Option<String>,
},
Nick {
old: String,
new: String,
},
Quit {
nickname: String,
reason: Option<String>,
},
Privmsg {
nickname: String,
message: String,
},
}
struct Cri {
message_rx: RefCell<Option<UnboundedReceiver<irc::proto::Message>>>,
input_tx: RefCell<UnboundedSender<irc::proto::Message>>,
active_channel: Option<String>,
message_log: HashMap<Option<String>, Vec<IrcMessage>>,
input_value: String,
}
struct CriFlags { struct CriFlags {
pub message_rx: UnboundedReceiver<irc::proto::Message>, pub message_rx: UnboundedReceiver<irc::proto::Message>,
pub input_tx: UnboundedSender<irc::proto::Message>, pub input_tx: UnboundedSender<irc::proto::Message>,
@ -111,6 +83,16 @@ enum UiMessage {
HandleChannelPress(Option<String>), HandleChannelPress(Option<String>),
} }
struct Cri {
message_rx: RefCell<Option<UnboundedReceiver<irc::proto::Message>>>,
input_tx: RefCell<UnboundedSender<irc::proto::Message>>,
active_channel: Option<String>,
message_log: MessageLog,
input_value: String,
}
impl Application for Cri { impl Application for Cri {
type Executor = executor::Default; type Executor = executor::Default;
type Message = UiMessage; type Message = UiMessage;
@ -118,9 +100,6 @@ impl Application for Cri {
type Flags = CriFlags; type Flags = CriFlags;
fn new(flags: Self::Flags) -> (Self, iced::Command<Self::Message>) { fn new(flags: Self::Flags) -> (Self, iced::Command<Self::Message>) {
let mut message_log = HashMap::new();
message_log.insert(None, Vec::new());
( (
Self { Self {
message_rx: RefCell::new(Some(flags.message_rx)), message_rx: RefCell::new(Some(flags.message_rx)),
@ -128,7 +107,7 @@ impl Application for Cri {
active_channel: None, active_channel: None,
message_log, message_log: MessageLog::new(),
input_value: String::new(), input_value: String::new(),
}, },
iced::Command::none(), iced::Command::none(),
@ -140,74 +119,41 @@ impl Application for Cri {
} }
fn update(&mut self, message: Self::Message) -> iced::Command<Self::Message> { fn update(&mut self, message: Self::Message) -> iced::Command<Self::Message> {
use irc::proto::Command;
match message { match message {
UiMessage::IrcMessageReceived(message) => { UiMessage::IrcMessageReceived(message) => {
let message = *message;
// TODO use actual nickname // TODO use actual nickname
let source_nickname = message.source_nickname().unwrap_or("cri").to_string(); let source_nickname = message.source_nickname().unwrap_or("cri").to_string();
match &message.command { match &message.command {
irc::proto::Command::JOIN(chanlist, _, _) => { Command::JOIN(chanlist, _, _) => {
self.message_log.on_join(chanlist.clone(), &source_nickname);
}
Command::PART(chanlist, comment) => {
self.message_log.on_part(
chanlist.clone(),
&source_nickname,
comment.as_deref(),
);
}
Command::NICK(new) => {
self.message_log.on_nick(&source_nickname, new);
}
Command::QUIT(comment) => {
self.message_log self.message_log
.entry(Some(chanlist.to_string())) .on_quit(&source_nickname, comment.as_deref());
.or_insert_with(Vec::new)
.push(IrcMessage::Join {
nickname: source_nickname,
});
} }
irc::proto::Command::PART(chanlist, comment) => {
Command::PRIVMSG(msgtarget, content) => {
let channel = message.response_target().unwrap_or(msgtarget).to_string();
self.message_log self.message_log
.entry(Some(chanlist.to_string())) .on_privmsg(channel, &source_nickname, content);
.or_insert_with(Vec::new)
.push(IrcMessage::Part {
nickname: source_nickname,
reason: comment.clone(),
});
} }
irc::proto::Command::NICK(new) => {
let channels = self.message_log.keys().cloned().collect::<Vec<_>>();
for channel in channels {
if channel.is_some() {
self.message_log
.get_mut(&channel)
.unwrap()
.push(IrcMessage::Nick {
old: source_nickname.clone(),
new: new.to_string(),
});
}
}
}
irc::proto::Command::QUIT(comment) => {
let channels = self.message_log.keys().cloned().collect::<Vec<_>>();
for channel in channels {
if channel.is_some() {
self.message_log
.get_mut(&channel)
.unwrap()
.push(IrcMessage::Quit {
nickname: source_nickname.clone(),
reason: comment.clone(),
});
}
}
}
irc::proto::Command::PRIVMSG(msgtarget, content) => {
let channel = message.response_target().unwrap_or(msgtarget);
self.message_log
.entry(Some(channel.to_string()))
.or_insert_with(Vec::new)
.push(IrcMessage::Privmsg {
nickname: source_nickname,
message: content.to_string(),
})
}
_ => (), _ => (),
} }
} }
@ -220,13 +166,12 @@ impl Application for Cri {
); );
let message: irc::proto::Message = command.into(); let message: irc::proto::Message = command.into();
self.message_log self.message_log.get_mut(self.active_channel.clone()).push(
.get_mut(&self.active_channel) IrcMessage::Privmsg {
.unwrap()
.push(IrcMessage::Privmsg {
nickname: String::from("cri"), nickname: String::from("cri"),
message: self.input_value.clone(), message: self.input_value.clone(),
}); },
);
self.input_tx.borrow().send(message.clone()).unwrap(); self.input_tx.borrow().send(message.clone()).unwrap();
} }
@ -254,38 +199,39 @@ impl Application for Cri {
let _darker_grey = Color::new(0.498, 0.549, 0.553, 1.0); let _darker_grey = Color::new(0.498, 0.549, 0.553, 1.0);
let log = scrollable(column( let log = scrollable(column(
self.message_log[&self.active_channel] self.message_log
.get(&self.active_channel)
.unwrap()
.iter() .iter()
.flat_map(|message| { .flat_map(|message| match message {
match message { IrcMessage::Join { nickname } => {
IrcMessage::Join { nickname } => { Some(text(format!("* {nickname} joined the channel")).style(dark_green))
Some(text(format!("* {nickname} joined the channel")).style(dark_green)) }
} IrcMessage::Part { nickname, reason } => {
IrcMessage::Part { nickname, reason } => { let reason = match reason {
let reason = match reason { Some(reason) => format!(" ({reason})"),
Some(reason) => format!(" ({reason})"), None => String::new(),
None => String::new(), };
}; Some(
Some( text(format!("* {nickname} left the channel{reason}"))
text(format!("* {nickname} left the channel{reason}")) .style(dark_green),
.style(dark_green), )
) }
} IrcMessage::Nick { old, new } => Some(
IrcMessage::Nick { old, new } => { text(format!("* {old} changed their nickname to {new}")).style(dark_green),
Some(text(format!("* {old} changed their nickname to {new}")).style(dark_green)) ),
} IrcMessage::Quit { nickname, reason } => {
IrcMessage::Quit { nickname, reason }=> { let reason = match reason {
let reason = match reason { Some(reason) => format!(" ({reason})"),
Some(reason) => format!(" ({reason})"), None => String::new(),
None => String::new(), };
};
Some(text(format!("* {nickname} quit the server{reason}")) Some(
.style(dark_green)) text(format!("* {nickname} quit the server{reason}")).style(dark_green),
}, )
IrcMessage::Privmsg { nickname, message } => { }
Some(text(format!("<{nickname}> {message}"))) IrcMessage::Privmsg { nickname, message } => {
} Some(text(format!("<{nickname}> {message}")))
} }
}) })
.map(|element| element.into()) .map(|element| element.into())
@ -301,15 +247,11 @@ impl Application for Cri {
let channels = column( let channels = column(
self.message_log self.message_log
.keys() .get_channels()
.iter()
.map(|channel| { .map(|channel| {
let channel_name = channel let text = text(channel);
.as_ref() let is_active = self.active_channel.as_ref() == Some(channel);
.unwrap_or(&String::from("Server"))
.to_string();
let text = text(channel_name);
let is_active = &self.active_channel == channel;
let container = container(text) let container = container(text)
.style(move |_: &_| { .style(move |_: &_| {
let background = if is_active { let background = if is_active {
@ -326,7 +268,7 @@ impl Application for Cri {
.width(Length::Fill); .width(Length::Fill);
mouse_area(container) mouse_area(container)
.on_press(UiMessage::HandleChannelPress(channel.clone())) .on_press(UiMessage::HandleChannelPress(Some(channel.to_string())))
.into() .into()
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),

64
src/message_log.rs Normal file
View file

@ -0,0 +1,64 @@
use crate::irc_message::IrcMessage;
use std::collections::HashMap;
pub struct MessageLog(HashMap<Option<String>, Vec<IrcMessage>>);
impl<'a> MessageLog {
pub fn new() -> Self {
let mut log = HashMap::new();
log.insert(None, Vec::new());
Self(log)
}
pub fn get_channels(&'a self) -> Vec<&String> {
let mut keys = self.0.keys().flatten().collect::<Vec<_>>();
keys.sort_unstable();
keys
}
pub fn get(&self, channel: &Option<String>) -> Option<&Vec<IrcMessage>> {
self.0.get(channel)
}
pub fn get_mut(&mut self, channel: Option<String>) -> &mut Vec<IrcMessage> {
self.0.entry(channel).or_insert_with(Vec::new)
}
pub fn on_join(&mut self, channel: String, nickname: &str) {
self.get_mut(Some(channel)).push(IrcMessage::Join {
nickname: nickname.to_string(),
});
}
pub fn on_part(&mut self, channel: String, nickname: &str, comment: Option<&str>) {
self.get_mut(Some(channel)).push(IrcMessage::Part {
nickname: nickname.to_string(),
reason: comment.map(str::to_string),
});
}
pub fn on_nick(&mut self, old: &str, new: &str) {
for log in self.0.values_mut() {
log.push(IrcMessage::Nick {
old: old.to_string(),
new: new.to_string(),
});
}
}
pub fn on_quit(&mut self, nickname: &str, reason: Option<&str>) {
for log in self.0.values_mut() {
log.push(IrcMessage::Quit {
nickname: nickname.to_string(),
reason: reason.map(str::to_string),
})
}
}
pub fn on_privmsg(&mut self, channel: String, nickname: &str, message: &str) {
self.get_mut(Some(channel)).push(IrcMessage::Privmsg {
nickname: nickname.to_string(),
message: message.to_string(),
})
}
}