Added current nickname tracking (fixes #125). [BREAKING]

This commit is contained in:
Aaron Weiss 2018-05-05 15:12:29 +02:00
parent b452921a03
commit 0235c71268
No known key found for this signature in database
GPG key ID: 047D32DF25DC22EF
3 changed files with 44 additions and 22 deletions

View file

@ -21,7 +21,7 @@ fn main() {
client.for_each_incoming(|message| { client.for_each_incoming(|message| {
print!("{}", message); print!("{}", message);
if let Command::PRIVMSG(ref target, ref msg) = message.command { if let Command::PRIVMSG(ref target, ref msg) = message.command {
if msg.starts_with(client.current_nickname()) { if msg.starts_with(&*client.current_nickname()) {
let tokens: Vec<_> = msg.split(' ').collect(); let tokens: Vec<_> = msg.split(' ').collect();
if tokens.len() > 2 { if tokens.len() > 2 {
let n = tokens[0].len() + tokens[1].len() + 2; let n = tokens[0].len() + tokens[1].len() + 2;

View file

@ -37,7 +37,7 @@
//! # client.identify().unwrap(); //! # client.identify().unwrap();
//! client.for_each_incoming(|irc_msg| { //! client.for_each_incoming(|irc_msg| {
//! if let Command::PRIVMSG(channel, message) = irc_msg.command { //! if let Command::PRIVMSG(channel, message) = irc_msg.command {
//! if message.contains(client.current_nickname()) { //! if message.contains(&*client.current_nickname()) {
//! client.send_privmsg(&channel, "beep boop").unwrap(); //! client.send_privmsg(&channel, "beep boop").unwrap();
//! } //! }
//! } //! }
@ -49,7 +49,7 @@
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path; use std::path::Path;
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard};
use std::thread; use std::thread;
#[cfg(feature = "ctcp")] #[cfg(feature = "ctcp")]
@ -94,7 +94,7 @@ pub mod transport;
/// # client.identify().unwrap(); /// # client.identify().unwrap();
/// client.stream().for_each_incoming(|irc_msg| { /// client.stream().for_each_incoming(|irc_msg| {
/// match irc_msg.command { /// match irc_msg.command {
/// Command::PRIVMSG(channel, message) => if message.contains(client.current_nickname()) { /// Command::PRIVMSG(channel, message) => if message.contains(&*client.current_nickname()) {
/// client.send_privmsg(&channel, "beep boop").unwrap(); /// client.send_privmsg(&channel, "beep boop").unwrap();
/// } /// }
/// _ => () /// _ => ()
@ -159,7 +159,7 @@ pub trait Client {
/// # client.identify().unwrap(); /// # client.identify().unwrap();
/// client.for_each_incoming(|irc_msg| { /// client.for_each_incoming(|irc_msg| {
/// if let Command::PRIVMSG(channel, message) = irc_msg.command { /// if let Command::PRIVMSG(channel, message) = irc_msg.command {
/// if message.contains(client.current_nickname()) { /// if message.contains(&*client.current_nickname()) {
/// client.send_privmsg(&channel, "beep boop").unwrap(); /// client.send_privmsg(&channel, "beep boop").unwrap();
/// } /// }
/// } /// }
@ -231,6 +231,9 @@ struct ClientState {
chanlists: Mutex<HashMap<String, Vec<User>>>, chanlists: Mutex<HashMap<String, Vec<User>>>,
/// A thread-safe index to track the current alternative nickname being used. /// A thread-safe index to track the current alternative nickname being used.
alt_nick_index: RwLock<usize>, alt_nick_index: RwLock<usize>,
/// The current nickname in use by this client, which may differ from the one implied by
/// `alt_nick_index`. This can be the case if, for example, a new `NICK` command is sent.
current_nickname: RwLock<String>,
/// A thread-safe internal IRC stream used for the reading API. /// A thread-safe internal IRC stream used for the reading API.
incoming: Mutex<Option<SplitStream<Connection>>>, incoming: Mutex<Option<SplitStream<Connection>>>,
/// A thread-safe copy of the outgoing channel. /// A thread-safe copy of the outgoing channel.
@ -289,26 +292,20 @@ impl ClientState {
incoming: SplitStream<Connection>, incoming: SplitStream<Connection>,
outgoing: UnboundedSender<Message>, outgoing: UnboundedSender<Message>,
config: Config, config: Config,
) -> ClientState { ) -> error::Result<ClientState> {
ClientState { Ok(ClientState {
config: config,
chanlists: Mutex::new(HashMap::new()), chanlists: Mutex::new(HashMap::new()),
alt_nick_index: RwLock::new(0), alt_nick_index: RwLock::new(0),
current_nickname: RwLock::new(config.nickname()?.to_owned()),
incoming: Mutex::new(Some(incoming)), incoming: Mutex::new(Some(incoming)),
outgoing: outgoing, outgoing: outgoing,
} config: config,
})
} }
/// Gets the current nickname in use. /// Gets the current nickname in use.
fn current_nickname(&self) -> &str { fn current_nickname(&self) -> RwLockReadGuard<String> {
let alt_nicks = self.config().alternate_nicknames(); self.current_nickname.read().unwrap()
let index = self.alt_nick_index.read().unwrap();
match *index {
0 => self.config().nickname().expect(
"current_nickname should not be callable if nickname is not defined."
),
i => alt_nicks[i - 1],
}
} }
/// Handles sent messages internally for basic client functionality. /// Handles sent messages internally for basic client functionality.
@ -332,6 +329,7 @@ impl ClientState {
KICK(ref chan, ref user, _) => self.handle_part(user, chan), KICK(ref chan, ref user, _) => self.handle_part(user, chan),
QUIT(_) => self.handle_quit(msg.source_nickname().unwrap_or("")), QUIT(_) => self.handle_quit(msg.source_nickname().unwrap_or("")),
NICK(ref new_nick) => { NICK(ref new_nick) => {
self.handle_current_nick_change(msg.source_nickname().unwrap_or(""), new_nick);
self.handle_nick_change(msg.source_nickname().unwrap_or(""), new_nick) self.handle_nick_change(msg.source_nickname().unwrap_or(""), new_nick)
} }
ChannelMODE(ref chan, ref modes) => self.handle_mode(chan, modes), ChannelMODE(ref chan, ref modes) => self.handle_mode(chan, modes),
@ -475,6 +473,14 @@ impl ClientState {
} }
} }
fn handle_current_nick_change(&self, old_nick: &str, new_nick: &str) {
if old_nick.is_empty() || new_nick.is_empty() || old_nick != &*self.current_nickname() {
return;
}
let mut nick = self.current_nickname.write().unwrap();
*nick = new_nick.to_owned();
}
#[cfg(feature = "nochanlists")] #[cfg(feature = "nochanlists")]
fn handle_nick_change(&self, _: &str, _: &str) {} fn handle_nick_change(&self, _: &str, _: &str) {}
@ -715,7 +721,7 @@ impl IrcClient {
}); });
Ok(IrcClient { Ok(IrcClient {
state: Arc::new(ClientState::new(rx_incoming.wait()?, tx_outgoing, config)), state: Arc::new(ClientState::new(rx_incoming.wait()?, tx_outgoing, config)?),
view: rx_view.wait()?, view: rx_view.wait()?,
}) })
} }
@ -774,7 +780,7 @@ impl IrcClient {
/// Gets the current nickname in use. This may be the primary username set in the configuration, /// Gets the current nickname in use. This may be the primary username set in the configuration,
/// or it could be any of the alternative nicknames listed as well. As a result, this is the /// or it could be any of the alternative nicknames listed as well. As a result, this is the
/// preferred way to refer to the client's nickname. /// preferred way to refer to the client's nickname.
pub fn current_nickname(&self) -> &str { pub fn current_nickname(&self) -> RwLockReadGuard<String> {
self.state.current_nickname() self.state.current_nickname()
} }
@ -820,7 +826,7 @@ impl<'a> Future for IrcClientFuture<'a> {
let server = IrcClient { let server = IrcClient {
state: Arc::new(ClientState::new( state: Arc::new(ClientState::new(
stream, self.tx_outgoing.take().unwrap(), self.config.clone() stream, self.tx_outgoing.take().unwrap(), self.config.clone()
)), )?),
view: view, view: view,
}; };
Ok(Async::Ready(PackedIrcClient(server, Box::new(outgoing_future)))) Ok(Async::Ready(PackedIrcClient(server, Box::new(outgoing_future))))
@ -1048,6 +1054,22 @@ mod test {
} }
} }
#[test]
fn current_nickname_tracking() {
let value = ":test!test@test NICK :t3st\r\n\
:t3st!test@test NICK :t35t\r\n";
let client = IrcClient::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
assert_eq!(&*client.current_nickname(), "test");
client.for_each_incoming(|message| {
println!("{:?}", message);
}).unwrap();
assert_eq!(&*client.current_nickname(), "t35t");
}
#[test] #[test]
fn send() { fn send() {
let client = IrcClient::from_config(test_config()).unwrap(); let client = IrcClient::from_config(test_config()).unwrap();

View file

@ -29,7 +29,7 @@
//! client.for_each_incoming(|irc_msg| { //! client.for_each_incoming(|irc_msg| {
//! // irc_msg is a Message //! // irc_msg is a Message
//! if let Command::PRIVMSG(channel, message) = irc_msg.command { //! if let Command::PRIVMSG(channel, message) = irc_msg.command {
//! if message.contains(client.current_nickname()) { //! if message.contains(&*client.current_nickname()) {
//! // send_privmsg comes from ClientExt //! // send_privmsg comes from ClientExt
//! client.send_privmsg(&channel, "beep boop").unwrap(); //! client.send_privmsg(&channel, "beep boop").unwrap();
//! } //! }