Created
April 15, 2019 20:11
-
-
Save cfcosta/6f326edafd3bda30f9d816f0e54a716e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use nom::is_alphabetic; | |
use std::str; | |
#[derive(Debug, PartialEq, Eq)] | |
pub enum Keyword { | |
Unknown, | |
Help, | |
Echo, | |
} | |
#[derive(Debug, PartialEq, Eq)] | |
pub enum Command { | |
Unknown, | |
Help, | |
Echo(String), | |
} | |
fn to_keyword(s: &[u8]) -> Keyword { | |
if compare_no_case(&s, b"help") { | |
return Keyword::Help; | |
} else if compare_no_case(&s, b"echo") { | |
return Keyword::Echo; | |
} | |
Keyword::Unknown | |
} | |
fn is_argument_char(i: u8) -> bool { | |
!b"\r\n".contains(&i) | |
} | |
pub fn to_command(keyword: Keyword, argument: &[u8]) -> Command { | |
match keyword { | |
Keyword::Unknown => Command::Unknown, | |
Keyword::Help => Command::Help, | |
Keyword::Echo => Command::Echo(str::from_utf8(argument).unwrap().into()), | |
} | |
} | |
fn is_token_char(i: u8) -> bool { | |
is_alphabetic(i) || b"_-".contains(&i) | |
} | |
pub fn compare_no_case(left: &[u8], right: &[u8]) -> bool { | |
if left.len() != right.len() { | |
return false; | |
} | |
left.iter().zip(right).all(|(a, b)| match (*a, *b) { | |
(0...64, 0...64) | (91...96, 91...96) | (123...255, 123...255) => a == b, | |
(65...90, 65...90) | (97...122, 97...122) | (65...90, 97...122) | (97...122, 65...90) => { | |
*a | 0b00_10_00_00 == *b | 0b00_10_00_00 | |
} | |
_ => false, | |
}) | |
} | |
named!(eoc, alt!(tag!("\n") | tag!("\r\n"))); | |
named!(space, alt!(tag!(" ") | tag!("\t"))); | |
named!(keyword<&[u8], Keyword>, map!(take_while!(is_token_char), to_keyword)); | |
named!(argument, take_while!(is_argument_char)); | |
named!(command<&[u8], Command>, | |
do_parse!( | |
tag!("!") >> | |
keyword: keyword >> | |
opt!(space) >> | |
argument: argument >> | |
eoc >> | |
(to_command(keyword, argument)) | |
) | |
); | |
pub fn parse(input: &str) -> Result<Command, nom::Err<&[u8]>> { | |
let result = command(input.as_bytes())?; | |
Ok(result.1) | |
} | |
#[cfg(test)] | |
mod tests { | |
use crate::parser::*; | |
#[test] | |
fn parse_command_with_crlf() { | |
assert_eq!(parse("!help\r\n"), Ok(Command::Help)); | |
} | |
#[test] | |
fn parse_command_with_lf() { | |
assert_eq!(parse("!help\n"), Ok(Command::Help)); | |
} | |
#[test] | |
fn parse_unknown_command() { | |
assert_eq!(parse("!omg\n"), Ok(Command::Unknown)); | |
} | |
#[test] | |
fn parse_command_with_argument() { | |
assert_eq!(parse("!echo omg\n"), Ok(Command::Echo("omg".into()))); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment