summaryrefslogtreecommitdiff
path: root/src/parser.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser.rs')
-rw-r--r--src/parser.rs132
1 files changed, 75 insertions, 57 deletions
diff --git a/src/parser.rs b/src/parser.rs
index 75ac333..ef7d5f8 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -1,72 +1,90 @@
-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 clap::{Parser, Subcommand};
use std::str::FromStr;
-// TODO: It might work well to use clap and parse_from for this particular case
-#[derive(Clone)]
-pub enum Command {
+#[derive(Parser, Clone, Debug, PartialEq, Eq)]
+#[command(name = "")]
+pub enum ShackleCommand {
+ #[command(skip)]
Whitespace,
Exit,
- GitInit(String),
- GitUploadPack(String),
- GitReceivePack(String),
+ GitInit(GitInitArgs),
+ GitUploadPack(GitUploadPackArgs),
}
-impl FromStr for Command {
- // the error must be owned as well
- type Err = String;
+#[derive(Subcommand, Clone, Debug, PartialEq, Eq)]
+pub enum GitCommand {
+ UploadPack(GitUploadPackArgs),
+}
+
+#[derive(Parser, Clone, Debug, PartialEq, Eq)]
+pub struct GitInitArgs {
+ pub repo_name: String,
+}
+
+#[derive(Parser, Clone, Debug, PartialEq, Eq)]
+pub struct GitUploadPackArgs {
+ #[arg(long)]
+ pub strict: bool,
+ #[arg(long)]
+ pub no_strict: bool,
+ #[arg(long)]
+ pub timeout: Option<u32>,
+ #[arg(long)]
+ pub stateless_rpc: bool,
+ #[arg(long)]
+ pub advertise_refs: bool,
+ pub directory: String,
+}
+
+impl FromStr for ShackleCommand {
+ type Err = clap::error::Error;
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()),
+ let trimmed = s.trim();
+ if s.len() == 0 {
+ Ok(ShackleCommand::Exit)
+ } else if trimmed.len() == 0 {
+ Ok(ShackleCommand::Whitespace)
+ } else {
+ ShackleCommand::try_parse_from([""].into_iter().chain(trimmed.split_whitespace()))
}
}
}
-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()),
- ),
- map(
- ws(tuple((tag("git upload-pack"), multispace1, not_space))),
- |(_, _, git_dir)| Command::GitUploadPack(git_dir.to_owned()),
- ),
- map(
- ws(tuple((tag("git receive-pack"), multispace1, not_space))),
- |(_, _, git_dir)| Command::GitReceivePack(git_dir.to_owned()),
- ),
- ))(input)
-}
+#[cfg(test)]
+mod test {
+ use super::*;
-/// 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)
-}
+ #[test]
+ fn it_parses_exit_correctly() {
+ assert_eq!(
+ "exit".parse::<ShackleCommand>().unwrap(),
+ ShackleCommand::Exit
+ );
+ }
-fn not_space(s: &str) -> IResult<&str, &str> {
- is_not(" \t\r\n")(s)
+ #[test]
+ fn it_parses_git_upload_pack_correctly() {
+ assert_eq!(
+ "git-upload-pack --stateless-rpc foobar.git"
+ .parse::<ShackleCommand>()
+ .unwrap(),
+ ShackleCommand::GitUploadPack(GitUploadPackArgs {
+ strict: false,
+ no_strict: false,
+ timeout: None,
+ stateless_rpc: true,
+ advertise_refs: false,
+ directory: "foobar.git".to_owned(),
+ })
+ );
+ }
+
+ #[test]
+ fn it_parses_whitespace_correctly() {
+ assert_eq!(
+ " ".parse::<ShackleCommand>().unwrap(),
+ ShackleCommand::Whitespace
+ );
+ }
}