From ce86cfe354b6b755c7db68e65f895f8947cdf33e Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Mon, 15 Sep 2025 22:50:50 +0400 Subject: [PATCH] modifs.mineures sans commentaires --- src/app/cli/Arg.php | 194 +++++++++++++++++++++++++++++++++++ src/app/cli/ArgException.php | 7 ++ tests/app/cli/ArgTest.php | 48 +++++++++ 3 files changed, 249 insertions(+) create mode 100644 src/app/cli/Arg.php create mode 100644 src/app/cli/ArgException.php create mode 100644 tests/app/cli/ArgTest.php diff --git a/src/app/cli/Arg.php b/src/app/cli/Arg.php new file mode 100644 index 0000000..4a7dd9c --- /dev/null +++ b/src/app/cli/Arg.php @@ -0,0 +1,194 @@ +_args = cl::withn($args); + + $this->argsdesc = $params["argsdesc"] ?? null; + + $extends = $params["extends"] ?? null; + if ($extends !== null) { + A::merge($extends["add"], $options); + $this->extends = $extends; + $this->processExtends(); + #XXX à terme, processExtends() est appelé par ArgsParser après le + # chargement de tous les arguments, parce que [arg] peut-être une + # référence e.g ["extends" => ["arg" => "-o", "add" => ["--longo"]]] + } else { + $this->addOptions($options); + } + } + + protected ?array $options = []; + + function getOptions(): array { + return array_keys($this->options); + } + + public bool $haveShortOptions = false; + public bool $haveLongOptions = false; + public bool $haveCommands = false; + + public bool $haveArgs = false; + public ?int $minArgs = null; + public ?int $maxArgs = null; + public ?string $argsdesc = null; + + function addOptions(?array $options): void { + if ($options === null) return; + foreach ($options as $option) { + if (substr($option, 0, 2) === "--") { + $type = self::TYPE_LONG; + if (preg_match('/^--([^:-]+)(::?)?$/', $option, $ms)) { + $name = $ms[1]; + $args = $ms[2] ?? null; + $option = "--$name"; + } else { + throw new ArgException("$option: invalid long option"); + } + } elseif (substr($option, 0, 1) === "-") { + $type = self::TYPE_SHORT; + if (preg_match('/^-([^:-])(::?)?$/', $option, $ms)) { + $name = $ms[1]; + $args = $ms[2] ?? null; + $option = "-$name"; + } else { + throw new ArgException("$option: invalid short option"); + } + } else { + $type = self::TYPE_COMMAND; + if (preg_match('/^([^:-]+)$/', $option, $ms)) { + $name = $ms[1]; + $args = null; + $option = "$name"; + } else { + throw new ArgException("$option: invalid command"); + } + } + if ($args === ":") { + $argsType = self::ARGS_MANDATORY; + } elseif ($args === "::") { + $argsType = self::ARGS_OPTIONAL; + } else { + $argsType = self::ARGS_NONE; + } + $this->options[$option] = [ + "name" => $name, + "option" => $option, + "type" => $type, + "args_type" => $argsType, + ]; + } + $this->updateType(); + } + + function removeOptions(?array $options): void { + if ($options === null) return; + foreach ($options as $option) { + if (substr($option, 0, 2) === "--") { + if (preg_match('/^--([^:-]+)(::?)?$/', $option, $ms)) { + $name = $ms[1]; + $option = "--$name"; + } else { + throw new ArgException("$option: invalid long option"); + } + } elseif (substr($option, 0, 1) === "-") { + if (preg_match('/^-([^:-])(::?)?$/', $option, $ms)) { + $name = $ms[1]; + $option = "-$name"; + } else { + throw new ArgException("$option: invalid short option"); + } + } else { + if (preg_match('/^([^:-]+)$/', $option, $ms)) { + $name = $ms[1]; + $option = "$name"; + } else { + throw new ArgException("$option: invalid command"); + } + } + unset($this->options[$option]); + } + $this->updateType(); + } + + protected ?array $extends; + + function processExtends(): void { + $extends = $this->extends; + $base = $extends["arg"] ?? null; + if ($base === null) return; + $base = new self($base); + $this->options = $base->options; + $this->removeOptions(varray::withn($extends["remove"] ?? null)); + $this->addOptions(varray::withn($extends["add"] ?? null)); + } + + protected function updateType(): void { + $haveShortOptions = false; + $haveLongOptions = false; + $haveCommands = false; + foreach ($this->options as $option) { + switch ($option["type"]) { + case self::TYPE_SHORT: + $haveShortOptions = true; + break; + case self::TYPE_LONG: + $haveLongOptions = true; + break; + case self::TYPE_COMMAND: + $haveCommands = true; + break; + } + } + $this->haveShortOptions = $haveShortOptions; + $this->haveLongOptions = $haveLongOptions; + $this->haveCommands = $haveCommands; + } + + protected ?array $_args = null; + + protected function updateArgs(): void { + if ($this->_args === null) { + $haveArgs = false; + $optionalArgs = null; + foreach ($this->options as $option) { + switch ($option["args_type"]) { + case self::ARGS_NONE: + break; + case self::ARGS_MANDATORY: + $haveArgs = true; + $optionalArgs = false; + break; + case self::ARGS_OPTIONAL: + $haveArgs = true; + $optionalArgs ??= true; + break; + } + } + $optionalArgs ??= false; + if ($haveArgs) { + $args = ["value"]; + if ($optionalArgs) $args = [$args]; + } + } + #XXX calculer minArgs, maxArgs, argsdesc + $this->haveArgs = $haveArgs; + $this->_args = $args; + $this->argsdesc = $argsdesc; + } +} diff --git a/src/app/cli/ArgException.php b/src/app/cli/ArgException.php new file mode 100644 index 0000000..2a96218 --- /dev/null +++ b/src/app/cli/ArgException.php @@ -0,0 +1,7 @@ +getOptions()); + self::assertFalse($arg->haveArgs); + self::assertFalse($arg->optionalArgs); + + $arg = new Arg(["-o:", "--longo"]); + self::assertSame(["-o", "--longo"], $arg->getOptions()); + self::assertTrue($arg->haveArgs); + self::assertFalse($arg->optionalArgs); + + $arg = new Arg(["-o::", "--longo"]); + self::assertSame(["-o", "--longo"], $arg->getOptions()); + self::assertTrue($arg->haveArgs); + self::assertTrue($arg->optionalArgs); + + $arg = new Arg(["-o:", "--longo:"]); + self::assertSame(["-o", "--longo"], $arg->getOptions()); + self::assertTrue($arg->haveArgs); + self::assertFalse($arg->optionalArgs); + + $arg = new Arg(["-o:", "--longo::"]); + self::assertSame(["-o", "--longo"], $arg->getOptions()); + self::assertTrue($arg->haveArgs); + self::assertFalse($arg->optionalArgs); + } + + function testExtends() { + $basedef = ["-o:", "--longo"]; + $def = [ + "extends" => [ + "arg" => $basedef, + "add" => ["-a", "--longa"], + "remove" => ["-o", "--longo"], + ], + ]; + $arg = new Arg($def); + self::assertSame(["-a", "--longa"], $arg->getOptions()); + self::assertFalse($arg->haveArgs); + self::assertFalse($arg->optionalArgs); + } +}