summaryrefslogtreecommitdiff
path: root/src/commands/parser.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/commands/parser.rs')
-rw-r--r--src/commands/parser.rs77
1 files changed, 77 insertions, 0 deletions
diff --git a/src/commands/parser.rs b/src/commands/parser.rs
new file mode 100644
index 0000000..e75840c
--- /dev/null
+++ b/src/commands/parser.rs
@@ -0,0 +1,77 @@
+
+pub enum ParseResult<'a, T> {
+ Success(T, &'a str),
+ Failure(String),
+}
+
+pub trait Parse<'a> {
+ type Res;
+
+ fn parse(self, input: &'a str) -> ParseResult<'a, Self::Res>;
+}
+
+pub struct Text(String);
+
+impl<'a> Parse<'a> for Text {
+ type Res = Text;
+
+ fn parse(self, input: &'a str) -> ParseResult<'a, Text> {
+ let mut value = String::new();
+
+ let mut it = input.chars();
+ let mut escape_next = false;
+ while let Some(c) = it.next() {
+ if c == '\\' && !escape_next {
+ escape_next = true;
+ continue;
+ } else if c == ';' {
+ break;
+ }
+ value.push(c);
+
+ escape_next = false;
+ }
+
+ ParseResult::Success(Text(value), it.as_str())
+ }
+}
+
+pub struct Branch<T> {
+ choices: Vec<(&'static str, Option<char>, T)>,
+}
+
+impl<'a, T> Parse<'a> for Branch<T> {
+ type Res = T;
+
+ fn parse(self, input: &'a str) -> ParseResult<'a, T> {
+ let choices: Vec<&'static str> = self.choices.iter().map(|x| x.0).collect();
+
+ for (long, short_op, res) in self.choices {
+ let rest = if input.starts_with(long) {
+ Some(&input[long.len()..])
+ } else if let Some(short) = short_op {
+ if input.starts_with(short) {
+ Some(&input[1..])
+ } else {
+ None
+ }
+ } else { None };
+
+ if let Some(rest) = rest {
+ return ParseResult::Success(res, rest);
+ }
+ }
+
+ return ParseResult::Failure(format!("Expected one of [{}]", choices.join(", ")));
+ }
+}
+
+impl<T> Branch<T> {
+ pub fn new() -> Branch<T> {
+ Branch { choices: Vec::new() }
+ }
+
+ pub fn add(&mut self, long: &'static str, short: Option<char>, res: T) {
+ self.choices.push((long, short, res));
+ }
+}