From 9f0f47abaa934b66da5b302236bfc89f95a7f329 Mon Sep 17 00:00:00 2001 From: Justin Wernick Date: Mon, 27 Feb 2023 23:12:32 +0200 Subject: Revamp parsing to support more complex commands --- src/parser.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/parser.rs (limited to 'src/parser.rs') diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..2f65180 --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,61 @@ +use nom::{ + branch::alt, + bytes::complete::{is_not, tag}, + character::complete::{multispace0, multispace1}, + combinator::{eof, map, value}, + error::ParseError, + sequence::delimited, + sequence::tuple, + Finish, IResult, +}; +use std::str::FromStr; + +#[derive(Clone)] +pub enum Command { + Whitespace, + Exit, + GitInit(String), +} + +impl FromStr for Command { + // the error must be owned as well + type Err = String; + + fn from_str(s: &str) -> Result { + match command_parser(s).finish() { + Ok((remaining, command)) => { + if remaining.trim().is_empty() { + Ok(command) + } else { + Err(s.trim().to_owned()) + } + } + Err(_) => Err(s.trim().to_owned()), + } + } +} + +fn command_parser(input: &str) -> IResult<&str, Command> { + alt(( + value(Command::Exit, eof), + value(Command::Whitespace, tuple((multispace1, eof))), + value(Command::Exit, ws(tag("exit"))), + map( + ws(tuple((tag("git-init"), multispace1, not_space))), + |(_, _, repo_name)| Command::GitInit(repo_name.to_owned()), + ), + ))(input) +} + +/// A combinator that takes a parser `inner` and produces a parser that also consumes both leading and +/// trailing whitespace, returning the output of `inner`. +fn ws<'a, F, O, E: ParseError<&'a str>>(inner: F) -> impl FnMut(&'a str) -> IResult<&'a str, O, E> +where + F: FnMut(&'a str) -> IResult<&'a str, O, E>, +{ + delimited(multispace0, inner, multispace0) +} + +fn not_space(s: &str) -> IResult<&str, &str> { + is_not(" \t\r\n")(s) +} -- cgit v1.2.3