Allow parse function to accept multiple lines
This commit is contained in:
		
							parent
							
								
									8a962bd25c
								
							
						
					
					
						commit
						80fe0fcfef
					
				
							
								
								
									
										189
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										189
									
								
								src/lib.rs
									
									
									
									
									
								
							| 
						 | 
					@ -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,51 +136,62 @@ fn find_index(text: &str, char: char, start: usize) -> Option<usize> {
 | 
				
			||||||
///     }
 | 
					///     }
 | 
				
			||||||
/// };
 | 
					/// };
 | 
				
			||||||
/// ```
 | 
					/// ```
 | 
				
			||||||
pub fn parse(line: &str) -> ParseResult<Line> {
 | 
					///
 | 
				
			||||||
    if line.is_empty() {
 | 
					/// # Notice
 | 
				
			||||||
        return Err(ParseError::new("line length cannot be 0"));
 | 
					/// 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();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut idx = 0;
 | 
					    for line in text.replace("\r", "").split("\n") {
 | 
				
			||||||
    let mut tags: HashMap<String, String> = HashMap::new();
 | 
					        if line.is_empty() {
 | 
				
			||||||
    let mut source: Option<String> = None;
 | 
					            return Err(ParseError::new("line length cannot be 0"));
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Parse tags component.
 | 
					 | 
				
			||||||
    if line.starts_with('@') {
 | 
					 | 
				
			||||||
        idx = line.find(' ').unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for part in Some(&line[1..idx]).unwrap().split(';') {
 | 
					 | 
				
			||||||
            let kv: Vec<&str> = part.split('=').collect();
 | 
					 | 
				
			||||||
            tags.insert(kv[0].to_string(), kv[1].to_string());
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        idx += 1;
 | 
					        let mut idx = 0;
 | 
				
			||||||
    }
 | 
					        let mut tags: HashMap<String, String> = HashMap::new();
 | 
				
			||||||
 | 
					        let mut source: Option<String> = None;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Parse source component.
 | 
					        // Parse tags component.
 | 
				
			||||||
    if line.chars().nth(idx).unwrap() == ':' {
 | 
					        if line.starts_with('@') {
 | 
				
			||||||
 | 
					            idx = line.find(' ').unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for part in Some(&line[1..idx]).unwrap().split(';') {
 | 
				
			||||||
 | 
					                let kv: Vec<&str> = part.split('=').collect();
 | 
				
			||||||
 | 
					                tags.insert(kv[0].to_string(), kv[1].to_string());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            idx += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Parse source component.
 | 
				
			||||||
 | 
					        if line.chars().nth(idx).unwrap() == ':' {
 | 
				
			||||||
 | 
					            let end_idx = find_index(line, ' ', idx).unwrap();
 | 
				
			||||||
 | 
					            source = Some(line[idx..end_idx].to_string());
 | 
				
			||||||
 | 
					            idx = end_idx + 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Parse command component.
 | 
				
			||||||
        let end_idx = find_index(line, ' ', idx).unwrap();
 | 
					        let end_idx = find_index(line, ' ', idx).unwrap();
 | 
				
			||||||
        source = Some(line[idx..end_idx].to_string());
 | 
					        let command = &line[idx..end_idx];
 | 
				
			||||||
        idx = end_idx + 1;
 | 
					        idx = end_idx + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let c_idx = match find_index(line, ':', idx) {
 | 
				
			||||||
 | 
					            Some(x) => x - 1,
 | 
				
			||||||
 | 
					            None => line.len(),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Parse params component.
 | 
				
			||||||
 | 
					        let mut params: Vec<String> = line[idx..c_idx].split(' ').map(|x| x.to_string()).collect();
 | 
				
			||||||
 | 
					        if c_idx != line.len() {
 | 
				
			||||||
 | 
					            params.push(line[c_idx + 2..].to_string());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        parsed_lines.push_back(Line::new(tags, source, command, params));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Parse command component.
 | 
					    Ok(parsed_lines)
 | 
				
			||||||
    let end_idx = find_index(line, ' ', idx).unwrap();
 | 
					 | 
				
			||||||
    let command = &line[idx..end_idx];
 | 
					 | 
				
			||||||
    idx = end_idx + 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let c_idx = match find_index(line, ':', idx) {
 | 
					 | 
				
			||||||
        Some(x) => x - 1,
 | 
					 | 
				
			||||||
        None => line.len(),
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Parse params component.
 | 
					 | 
				
			||||||
    let mut params: Vec<String> = line[idx..c_idx].split(' ').map(|x| x.to_string()).collect();
 | 
					 | 
				
			||||||
    if c_idx != line.len() {
 | 
					 | 
				
			||||||
        params.push(line[c_idx + 2..].to_string());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Ok(Line::new(tags, source, command, params))
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(test)]
 | 
					#[cfg(test)]
 | 
				
			||||||
| 
						 | 
					@ -187,38 +201,58 @@ 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!";
 | 
				
			||||||
        assert_eq!(line.tags, HashMap::new());
 | 
					        match parse(msg) {
 | 
				
			||||||
        assert_eq!(line.source, None);
 | 
					            Ok(mut x) => {
 | 
				
			||||||
        assert_eq!(line.command, "PRIVMSG");
 | 
					                let line = x.pop_front().unwrap();
 | 
				
			||||||
        assert_eq!(line.params, vec!["#rickastley", "Never gonna give you up!"]);
 | 
					
 | 
				
			||||||
 | 
					                assert_eq!(line.tags, HashMap::new());
 | 
				
			||||||
 | 
					                assert_eq!(line.source, None);
 | 
				
			||||||
 | 
					                assert_eq!(line.command, "PRIVMSG");
 | 
				
			||||||
 | 
					                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!";
 | 
				
			||||||
        assert_eq!(
 | 
					        match parse(msg) {
 | 
				
			||||||
            line.tags,
 | 
					            Ok(mut x) => {
 | 
				
			||||||
            hashmap! {
 | 
					                let line = x.pop_front().unwrap();
 | 
				
			||||||
                String::from("id") => String::from("123"),
 | 
					
 | 
				
			||||||
                String::from("name") => String::from("rick"),
 | 
					                assert_eq!(
 | 
				
			||||||
 | 
					                    line.tags,
 | 
				
			||||||
 | 
					                    hashmap! {
 | 
				
			||||||
 | 
					                        String::from("id") => String::from("123"),
 | 
				
			||||||
 | 
					                        String::from("name") => String::from("rick"),
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                assert_eq!(
 | 
				
			||||||
 | 
					                    line.source,
 | 
				
			||||||
 | 
					                    Some(String::from(":nick!user@host.tmi.twitch.tv"))
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                assert_eq!(line.command, "PRIVMSG");
 | 
				
			||||||
 | 
					                assert_eq!(line.params, vec!["#rickastley", "Never gonna give you up!"]);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        );
 | 
					            Err(e) => {
 | 
				
			||||||
        assert_eq!(
 | 
					                println!("A parsing error occured: {e}");
 | 
				
			||||||
            line.source,
 | 
					                return;
 | 
				
			||||||
            Some(String::from(":nick!user@host.tmi.twitch.tv"))
 | 
					            }
 | 
				
			||||||
        );
 | 
					        };
 | 
				
			||||||
        assert_eq!(line.command, "PRIVMSG");
 | 
					 | 
				
			||||||
        assert_eq!(line.params, vec!["#rickastley", "Never gonna give you up!"]);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[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;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue