diff --git a/config.toml b/config.toml index 2ee957f..e299357 100644 --- a/config.toml +++ b/config.toml @@ -1,3 +1,6 @@ -username = "test" +channels = ["#main", "#main2"] +host = "192.168.178.100" mode = "+B" -channels = ["#main", "#fsoc"] \ No newline at end of file +nickname = "IRSC" +port = 6667 +username = "IRSC" diff --git a/src/lib.rs b/src/lib.rs index eae7b09..1bbf915 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,6 @@ +//! A simple IRC crate written in rust + +#![warn(missing_docs)] use std::borrow::Cow; use std::fs::File; use std::io::{Error, Read, Write}; @@ -6,35 +9,76 @@ use std::path::Path; use serde_derive::Deserialize; +/// An IRC client pub struct Client { - stream: TcpStream, config: Config, + stream: TcpStream, } +/// Config for the IRC client #[derive(Clone, Deserialize, Default)] pub struct Config { - username: String, - nickname: Option, - mode: Option, channels: Box<[String]>, + host: String, + mode: Option, + nickname: Option, + port: u16, + username: String, } +#[doc(hidden)] #[derive(Debug)] pub enum CapMode { LS, END, } +/// IRC commands #[derive(Debug)] pub enum Command { - PING(String), - PONG(String), + #[doc(hidden)] CAP(CapMode), - USER(String, String, String, String), + /// Joins a channel + /// ```rust + /// client.write_command(Command::PRIVMSG("#main".to_string()))?; + /// ``` + JOIN( + /// Channel + String, + ), + /// Sets the mode of the user + /// ```rust + /// client.write_command(Command::MODE("#main".to_string(), Some("+B")))?; + /// ``` + MODE( + /// Channel + String, + /// Mode + Option, + ), + #[doc(hidden)] NICK(String), - JOIN(String), - MODE(String, Option), + /// Everything that is not a command OTHER(String), + /// Ping another user + PING( + /// User + String, + ), + #[doc(hidden)] + PONG(String), + /// Sends a message in a channel + /// ```rust + /// client.write_command(Command::PRIVMSG("#main".to_string(), "This is an example message".to_string()))?; + /// ``` + PRIVMSG( + /// Channel + String, + /// Message + String, + ), + #[doc(hidden)] + USER(String, String, String, String), } impl Command { @@ -51,12 +95,19 @@ impl Command { } impl Client { - pub fn new(host: &str, port: u16, config: Config) -> Result { - let stream = TcpStream::connect(format!("{}:{}", host, port))?; - + /// Creates a new client with a given config + /// ```rust + /// let mut client = Client::new(config)?; + /// ``` + pub fn new(config: Config) -> Result { + let stream = TcpStream::connect(format!("{}:{}", config.host, config.port))?; Ok(Self { stream, config }) } + /// Identify user and join the IRC + /// ```rust + /// client.identify()?; + /// ``` pub fn identify(&mut self) -> Result<(), Error> { self.write_command(Command::CAP(CapMode::END))?; self.write_command(Command::USER( @@ -65,6 +116,7 @@ impl Client { "*".into(), self.config.username.clone(), ))?; + if let Some(nick) = self.config.nickname.clone() { self.write_command(Command::NICK(nick))?; } else { @@ -73,14 +125,14 @@ impl Client { loop { if let Ok(ref command) = self.read() { - if let Command::PING(code) = command { - self.write_command(Command::PONG(code.to_string()))?; - continue; - } - if let Command::OTHER(line) = command { - if line.contains("001") { - break; + match command { + Command::PING(code) => self.write_command(Command::PONG(code.to_string()))?, + Command::OTHER(line) => { + if line.contains("001") { + break; + } } + _ => {} } } } @@ -94,8 +146,7 @@ impl Client { Ok(()) } - // temporarily pub, change this later - pub fn read_string(&mut self) -> Option { + fn read_string(&mut self) -> Option { let mut buffer = [0u8; 512]; match self.stream.read(&mut buffer) { @@ -106,6 +157,14 @@ impl Client { Some(String::from_utf8_lossy(&buffer).into()) } + /// Read data comming from the IRC + /// ```rust + /// if let Ok(ref command) = client.read() { + /// if let Command::OTHER(line) = command { + /// print!("{}", line); + /// } + /// } + /// ``` pub fn read(&mut self) -> Result { if let Some(string) = self.read_string() { return Ok(Command::from_str(&string)); @@ -125,6 +184,10 @@ impl Client { Ok(()) } + /// Send a command to the IRC + /// ```rust + /// client.write_command(Command::PRIVMSG("#main".to_string(), "Hello".to_string()))?; + /// ``` pub fn write_command(&mut self, command: Command) -> Result<(), Error> { use Command::*; let computed = match command { @@ -135,26 +198,14 @@ impl Client { END => "CAP END", }) as Cow } - USER(username, s1, s2, realname) => { - let formatted = format!("USER {} {} {} :{}", username, s1, s2, realname); + JOIN(channel) => { + let formatted = format!("JOIN {}", channel); Cow::Owned(formatted) as Cow } NICK(nickname) => { let formatted = format!("NICK {}", nickname); Cow::Owned(formatted) as Cow } - PING(code) => { - let formatted = format!("PING {}", code); - Cow::Owned(formatted) as Cow - } - PONG(code) => { - let formatted = format!("PONG {}", code); - Cow::Owned(formatted) as Cow - } - JOIN(channel) => { - let formatted = format!("JOIN {}", channel); - Cow::Owned(formatted) as Cow - } MODE(target, mode) => { let formatted = { if let Some(mode) = mode { @@ -172,29 +223,79 @@ impl Client { "Cannot write commands of type OTHER", )); } + PING(code) => { + let formatted = format!("PING {}", code); + Cow::Owned(formatted) as Cow + } + PONG(code) => { + let formatted = format!("PONG {}", code); + Cow::Owned(formatted) as Cow + } + PRIVMSG(target, message) => { + let formatted = format!("PRIVMSG {} {}", target, message); + Cow::Owned(formatted) as Cow + } + USER(username, s1, s2, realname) => { + let formatted = format!("USER {} {} {} :{}", username, s1, s2, realname); + Cow::Owned(formatted) as Cow + } }; self.write(&computed)?; - Ok(()) } } impl Config { + /// Create a new config for the client
+ ///
+ /// channels: Channels to join on the IRC
+ /// host: IP or domain of the IRC server
+ /// mode: Mode to join the IRC with
+ /// nickname: Nickname to join the IRC with
+ /// port: Port of the IRC server
+ /// username: Username to join the IRC with
+ /// ```rust + /// let config = Config::new( + /// Box::new(["#main".to_string(), "#main2".to_string()]), + /// "192.168.178.100", + /// Some("+B".to_string()), + /// Some("IRSC".to_string()), + /// 6667, + /// "IRSC", + /// ); + /// ``` pub fn new( - username: &str, - nickname: Option, - mode: Option, channels: Box<[String]>, + host: &str, + mode: Option, + nickname: Option, + port: u16, + username: &str, ) -> Self { Self { - username: username.into(), - nickname, - mode, channels, + host: host.into(), + mode, + nickname, + port, + username: username.into(), } } + /// Create a config from a toml file + /// ```rust + /// let config = Config::from_toml("config.toml")?; + /// ``` + /// + /// ```toml + /// channels = ["#main", "#main2"] + /// host = "192.168.178.100" + /// mode = "+B" + /// nickname = "IRSC" + /// port = 6667 + /// username = "IRSC" + /// ``` pub fn from_toml>(path: P) -> Result { let mut file = File::open(&path)?; let mut data = String::new(); @@ -202,7 +303,6 @@ impl Config { toml::from_str(&data).map_err(|e| { use std::io::ErrorKind; - Error::new(ErrorKind::Other, format!("Invalid TOML: {}", e)) }) } diff --git a/src/main.rs b/src/main.rs index 35f7b66..e987dc7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,8 +11,17 @@ macro_rules! loop_n { fn main() -> Result<(), std::io::Error> { let config = Config::from_toml("config.toml")?; - let mut client = Client::new("192.168.1.28", 6667, config)?; + /*let config = Config::new( + Box::new(["#main".to_string(), "#main2".to_string()]), + "192.168.178.100", + Some("+B".to_string()), + Some("IRSC".to_string()), + 6667, + "IRSC", + );*/ + let mut client = Client::new(config)?; client.identify()?; + client.write_command(Command::PRIVMSG("#main".to_string(), "Hello".to_string()))?; loop { if let Ok(ref command) = client.read() {