diff --git a/Makefile b/Makefile index 7b02c9e..4bcd78e 100644 --- a/Makefile +++ b/Makefile @@ -24,5 +24,5 @@ format: .PHONY: spec spec: format - ./crystal spec --no-debug --warnings all --error-on-warnings + ./crystal spec --no-debug --warnings all --error-on-warnings --error-trace diff --git a/spec/command_router_spec.cr b/spec/command_router_spec.cr index dfd2154..681f001 100644 --- a/spec/command_router_spec.cr +++ b/spec/command_router_spec.cr @@ -2,13 +2,42 @@ require "./spec_helper" require "../src/cli/*" describe CLI::CommandRouter do - it "should handle simple command" do + it "should handle simple command as block" do router = CLI::CommandRouter.new x = 10 - router.add "plus" do + router.add "plus" do |p| x += 5 end router.handle "plus" x.should eq 15 end + + it "should handle simple command as proc" do + router = CLI::CommandRouter.new + x = 10 + cb = ->(params : Hash(String, String)) { x += 5 } + router.add "plus", &cb + router.handle "plus" + x.should eq 15 + end + + it "should handle command with argument" do + router = CLI::CommandRouter.new + x = 10 + router.add "plus {x}" do |params| + x += params["x"].to_i32 + end + router.handle "plus 5" + x.should eq 15 + end + + it "should handle command with three arguments" do + router = CLI::CommandRouter.new + x = 0 + router.add "plus {x} {y} {z}" do |p| + x = p["x"].to_i32 + p["y"].to_i32 + p["z"].to_i32 + end + router.handle "plus 1 3 6" + x.should eq 10 + end end diff --git a/src/cli/command_router.cr b/src/cli/command_router.cr index dc84da8..6f3dd24 100644 --- a/src/cli/command_router.cr +++ b/src/cli/command_router.cr @@ -1,17 +1,42 @@ class CLI::CommandRouter def initialize - @mappings = [] of {String, Proc(Nil)} + @mappings = [] of {Regex, Proc(Hash(String, String), Nil)} end - def add(route, &block) - @mappings.push({route, block}) + def add(route, &block : Hash(String, String) -> Nil) + pattern = route_to_regex(route) + @mappings.push({pattern, block}) end def handle(command) - @mappings.each do |i| - if i[0] == command - i[1].call - end + @mappings.each do |handler| + handle_pattern(command, *handler) end end + + private def handle_pattern(command, pattern, cb) + m = command.match(pattern) + return if m.nil? + groups = m.named_captures + nil_groups = groups.select { |k, v| v.nil? } + return if nil_groups.size != 0 + params = groups.transform_values { |v| v.to_s } + cb.call params + end + + private def route_to_regex(route) : Regex + vals = route.partition(/\{[a-z]+?\}/) + param = vals[1].lstrip('{').rstrip('}') + result = "" + if vals[0] != "" + result += Regex.escape(vals[0]) + end + if vals[1] != "" + result += sprintf "(?P<%s>.+?)", param + end + if vals[2] != "" + result += route_to_regex(vals[2]).source + end + Regex.new(result) + end end