Fix bug with line endings

This commit is contained in:
Ethan Henderson 2022-08-02 02:17:11 +01:00
parent e8a553170b
commit 6001224339
2 changed files with 40 additions and 12 deletions

View file

@ -35,7 +35,7 @@
//! You can parse IRC messages using the provided `parse` function. //! You can parse IRC messages using the provided `parse` function.
//! //!
//! ``` //! ```
//! 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!\r\n";
//! match ircparser::parse(msg) { //! match ircparser::parse(msg) {
//! Ok(mut x) => { //! Ok(mut x) => {
//! let line = x.pop_front().unwrap(); //! let line = x.pop_front().unwrap();
@ -81,6 +81,7 @@ impl ParseError {
/// ///
/// assert_eq!(e.details, "err".to_string()) /// assert_eq!(e.details, "err".to_string())
/// ``` /// ```
///
pub fn new(details: &str) -> Self { pub fn new(details: &str) -> Self {
Self { Self {
details: details.into(), details: details.into(),
@ -112,11 +113,12 @@ fn find_index(text: &str, char: char, start: usize) -> Option<usize> {
/// will be a separate element in the return value. /// will be a separate element in the return value.
/// ///
/// # Returns /// # Returns
/// - [`VecDeque<Line>`] - A [`VecDeque`] of all parsed [`Line`]s. /// - [`VecDeque<Line>`] - A [`VecDeque`] of all parsed [`Line`]s. This
/// will be empty if no valid lines were passed and no errors occur.
/// ///
/// # 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!\r\n";
/// ///
/// match ircparser::parse(msg) { /// match ircparser::parse(msg) {
/// Ok(mut x) => { /// Ok(mut x) => {
@ -141,12 +143,16 @@ fn find_index(text: &str, char: char, start: usize) -> Option<usize> {
/// The behaviour of this function changed in v0.2.0. It can now accept /// 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 /// multiple lines at once, but as a consequence, now returns a
/// [`VecDeque`] of [`Line`] objects instead of a single [`Line`]. /// [`VecDeque`] of [`Line`] objects instead of a single [`Line`].
///
pub fn parse(text: &str) -> ParseResult<VecDeque<Line>> { pub fn parse(text: &str) -> ParseResult<VecDeque<Line>> {
let mut parsed_lines: VecDeque<Line> = VecDeque::new(); let mut parsed_lines: VecDeque<Line> = VecDeque::new();
for line in text.replace('\r', "").split('\n') { for line in text.split("\r\n") {
if line.is_empty() { if line.is_empty() {
return Err(ParseError::new("line length cannot be 0")); // If the line length is 0, we can assume the previous line
// ended in \r\n, and that this line doesn't need to be
// processed.
continue;
} }
let mut idx = 0; let mut idx = 0;
@ -202,9 +208,11 @@ mod test_lib {
#[test] #[test]
fn test_single_partial() { fn test_single_partial() {
let msg = "PRIVMSG #rickastley :Never gonna give you up!"; let msg = "PRIVMSG #rickastley :Never gonna give you up!\r\n";
match parse(msg) { match parse(msg) {
Ok(mut x) => { Ok(mut x) => {
assert_eq!(x.len(), 1);
let line = x.pop_front().unwrap(); let line = x.pop_front().unwrap();
assert_eq!(line.tags, HashMap::new()); assert_eq!(line.tags, HashMap::new());
@ -214,16 +222,17 @@ mod test_lib {
} }
Err(e) => { Err(e) => {
println!("A parsing error occured: {e}"); println!("A parsing error occured: {e}");
return; assert!(false);
} }
} }
} }
#[test] #[test]
fn test_single_full() { fn test_single_full() {
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!\r\n";
match parse(msg) { match parse(msg) {
Ok(mut x) => { Ok(mut x) => {
assert_eq!(x.len(), 1);
let line = x.pop_front().unwrap(); let line = x.pop_front().unwrap();
assert_eq!( assert_eq!(
@ -249,9 +258,11 @@ mod test_lib {
#[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!\r\n";
match parse(msg) { match parse(msg) {
Ok(mut x) => { Ok(mut x) => {
println!("{x:?}");
assert_eq!(x.len(), 1);
let line = x.pop_front().unwrap(); let line = x.pop_front().unwrap();
assert_eq!(&line.tags["id"], "123"); assert_eq!(&line.tags["id"], "123");
@ -264,14 +275,27 @@ mod test_lib {
} }
Err(e) => { Err(e) => {
println!("A parsing error occured: {e}"); println!("A parsing error occured: {e}");
return; assert!(false);
}
};
}
#[test]
fn test_empty() {
match parse("") {
Ok(x) => {
assert_eq!(x.len(), 0);
}
Err(e) => {
println!("A parsing error occured: {e}");
assert!(false);
} }
}; };
} }
#[test] #[test]
fn test_multiline() { fn test_multiline() {
let msg = "@id=123 PRIVMSG #rickastley :Never gonna give you up!\n@id=456 PRIVMSG #rickastley :Never gonna let you down!"; let msg = "@id=123 PRIVMSG #rickastley :Never gonna give you up!\r\n@id=456 PRIVMSG #rickastley :Never gonna let you down!\r\n";
match parse(msg) { match parse(msg) {
Ok(mut x) => { Ok(mut x) => {
assert_eq!(x.len(), 2); assert_eq!(x.len(), 2);
@ -287,7 +311,7 @@ mod test_lib {
} }
Err(e) => { Err(e) => {
println!("A parsing error occured: {e}"); println!("A parsing error occured: {e}");
return; assert!(false);
} }
} }
} }

View file

@ -36,11 +36,14 @@ pub struct Line {
/// This line's tags. This will be an empty hashmap if there are /// This line's tags. This will be an empty hashmap if there are
/// none. /// none.
pub tags: HashMap<String, String>, pub tags: HashMap<String, String>,
/// This line's source (including the nick, user, and host). This is /// This line's source (including the nick, user, and host). This is
/// optional, and will be [`None`] if not provided. /// optional, and will be [`None`] if not provided.
pub source: Option<String>, pub source: Option<String>,
/// This line's command. /// This line's command.
pub command: String, pub command: String,
/// Any parameters passed to the command. This will be an empty /// Any parameters passed to the command. This will be an empty
/// vector if there are none. /// vector if there are none.
pub params: Vec<String>, pub params: Vec<String>,
@ -78,6 +81,7 @@ impl Line {
/// assert_eq!(line.command, "PRIVMSG"); /// assert_eq!(line.command, "PRIVMSG");
/// assert_eq!(line.params[0], "#rickastley"); /// assert_eq!(line.params[0], "#rickastley");
/// ``` /// ```
///
pub fn new( pub fn new(
tags: HashMap<String, String>, tags: HashMap<String, String>,
source: Option<String>, source: Option<String>,