summaryrefslogtreecommitdiff
path: root/src/parser.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser.rs')
-rw-r--r--src/parser.rs61
1 files changed, 61 insertions, 0 deletions
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<Self, Self::Err> {
+ 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)
+}