diff --git a/src/commands.rs b/src/commands.rs index 7fb5bd2..51d7fc5 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -182,9 +182,10 @@ impl Command { /// /// # Panics /// - /// This function will panic if the IRCd sends malformed messages. Please contact the - /// maintainer of your IRCd if this happens. - pub fn from_str(s: &str) -> Self { + /// This function will panic if the ``IRCd`` sends malformed messages. Please contact the + /// maintainer of your ``IRCd`` if this happens. + #[must_use] + pub fn command_from_str(s: &str) -> Self { let new = s.trim(); #[cfg(feature = "debug")] diff --git a/src/lib.rs b/src/lib.rs index b8f5d54..7d68ff9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ //! } #![warn(missing_docs)] +#![allow(clippy::too_many_lines)] #[cfg(feature = "tls")] use native_tls::TlsConnector; @@ -56,6 +57,18 @@ pub struct Config { username: String, } +/// Custom Error for the `read` function +#[derive(Debug)] +pub struct NoNewLines; + +impl std::fmt::Display for NoNewLines { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Now new lines from the stream.") + } +} + +impl std::error::Error for NoNewLines {} + impl Client { /// Creates a new client with a given [`Config`]. /// ```no_run @@ -64,8 +77,10 @@ impl Client { /// let mut client = Client::new(config)?; /// # Ok::<(), std::io::Error>(()) /// ``` - /// + /// # Errors /// Returns error if the client could not connect to the host. + /// # Panics + /// Panics if the client can't connect to the host. pub fn new(config: Config) -> Result { let stream = TcpStream::connect(format!("{}:{}", config.host, config.port))?; #[cfg(feature = "tls")] @@ -94,6 +109,7 @@ impl Client { /// client.identify()?; /// # Ok::<(), std::io::Error>(()) /// ``` + /// # Errors /// Returns error if the client could not write to the stream. pub fn identify(&mut self) -> Result<(), Error> { self.write_command(commands::Command::CAP(commands::CapMode::LS))?; @@ -107,7 +123,7 @@ impl Client { ))?; if let Some(nick) = self.config.nickname.clone() { - self.write_command(commands::Command::NICK(nick.to_string()))?; + self.write_command(commands::Command::NICK(nick))?; } else { self.write_command(commands::Command::NICK(self.config.username.clone()))?; } @@ -132,7 +148,7 @@ impl Client { let config = self.config.clone(); self.write_command(commands::Command::MODE(config.username, config.mode))?; - for channel in config.channels.iter() { + for channel in &config.channels { self.write_command(commands::Command::JOIN(channel.to_string()))?; } @@ -169,17 +185,18 @@ impl Client { /// # Ok::<(), std::io::Error>(()) /// # } /// ``` + /// # Errors /// Returns error if there are no new messages. This should not be taken as an actual error, because nothing went wrong. - pub fn read(&mut self) -> Result { + pub fn read(&mut self) -> Result { if let Some(string) = self.read_string() { #[cfg(feature = "debug")] println!("{:#?}", string); - let command = commands::Command::from_str(&string); + let command = commands::Command::command_from_str(&string); if let commands::Command::PONG(command) = command { if let Err(_e) = self.write_command(commands::Command::PONG(command)) { - return Err(()); + return Err(NoNewLines); } return Ok(commands::Command::PONG("".to_string())); } @@ -187,7 +204,7 @@ impl Client { return Ok(command); } - Err(()) + Err(NoNewLines) } fn write(&mut self, data: &str) -> Result<(), Error> { @@ -199,7 +216,7 @@ impl Client { #[cfg(feature = "debug")] println!("{:?}", formatted); - self.stream.write(formatted.as_bytes())?; + self.stream.write_all(formatted.as_bytes())?; Ok(()) } @@ -213,9 +230,13 @@ impl Client { /// client.write_command(Command::PRIVMSG("".to_string(), "#main".to_string(), "Hello".to_string()))?; /// # Ok::<(), std::io::Error>(()) /// ``` + /// # Errors /// Returns error if the client could not write to the stream. pub fn write_command(&mut self, command: commands::Command) -> Result<(), Error> { - use commands::Command::*; + use commands::Command::{ + ADMIN, AWAY, CAP, INVITE, JOIN, LIST, MODE, NAMES, NICK, OPER, OTHER, PART, PASS, PING, + PONG, PRIVMSG, QUIT, TOPIC, USER, + }; let computed = match command { ADMIN(target) => { let formatted = format!("ADMIN {}", target); @@ -226,7 +247,7 @@ impl Client { Cow::Owned(formatted) as Cow } CAP(mode) => { - use commands::CapMode::*; + use commands::CapMode::{END, LS}; Cow::Borrowed(match mode { LS => "CAP LS 302", END => "CAP END", @@ -337,6 +358,8 @@ impl Client { /// client.admin("192.168.178.100")?; /// # Ok::<(), std::io::Error>(()) /// ``` + /// # Errors + /// Returns error if the client could not write to the stream. pub fn admin(&mut self, target: &str) -> Result<(), Error> { self.write_command(commands::Command::ADMIN(target.to_string()))?; Ok(()) @@ -349,6 +372,8 @@ impl Client { /// client.away("AFK")?; /// # Ok::<(), std::io::Error>(()) /// ``` + /// # Errors + /// Returns error if the client could not write to the stream. pub fn away(&mut self, message: &str) -> Result<(), Error> { self.write_command(commands::Command::AWAY(message.to_string()))?; Ok(()) @@ -361,6 +386,8 @@ impl Client { /// client.privmsg("#main", "Hello")?; /// # Ok::<(), std::io::Error>(()) /// ``` + /// # Errors + /// Returns error if the client could not write to the stream. pub fn privmsg(&mut self, channel: &str, message: &str) -> Result<(), Error> { self.write_command(commands::Command::PRIVMSG( String::from(""), @@ -377,6 +404,8 @@ impl Client { /// client.invite("liblirc", "#circe")?; /// # Ok::<(), std::io::Error>(()) /// ``` + /// # Errors + /// Returns error if the client could not write to the stream. pub fn invite(&mut self, username: &str, channel: &str) -> Result<(), Error> { self.write_command(commands::Command::INVITE( username.to_string(), @@ -392,33 +421,25 @@ impl Client { /// client.join("#main")?; /// # Ok::<(), std::io::Error>(()) /// ``` + /// # Errors + /// Returns error if the client could not write to the stream. pub fn join(&mut self, channel: &str) -> Result<(), Error> { self.write_command(commands::Command::JOIN(channel.to_string()))?; Ok(()) } - /// Helper function for LISTing channels and modes + /// Helper function for ``LISTing`` channels and modes /// ```no_run /// # use circe::*; /// # let mut client = Client::new(Config::from_toml("config.toml")?)?; /// client.list(None, None)?; /// # Ok::<(), std::io::Error>(()) /// ``` + /// # Errors + /// Returns error if the client could not write to the stream. pub fn list(&mut self, channel: Option<&str>, server: Option<&str>) -> Result<(), Error> { - let channel_config = { - if let Some(channel) = channel { - Some(channel.to_string()) - } else { - None - } - }; - let server_config = { - if let Some(server) = server { - Some(server.to_string()) - } else { - None - } - }; + let channel_config = channel.map(std::string::ToString::to_string); + let server_config = server.map(std::string::ToString::to_string); self.write_command(commands::Command::LIST(channel_config, server_config))?; Ok(()) } @@ -430,6 +451,8 @@ impl Client { /// client.names("#main,#circe", None)?; /// # Ok::<(), std::io::Error>(()) /// ``` + /// # Errors + /// Returns error if the client could not write to the stream. pub fn names(&mut self, channel: &str, server: Option<&str>) -> Result<(), Error> { if let Some(server) = server { self.write_command(commands::Command::NAMES( @@ -449,6 +472,8 @@ impl Client { /// client.oper("username", "password")?; /// # Ok::<(), std::io::Error>(()) /// ``` + /// # Errors + /// Returns error if the client could not write to the stream. pub fn oper(&mut self, username: &str, password: &str) -> Result<(), Error> { self.write_command(commands::Command::OPER( username.to_string(), @@ -464,6 +489,8 @@ impl Client { /// client.mode("test", Some("+B"))?; /// # Ok::<(), std::io::Error>(()) /// ``` + /// # Errors + /// Returns error if the client could not write to the stream. pub fn mode(&mut self, target: &str, mode: Option<&str>) -> Result<(), Error> { if let Some(mode) = mode { self.write_command(commands::Command::MODE( @@ -483,6 +510,8 @@ impl Client { /// client.part("#main")?; /// # Ok::<(), std::io::Error>(()) /// ``` + /// # Errors + /// Returns error if the client could not write to the stream. pub fn part(&mut self, target: &str) -> Result<(), Error> { self.write_command(commands::Command::PART(target.to_string()))?; Ok(()) @@ -495,6 +524,8 @@ impl Client { /// client.topic("#main", Some("main channel"))?; /// # Ok::<(), std::io::Error>(()) /// ``` + /// # Errors + /// Returns error if the client could not write to the stream. pub fn topic(&mut self, channel: &str, topic: Option<&str>) -> Result<(), Error> { if let Some(topic) = topic { self.write_command(commands::Command::TOPIC( @@ -514,6 +545,8 @@ impl Client { /// client.quit(None)?; /// # Ok::<(), std::io::Error>(()) /// ``` + /// # Errors + /// Returns error if the client could not write to the stream. pub fn quit(&mut self, message: Option<&str>) -> Result<(), Error> { if let Some(message) = message { self.write_command(commands::Command::QUIT(message.to_string()))?; @@ -548,8 +581,9 @@ impl Config { /// "circe", /// ); /// ``` + #[must_use] pub fn new( - channels: Vec<&'static str>, + channels: &[&'static str], host: &str, mode: Option<&'static str>, nickname: Option<&'static str>, @@ -557,7 +591,10 @@ impl Config { username: &str, ) -> Self { // Conversion from &'static str to String - let channels_config = channels.iter().map(|channel| channel.to_string()).collect(); + let channels_config = channels + .iter() + .map(|channel| (*channel).to_string()) + .collect(); let mode_config: Option; if let Some(mode) = mode { @@ -597,6 +634,7 @@ impl Config { /// port = 6667 /// username = "circe" /// ``` + /// # Errors /// Returns an Error if the file cannot be opened or if the TOML is invalid pub fn from_toml>(path: P) -> Result { let mut file = File::open(&path)?; diff --git a/src/main.rs b/src/main.rs index bd720ef..f8a4d64 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ fn main() -> Result<(), std::io::Error> { let config = Config::from_toml("config.toml")?; let mut client = Client::new(config).unwrap(); client.identify().unwrap(); + client.privmsg("#main", "Test")?; loop { if let Ok(ref command) = client.read() {