diff --git a/README.md b/README.md index cac0b01..8dd7b96 100644 --- a/README.md +++ b/README.md @@ -25,10 +25,13 @@ $router = new cmdr\Cmdr(); //All command functions will have Request as the last argument #[cmdr\attributes\Cmd("example", "altname")] //define as many cmds for this function as you want #[cmdr\attributes\Syntax(" [optional]")] +#[cmdr\attributes\Options("--option", "--anotheroption")] function exampleFunc($additonal, $arguments, cmdr\Request $request) { echo $request->args["required"]; - if(isset($request->args["optional"]) + if(isset($request->args["optional"])) echo $request->args["optional"]; + if($request->getOpt('--option')) + echo "--option was used"; } //Do this AFTER all functions you want to load are defined diff --git a/src/Args.php b/src/Args.php index f98f075..8144448 100644 --- a/src/Args.php +++ b/src/Args.php @@ -13,7 +13,6 @@ */ class Args implements \ArrayAccess, \Countable { - public string $syntax; /** * @var Arg[] $args */ @@ -24,14 +23,20 @@ class Args implements \ArrayAccess, \Countable */ protected array $parsed = Array(); + /** + * @var array $parsedOpts + */ + protected array $parsedOpts = Array(); + /** * constructor. * @param string $syntax + * @param array $opts * @throws SyntaxException Will throw if syntax is invalid */ - function __construct(string $syntax) + function __construct(public string $syntax, protected array $opts = []) { - $this->syntax = $syntax; + $this->opts = array_map('\strtolower', $this->opts); $argv = array_filter(explode(' ', $syntax)); if (count($argv) == 0) { return; @@ -82,6 +87,17 @@ function __construct(string $syntax) * @throws ParseException throws exception if required args arent provided */ public function parse(string $msg) : Args { + $this->parsedOpts = []; + $msg = explode(' ', $msg); + $msgb = []; + foreach ($msg as $w) { + if(in_array($w, $this->opts)) + $this->parsedOpts[$w] = $w; + else + $msgb[] = $w; + } + $msg = implode(' ', $msgb); + $this->parsed = []; foreach ($this->args as $k => $v) $this->parsed[$k] = clone $v; @@ -102,6 +118,14 @@ public function parse(string $msg) : Args { return clone $this; } + public function getOpts() { + return $this->parsedOpts; + } + + public function getOpt($name) : bool { + return isset($this->parsedOpts[strtolower($name)]); + } + public function getArg(string $name): ?Arg { foreach ($this->parsed as &$arg) { if ($arg->name == $name) { diff --git a/src/Cmd.php b/src/Cmd.php index 416b3b5..b8a2a9a 100644 --- a/src/Cmd.php +++ b/src/Cmd.php @@ -11,12 +11,13 @@ public function __construct( public $method, public array $preArgs, public array $postArgs, - public string $syntax + public string $syntax, + public array $opts ) { if(!is_callable($this->method)) { throw new \Exception("Method argument to Cmd isn't callable (" . print_r($method, 1) .")"); } - $this->cmdArgs = new Args($syntax); + $this->cmdArgs = new Args($syntax, $opts); } } diff --git a/src/Cmdr.php b/src/Cmdr.php index 5e85b2b..d7602f9 100644 --- a/src/Cmdr.php +++ b/src/Cmdr.php @@ -12,14 +12,14 @@ public function __construct() $this->cmds = new CIArray(); } - function add(string $command, callable $method, array $preArgs = [], array $postArgs = [], string $syntax = '') { + function add(string $command, callable $method, array $preArgs = [], array $postArgs = [], string $syntax = '', array $opts = []) { if (str_contains($command, '#')) { throw new \Exception('Command name cannot contain #'); } if(isset($this->cmds[$command])) { throw new \Exception('Command already exists'); } - $this->cmds[$command] = new Cmd($command, $method, $preArgs, $postArgs, $syntax); + $this->cmds[$command] = new Cmd($command, $method, $preArgs, $postArgs, $syntax, $opts); } function get(string $command, string $text) : Request|false { @@ -52,18 +52,22 @@ protected function attrAddCmd($rf, $f) { $sa = $syntaxAttr[0]->newInstance(); $syntax = $sa->syntax; } + $callWrapAttr = $rf->getAttributes(attributes\CallWrap::class); - if (isset($callWrapAttr[0])) { - $cw = $callWrapAttr[0]->newInstance(); - $callWrapper = $cw->caller; - $callWrapperPre = $cw->preArgs; + if ($cw = ($callWrapAttr[0]??null)?->newInstance()) { + $callWrapperPre = [...$cw->preArgs, $f]; + $f = $cw->caller; $callWrapperPost = $cw->postArgs; } + + $optionsAttr = $rf->getAttributes(attributes\Options::class); + $opts = []; + if(isset($optionsAttr[0])) + $opts = $optionsAttr[0]->newInstance()->options; + + foreach ($cmdAttr->args as $command) { - if ($callWrapper != null) - $this->cmds[$command] = new Cmd($command, $callWrapper, [...$callWrapperPre, $f], $callWrapperPost, $syntax); - else - $this->cmds[$command] = new Cmd($command, $f, $callWrapperPre, $callWrapperPost, $syntax); + $this->cmds[$command] = new Cmd($command, $f, $callWrapperPre, $callWrapperPost, $syntax, $opts); } } diff --git a/src/attributes/Options.php b/src/attributes/Options.php new file mode 100644 index 0000000..8dda90d --- /dev/null +++ b/src/attributes/Options.php @@ -0,0 +1,14 @@ +options = $options; + } +} diff --git a/tests/ArgsTest.php b/tests/ArgsTest.php index a0e283a..656af38 100644 --- a/tests/ArgsTest.php +++ b/tests/ArgsTest.php @@ -255,4 +255,42 @@ function testArgWhenNotReq() $args->parse('moo boo poo woo'); $this->assertCount(0, $args); } + + + function testOptions() + { + $args = new Args('...', ['--nes']); + $args->parse('moo boo poo'); + $this->assertEquals('moo boo poo', $args[0]); + $this->assertEmpty($args->getOpts()); + $args->parse('moo --nes poo'); + $this->assertEquals('moo poo', $args[0]); + $this->assertEquals(['--nes'=>'--nes'], $args->getOpts()); + + $args = new Args('', ['--nes']); + $args->parse('moo boo poo'); + $this->assertEquals('moo', $args[0]); + $this->assertEmpty($args->getOpts()); + $args->parse('moo --nes poo'); + $this->assertEquals('moo', $args[0]); + $this->assertEquals(['--nes'=>'--nes'], $args->getOpts()); + + $args = new Args('[foo]', ['--nes']); + $args->parse('moo boo poo'); + $this->assertEquals('moo', $args[0]); + $this->assertEmpty($args->getOpts()); + $args->parse('--nes moo'); + $this->assertEquals('moo', $args[0]); + $this->assertTrue($args->getOpt('--nes')); + + $args = new Args('[foo]', ['--nes', '--bar']); + $args->parse('moo --nes boo poo'); + $this->assertEquals('moo', $args[0]); + $this->assertTrue($args->getOpt('--nes')); + $this->assertFalse($args->getOpt('--bar')); + $args->parse('--nes moo --bar'); + $this->assertEquals('moo', $args[0]); + $this->assertTrue($args->getOpt('--nes')); + $this->assertTrue($args->getOpt('--bar')); + } } diff --git a/tests/CmdrTest.php b/tests/CmdrTest.php index 7f87080..c6b6479 100644 --- a/tests/CmdrTest.php +++ b/tests/CmdrTest.php @@ -2,6 +2,7 @@ namespace knivey\cmdr\test; +use knivey\cmdr\attributes\Options; use knivey\cmdr\Cmdr; use knivey\cmdr\Request; use knivey\cmdr\attributes\Cmd; @@ -44,10 +45,11 @@ public function testUsingAdd() $lol = function ($req) use(&$cnt) { $this->assertInstanceOf(Request::class, $req); $this->assertEquals('abc def', $req->args['stuff']); + $this->assertEquals(['--bar'], $req->args->getOpts()); $cnt++; }; - $cmdr->add('test', $lol, syntax: '...'); - $cmdr->call('test', 'abc def'); + $cmdr->add('test', $lol, syntax: '...', opts: ['--bar']); + $cmdr->call('test', 'abc def --bar'); $this->assertEquals(1, $cnt); } @@ -91,10 +93,11 @@ public function testLoadFuncs() $testFunc = function ($req) use(&$cnt) { $this->assertEquals('abc def', $req->args['foo']); + $this->assertTrue($req->args->getOpt('--bar')); $cnt++; }; $cmdr->loadFuncs(); - $cmdr->call('testattrs', 'abc def'); + $cmdr->call('testattrs', 'abc --bar def'); $cmdr->call('noexist', 'abc def'); $this->assertEquals(1, $cnt); } @@ -167,6 +170,7 @@ public function testCallWrapAttribute() #[Cmd("testAttrs")] #[Syntax("...")] +#[Options("--bar")] function testAttrs(...$args) { global $testFunc; $testFunc(...$args);