modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2025-09-15 22:50:50 +04:00
parent b0f5de5e9f
commit ce86cfe354
3 changed files with 249 additions and 0 deletions

194
src/app/cli/Arg.php Normal file
View File

@ -0,0 +1,194 @@
<?php
namespace nulib\app\cli;
use nulib\A;
use nulib\cl;
use nulib\php\types\varray;
class Arg {
const TYPE_SHORT = 0, TYPE_LONG = 1, TYPE_COMMAND = 2;
const ARGS_NONE = 0, ARGS_MANDATORY = 1, ARGS_OPTIONAL = 2;
function __construct(array $def) {
[$options, $params] = cl::split_assoc($def);
$args = $params["args"] ?? null;
$args ??= $params["arg"] ?? null;
if ($args === true) $args = 1;
if (is_int($args)) $args = array_fill(0, $args, "value");
$this->_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;
}
}

View File

@ -0,0 +1,7 @@
<?php
namespace nulib\app\cli;
use nulib\ValueException;
class ArgException extends ValueException {
}

48
tests/app/cli/ArgTest.php Normal file
View File

@ -0,0 +1,48 @@
<?php
namespace nulib\app\cli;
use nur\t\TestCase;
class ArgTest extends TestCase {
function testBase() {
$arg = new Arg(["-o", "--longo"]);
self::assertSame(["-o", "--longo"], $arg->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);
}
}