diff --git a/README.md b/README.md index 074fa7c..a3bccf5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,53 @@ # ircparser An IRC (RFC1459) parser and formatter, built in Rust. + +## Setup + +To use the latest stable version of `ircparser`, add it to your Cargo.toml file like so: + +```toml +[dependencies] +ircparser = "^0.1" +``` + +You can also use the latest development version by specifying the following: + +```toml +[dependencies] +ircparser = { git = "https://github.com/parafoxia/ircparser" } +``` + +## Usage + +`ircparser` currently only has one public function — `parse`. +This function takes a line of an IRC message, and parses it into an easy-to-use `Line` object. + +```rs +use ircparser; + +fn main() { + let msg = "@id=123;name=rick :nick!user@host.tmi.twitch.tv PRIVMSG #rickastley :Never gonna give you up!"; + match ircparser::parse(msg) { + Ok(x) => { + let line = x; + + assert_eq!(&line.tags["id"], "123"); + if line.source.is_some() { + assert_eq!(line.source.unwrap(), ":nick!user@host.tmi.twitch.tv"); + } + assert_eq!(line.command, "PRIVMSG"); + assert_eq!(line.params[0], "#rickastley"); + assert_eq!(line.params[1], "Never gonna give you up!"); + } + Err(e) => { + println!("A parsing error occured: {e}"); + return; + } + }; +} +``` + +## License + +The `ircparser` crate for Rust is licensed under the [BSD 3-Clause License](https://github.com/parafoxia/ircparser/blob/main/LICENSE). diff --git a/src/lib.rs b/src/lib.rs index 8d51c47..de371b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,35 @@ // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//! An IRC (RFC1459) parser and formatter, built in Rust. +//! +//! ## Parsing messages +//! +//! You can parse IRC messages using the provided `parse` function. +//! +//! ``` +//! fn main() { +//! let msg = "@id=123;name=rick :nick!user@host.tmi.twitch.tv PRIVMSG #rickastley :Never gonna give you up!"; +//! match ircparser::parse(msg) { +//! Ok(x) => { +//! let line = x; +//! +//! assert_eq!(&line.tags["id"], "123"); +//! if line.source.is_some() { +//! assert_eq!(line.source.unwrap(), ":nick!user@host.tmi.twitch.tv"); +//! } +//! assert_eq!(line.command, "PRIVMSG"); +//! assert_eq!(line.params[0], "#rickastley"); +//! assert_eq!(line.params[1], "Never gonna give you up!"); +//! } +//! Err(e) => { +//! println!("A parsing error occured: {e}"); +//! return; +//! } +//! }; +//! } +//! ``` + mod line; pub use line::Line; @@ -35,12 +64,25 @@ use std::collections::HashMap; type ParseResult = Result; +/// Exception thrown when an error occurs during message parsing. #[derive(Debug, Clone)] pub struct ParseError { + /// The details of this error. pub details: String, } impl ParseError { + /// Generates a new [`ParseError`]. + /// + /// # Arguments + /// - `details` - THe details of this error. + /// + /// # Example + /// ``` + /// let e = ircparser::ParseError::new("err"); + /// + /// assert_eq!(e.details, "err".to_string()) + /// ``` pub fn new(details: &str) -> Self { Self { details: details.into(), @@ -64,6 +106,35 @@ fn find_index(text: &str, char: char, start: usize) -> Option { None } +/// Parses an IRC message. +/// +/// # Arguments +/// - `line` - The line you want to parse. +/// +/// # Returns +/// - [`Line`] - An instance representing a parsed line. +/// +/// # Example +/// ``` +/// let msg = "@id=123;name=rick :nick!user@host.tmi.twitch.tv PRIVMSG #rickastley :Never gonna give you up!"; +/// match ircparser::parse(msg) { +/// Ok(x) => { +/// let line = x; +/// +/// assert_eq!(&line.tags["id"], "123"); +/// if line.source.is_some() { +/// assert_eq!(line.source.unwrap(), ":nick!user@host.tmi.twitch.tv"); +/// } +/// assert_eq!(line.command, "PRIVMSG"); +/// assert_eq!(line.params[0], "#rickastley"); +/// assert_eq!(line.params[1], "Never gonna give you up!"); +/// } +/// Err(e) => { +/// println!("A parsing error occured: {e}"); +/// return; +/// } +/// }; +/// ``` pub fn parse(line: &str) -> ParseResult { if line.is_empty() { return Err(ParseError::new("line length cannot be 0")); @@ -143,4 +214,26 @@ mod test_lib { assert_eq!(line.command, "PRIVMSG"); assert_eq!(line.params, vec!["#rickastley", "Never gonna give you up!"]); } + + #[test] + fn test_readme_example() { + let msg = "@id=123;name=rick :nick!user@host.tmi.twitch.tv PRIVMSG #rickastley :Never gonna give you up!"; + match parse(msg) { + Ok(x) => { + let line = x; + + assert_eq!(&line.tags["id"], "123"); + if line.source.is_some() { + assert_eq!(line.source.unwrap(), ":nick!user@host.tmi.twitch.tv"); + } + assert_eq!(line.command, "PRIVMSG"); + assert_eq!(line.params[0], "#rickastley"); + assert_eq!(line.params[1], "Never gonna give you up!"); + } + Err(e) => { + println!("A parsing error occured: {e}"); + return; + } + }; + } } diff --git a/src/line.rs b/src/line.rs index e2946c1..7e102a7 100644 --- a/src/line.rs +++ b/src/line.rs @@ -30,15 +30,54 @@ use std::collections::HashMap; +/// A struct representing a parsed line. #[derive(Debug, Clone, Default)] pub struct Line { + /// This line's tags. This will be an empty hashmap if there are + /// none. pub tags: HashMap, + /// This line's source (including the nick, user, and host). This is + /// optional, and will be [`None`] if not provided. pub source: Option, + /// This line's command. pub command: String, + /// Any parameters passed to the command. This will be an empty + /// vector if there are none. pub params: Vec, } impl Line { + /// Creates a new [`Line`]. You should never call this directly, but + /// instead use the [ircparser::parse](super::parse) function. + /// + /// # Arguments + /// - `tags` - This line's tags. + /// = `source` - This line's source, or [`None`] if not to be + /// provided. + /// - `command` - This line's command. + /// - `params` - Any parameters passed to the command. + /// + /// # Returns + /// - [`Line`] - The new [`Line`] instance. + /// + /// # Example + /// ``` + /// use std::collections::HashMap; + /// + /// let mut tags: HashMap = HashMap::new(); + /// tags.insert("id".to_string(), "123".to_string()); + /// + /// let source = Some(":nick!user@host.tmi.twitch.tv".to_string()); + /// let command = "PRIVMSG"; + /// let params = vec!["#rickastley".to_string()]; + /// + /// let line = ircparser::Line::new(tags, source, command, params); + /// + /// assert_eq!(&line.tags["id"], "123"); + /// assert_eq!(line.source.unwrap(), ":nick!user@host.tmi.twitch.tv"); + /// assert_eq!(line.command, "PRIVMSG"); + /// assert_eq!(line.params[0], "#rickastley"); + /// ``` pub fn new( tags: HashMap, source: Option,