Allow parse function to accept multiple lines

This commit is contained in:
Ethan Henderson 2022-08-02 01:12:11 +01:00
parent 8a962bd25c
commit 80fe0fcfef

View file

@ -37,8 +37,8 @@
//! ``` //! ```
//! let msg = "@id=123;name=rick :nick!user@host.tmi.twitch.tv PRIVMSG #rickastley :Never gonna give you up!"; //! let msg = "@id=123;name=rick :nick!user@host.tmi.twitch.tv PRIVMSG #rickastley :Never gonna give you up!";
//! match ircparser::parse(msg) { //! match ircparser::parse(msg) {
//! Ok(x) => { //! Ok(mut x) => {
//! let line = x; //! let line = x.pop_front().unwrap();
//! //!
//! assert_eq!(&line.tags["id"], "123"); //! assert_eq!(&line.tags["id"], "123");
//! if line.source.is_some() { //! if line.source.is_some() {
@ -58,7 +58,7 @@
mod line; mod line;
pub use line::Line; pub use line::Line;
use std::collections::HashMap; use std::collections::{HashMap, VecDeque};
type ParseResult<T> = Result<T, ParseError>; type ParseResult<T> = Result<T, ParseError>;
@ -107,17 +107,20 @@ fn find_index(text: &str, char: char, start: usize) -> Option<usize> {
/// Parses an IRC message. /// Parses an IRC message.
/// ///
/// # Arguments /// # Arguments
/// - `line` - The line you want to parse. /// - `text` - The text you want to parse. This can comprise of multiple
/// lines. In this case, each line (separated by a newline character)
/// will be a separate element in the return value.
/// ///
/// # Returns /// # Returns
/// - [`Line`] - An instance representing a parsed line. /// - [`VecDeque<Line>`] - A [`VecDeque`] of all parsed [`Line`]s.
/// ///
/// # Example /// # Example
/// ``` /// ```
/// let msg = "@id=123;name=rick :nick!user@host.tmi.twitch.tv PRIVMSG #rickastley :Never gonna give you up!"; /// let msg = "@id=123;name=rick :nick!user@host.tmi.twitch.tv PRIVMSG #rickastley :Never gonna give you up!";
///
/// match ircparser::parse(msg) { /// match ircparser::parse(msg) {
/// Ok(x) => { /// Ok(mut x) => {
/// let line = x; /// let line = x.pop_front().unwrap();
/// ///
/// assert_eq!(&line.tags["id"], "123"); /// assert_eq!(&line.tags["id"], "123");
/// if line.source.is_some() { /// if line.source.is_some() {
@ -133,7 +136,15 @@ fn find_index(text: &str, char: char, start: usize) -> Option<usize> {
/// } /// }
/// }; /// };
/// ``` /// ```
pub fn parse(line: &str) -> ParseResult<Line> { ///
/// # Notice
/// The behaviour of this function changed in v0.2.0. It can now accept
/// multiple lines at once, but as a consequence, now returns a
/// [`VecDeque`] of [`Line`] objects instead of a single [`Line`].
pub fn parse(text: &str) -> ParseResult<VecDeque<Line>> {
let mut parsed_lines: VecDeque<Line> = VecDeque::new();
for line in text.replace("\r", "").split("\n") {
if line.is_empty() { if line.is_empty() {
return Err(ParseError::new("line length cannot be 0")); return Err(ParseError::new("line length cannot be 0"));
} }
@ -177,7 +188,10 @@ pub fn parse(line: &str) -> ParseResult<Line> {
params.push(line[c_idx + 2..].to_string()); params.push(line[c_idx + 2..].to_string());
} }
Ok(Line::new(tags, source, command, params)) parsed_lines.push_back(Line::new(tags, source, command, params));
}
Ok(parsed_lines)
} }
#[cfg(test)] #[cfg(test)]
@ -187,17 +201,31 @@ mod test_lib {
use std::collections::HashMap; use std::collections::HashMap;
#[test] #[test]
fn test_partial() { fn test_single_partial() {
let line = parse("PRIVMSG #rickastley :Never gonna give you up!").unwrap(); let msg = "PRIVMSG #rickastley :Never gonna give you up!";
match parse(msg) {
Ok(mut x) => {
let line = x.pop_front().unwrap();
assert_eq!(line.tags, HashMap::new()); assert_eq!(line.tags, HashMap::new());
assert_eq!(line.source, None); assert_eq!(line.source, None);
assert_eq!(line.command, "PRIVMSG"); assert_eq!(line.command, "PRIVMSG");
assert_eq!(line.params, vec!["#rickastley", "Never gonna give you up!"]); assert_eq!(line.params, vec!["#rickastley", "Never gonna give you up!"]);
} }
Err(e) => {
println!("A parsing error occured: {e}");
return;
}
}
}
#[test] #[test]
fn test_full() { fn test_single_full() {
let line = parse("@id=123;name=rick :nick!user@host.tmi.twitch.tv PRIVMSG #rickastley :Never gonna give you up!").unwrap(); let msg = "@id=123;name=rick :nick!user@host.tmi.twitch.tv PRIVMSG #rickastley :Never gonna give you up!";
match parse(msg) {
Ok(mut x) => {
let line = x.pop_front().unwrap();
assert_eq!( assert_eq!(
line.tags, line.tags,
hashmap! { hashmap! {
@ -212,13 +240,19 @@ mod test_lib {
assert_eq!(line.command, "PRIVMSG"); assert_eq!(line.command, "PRIVMSG");
assert_eq!(line.params, vec!["#rickastley", "Never gonna give you up!"]); assert_eq!(line.params, vec!["#rickastley", "Never gonna give you up!"]);
} }
Err(e) => {
println!("A parsing error occured: {e}");
return;
}
};
}
#[test] #[test]
fn test_readme_example() { fn test_readme_example() {
let msg = "@id=123;name=rick :nick!user@host.tmi.twitch.tv PRIVMSG #rickastley :Never gonna give you up!"; let msg = "@id=123;name=rick :nick!user@host.tmi.twitch.tv PRIVMSG #rickastley :Never gonna give you up!";
match parse(msg) { match parse(msg) {
Ok(x) => { Ok(mut x) => {
let line = x; let line = x.pop_front().unwrap();
assert_eq!(&line.tags["id"], "123"); assert_eq!(&line.tags["id"], "123");
if line.source.is_some() { if line.source.is_some() {
@ -234,4 +268,27 @@ mod test_lib {
} }
}; };
} }
#[test]
fn test_multiline() {
let msg = "@id=123 PRIVMSG #rickastley :Never gonna give you up!\n@id=456 PRIVMSG #rickastley :Never gonna let you down!";
match parse(msg) {
Ok(mut x) => {
assert_eq!(x.len(), 2);
let l1 = x.pop_front().unwrap();
let l2 = x.pop_front().unwrap();
assert_eq!(&l1.tags["id"], "123");
assert_eq!(&l2.tags["id"], "456");
assert_eq!(l1.command, l2.command);
assert_eq!(l1.params[1], "Never gonna give you up!");
assert_eq!(l2.params[1], "Never gonna let you down!");
}
Err(e) => {
println!("A parsing error occured: {e}");
return;
}
}
}
} }