modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2025-09-22 10:39:21 +04:00
parent e4d5860d8e
commit 737f0b411b
8 changed files with 249 additions and 138 deletions

View File

@ -12,27 +12,61 @@ class ArgDef {
const ARGS_NONE = 0, ARGS_MANDATORY = 1, ARGS_OPTIONAL = 2; const ARGS_NONE = 0, ARGS_MANDATORY = 1, ARGS_OPTIONAL = 2;
const ACTION_SET = 0, ACTION_INC = 1, ACTION_DEC = 2, ACTION_FUNC = 3; const ACTION_SET = 0, ACTION_INC = 1, ACTION_DEC = 2, ACTION_FUNC = 3;
protected static function parse_def(ArgDef $dest, array $def): void { function __construct(array $def) {
$this->def = $def;
$this->mergeParse($def);
}
protected array $def;
protected function mergeParse(array $def): void {
$defaults = $defs["defaults"] ?? null;
if ($defaults !== null) $this->mergeParse($defaults);
$this->parse($def);
$merges = $defs["merges"] ?? null;
$merge = $defs["merge"] ?? null;
if ($merge !== null) $merges[] = $merge;
if ($merges !== null) {
foreach ($merges as $merge) {
if ($merge !== null) $this->mergeParse($merge);
}
}
}
protected ?bool $ensureArray = null;
protected ?int $action = null;
protected ?func $func = null;
protected ?bool $inverse = null;
protected $value = null;
protected ?string $name = null;
protected ?string $property = null;
protected ?string $key = null;
protected function parse(array $def): void {
[$options, $params] = cl::split_assoc($def); [$options, $params] = cl::split_assoc($def);
$this->extends = $params["extends"] ?? null;
$args = $params["args"] ?? null; $args = $params["args"] ?? null;
$args ??= $params["arg"] ?? null; $args ??= $params["arg"] ?? null;
if ($args === true) $args = 1; if ($args === true) $args = 1;
elseif ($args === "*") $args = [null]; elseif ($args === "*") $args = [null];
elseif ($args === "+") $args = ["value", null]; elseif ($args === "+") $args = ["value", null];
if (is_int($args)) $args = array_fill(0, $args, "value"); if (is_int($args)) $args = array_fill(0, $args, "value");
$dest->args ??= cl::withn($args); $this->args ??= cl::withn($args);
$dest->argsdesc ??= $params["argsdesc"] ?? null; $this->argsdesc ??= $params["argsdesc"] ?? null;
$this->disabled = vbool::withn($params["disabled"] ?? null);
$adds = varray::withn($params["add"] ?? null); $adds = varray::withn($params["add"] ?? null);
A::merge($dest->adds, $adds); A::merge($this->adds, $adds);
A::merge($dest->adds, $options); A::merge($this->adds, $options);
$removes = varray::withn($params["remove"] ?? null); $removes = varray::withn($params["remove"] ?? null);
A::merge($dest->removes, $removes); A::merge($this->removes, $removes);
$dest->disabled = vbool::withn($params["disabled"] ?? null);
$dest->ensureArray ??= $params["ensure_array"] ?? null; $this->ensureArray ??= $params["ensure_array"] ?? null;
$action = $params["action"] ?? null; $action = $params["action"] ?? null;
if ($action !== null) { if ($action !== null) {
$func = null; $func = null;
@ -51,73 +85,45 @@ class ArgDef {
$action = self::ACTION_FUNC; $action = self::ACTION_FUNC;
break; break;
} }
$dest->action ??= $action; $this->action ??= $action;
$dest->func ??= $func; $this->func ??= $func;
} }
$dest->inverse ??= $params["inverse"] ?? false; $this->inverse ??= $params["inverse"] ?? null;
$dest->value ??= $params["value"] ?? null; $this->value ??= $params["value"] ?? null;
$dest->name ??= $params["name"] ?? null; $this->name ??= $params["name"] ?? null;
$dest->property ??= $params["property"] ?? null; $this->property ??= $params["property"] ?? null;
$dest->key ??= $params["key"] ?? null; $this->key ??= $params["key"] ?? null;
$dest->help ??= $params["help"] ?? null; $this->help ??= $params["help"] ?? null;
} }
protected static function merge_parse_def(ArgDef $dest, array $def, ?ArgDefs $argDefs=null): void {
$defaults = $defs["defaults"] ?? null;
if (is_string($defaults)) $defaults = $argDefs->getArgDef($defaults)->origDef;
if ($defaults !== null) self::merge_parse_def($dest, $defaults);
self::parse_def($dest, $def);
$merges = $defs["merges"] ?? null;
$merge = $defs["merge"] ?? null;
if ($merge !== null) $merges[] = $merge;
if ($merges !== null) {
foreach ($merges as $merge) {
if (is_string($merge)) $merge = $argDefs->getArgDef($merge)->origDef;
if ($merge !== null) self::merge_parse_def($dest, $merge);
}
}
}
function __construct(array $def, ?ArgDefs $argDefs=null) {
$this->origDef = $def;
self::merge_parse_def($this, $def, $argDefs);
}
protected array $origDef;
public bool $isHelp = false; public bool $isHelp = false;
public bool $isRemains = false; public bool $isRemains = false;
public bool $haveShortOptions = false; public bool $haveShortOptions = false;
public bool $haveLongOptions = false; public bool $haveLongOptions = false;
public bool $haveCommands = false; public bool $isCommand = false;
public bool $haveArgs = false; public bool $haveArgs = false;
public ?int $minArgs = null; public ?int $minArgs = null;
public ?int $maxArgs = null; public ?int $maxArgs = null;
public ?string $argsdesc = null; public ?string $argsdesc = null;
protected ?bool $disabled = null;
protected ?array $options = []; protected ?array $options = [];
function getOptions(): array { function getOptions(): array {
return array_keys($this->options); if ($this->disabled) return [];
else return array_keys($this->options);
} }
protected ?array $removes = null; protected ?array $removes = null;
protected ?array $adds = null; protected ?array $adds = null;
protected ?bool $disabled = null;
/** traiter le paramètre parent */ /** traiter le paramètre parent */
function processOptions(): void { function processOptions(): void {
if ($this->disabled) {
$this->options = [];
} else {
$this->removeOptions($this->removes); $this->removeOptions($this->removes);
$this->addOptions($this->adds); $this->addOptions($this->adds);
} }
}
function addOptions(?array $options): void { function addOptions(?array $options): void {
if ($options === null) return; if ($options === null) return;
@ -205,7 +211,7 @@ class ArgDef {
protected function updateType(): void { protected function updateType(): void {
$haveShortOptions = false; $haveShortOptions = false;
$haveLongOptions = false; $haveLongOptions = false;
$haveCommands = false; $isCommand = false;
$isHelp = false; $isHelp = false;
$isRemains = true; $isRemains = true;
foreach ($this->options as $option) { foreach ($this->options as $option) {
@ -218,18 +224,48 @@ class ArgDef {
$haveLongOptions = true; $haveLongOptions = true;
break; break;
case self::TYPE_COMMAND: case self::TYPE_COMMAND:
$haveCommands = true; $isCommand = true;
break; break;
} }
if ($option["option"] === "--help") $isHelp = true; if ($option["option"] === "--help") $isHelp = true;
} }
$this->haveShortOptions = $haveShortOptions; $this->haveShortOptions = $haveShortOptions;
$this->haveLongOptions = $haveLongOptions; $this->haveLongOptions = $haveLongOptions;
$this->haveCommands = $haveCommands; $this->isCommand = $isCommand;
$this->isHelp = $isHelp; $this->isHelp = $isHelp;
$this->isRemains = $isRemains; $this->isRemains = $isRemains;
} }
public ?array $extends = null;
function isExtends(): bool {
return $this->extends !== null;
}
function processExtends(ArgDefs $argDefs): void {
$option = $this->extends[0] ?? null;
if ($option === null) {
throw new ArgException("extends: missing destination arg");
}
$dest = $argDefs->get($option);
if ($dest === null) {
throw new ArgException("$option: invalid destination arg");
}
if ($this->ensureArray !== null) $dest->ensureArray = $this->ensureArray;
if ($this->action !== null) $dest->action = $this->action;
if ($this->func !== null) $dest->func = $this->func;
if ($this->inverse !== null) $dest->inverse = $this->inverse;
if ($this->value !== null) $dest->value = $this->value;
if ($this->name !== null) $dest->name = $this->name;
if ($this->property !== null) $dest->property = $this->property;
if ($this->key !== null) $dest->key = $this->key;
A::merge($dest->removes, $this->removes);
A::merge($dest->adds, $this->adds);
$dest->processOptions();
}
protected ?array $args = null; protected ?array $args = null;
/** /**
@ -330,15 +366,6 @@ class ArgDef {
$this->argsdesc = implode(" ", $argsdesc); $this->argsdesc = implode(" ", $argsdesc);
} }
protected ?bool $ensureArray = null;
protected ?int $action = null;
protected ?func $func = null;
protected bool $inverse = false;
protected $value = null;
protected ?string $name = null;
protected ?string $property = null;
protected ?string $key = null;
private static function get_longest(array $options, int $type): ?string { private static function get_longest(array $options, int $type): ?string {
$longest = null; $longest = null;
$maxlen = 0; $maxlen = 0;

View File

@ -2,68 +2,58 @@
namespace nulib\app\cli; namespace nulib\app\cli;
use nulib\cl; use nulib\cl;
use nulib\php\types\vbool;
abstract class ArgDefs { abstract class ArgDefs {
protected static function parse_defs(SimpleArgDefs $dest, array $defs, ?array &$argDefs): void { function __construct(array $defs) {
[$defs, $params] = cl::split_assoc($defs); $this->defs = $defs;
$this->mergeParse($defs, $argDefs);
# méta-informations $this->addAll($argDefs);
$dest->prefix ??= $params["prefix"] ?? null;
$dest->name ??= $params["name"] ?? null;
$dest->purpose ??= $params["purpose"] ?? null;
$dest->usage ??= $params["usage"] ?? null;
$dest->description ??= $params["description"] ?? null;
$dest->suffix ??= $params["suffix"] ?? null;
$dest->commandname ??= $params["commandname"] ?? null;
$dest->commandproperty ??= $params["commandproperty"] ?? null;
$dest->commandkey ??= $params["commandkey"] ?? null;
$dest->argsname ??= $params["argsname"] ?? null;
$dest->argsproperty ??= $params["argsproperty"] ?? null;
$dest->argskey ??= $params["argskey"] ?? null;
$dest->autohelp ??= vbool::withn($params["autohelp"] ?? null);
$dest->autoremains ??= vbool::withn($params["autoremains"] ?? null);
# définition des options
foreach ($defs as $def) {
$argDefs[] = new ArgDef($def, $dest);
}
} }
protected static function merge_parse_defs(SimpleArgDefs $dest, array $defs, ?array &$argDefs): void { protected array $defs;
protected function mergeParse(array $defs, ?array &$argDefs): void {
$defaults = $defs["defaults"] ?? null; $defaults = $defs["defaults"] ?? null;
if ($defaults !== null) self::merge_parse_defs($dest, $defaults, $argDefs); if ($defaults !== null) $this->mergeParse($defaults, $argDefs);
self::parse_defs($dest, $defs, $argDefs); $this->parse($defs, $argDefs);
$merges = $defs["merges"] ?? null; $merges = $defs["merges"] ?? null;
$merge = $defs["merge"] ?? null; $merge = $defs["merge"] ?? null;
if ($merge !== null) $merges[] = $merge; if ($merge !== null) $merges[] = $merge;
if ($merges !== null) self::merge_parse_defs($dest, $merges, $argDefs); if ($merges !== null) $this->mergeParse($merges, $argDefs);
} }
public ?string $prefix = null; protected function parseParams(?array $params): void {
public ?string $name = null; }
public ?string $purpose = null;
public ?string $usage = null;
public ?string $description = null;
public ?string $suffix = null;
public ?string $commandname = null; protected function parse(array $defs, ?array &$argDefs): void {
public ?string $commandproperty = null; [$defs, $params] = cl::split_assoc($defs);
public ?string $commandkey = null; foreach ($defs as $def) {
$argDefs[] = new ArgDef($def);
}
$this->parseParams($params);
}
public ?string $argsname = null; protected array $all;
public ?string $argsproperty = null;
public ?string $argskey = null;
public ?bool $autohelp = null; protected function addAll(?array $argDefs): void {
public ?bool $autoremains = null; $this->all = $argDefs ?? [];
}
abstract function getArgDef(string $option): ?ArgDef; function all(): array {
return array_filter($this->all, function (ArgDef $argDef) {
return !$argDef->isEmpty();
});
}
function isEmpty(): bool {
return !$this->all();
}
function get(string $option): ?ArgDef {
return null;
}
/** /**
* consommer les arguments de $src en avançant l'index $srci et provisionner * consommer les arguments de $src en avançant l'index $srci et provisionner
@ -116,5 +106,7 @@ abstract class ArgDefs {
} }
} }
abstract function normalize(array $args): array; function normalize(array $args): array {
return $args;
}
} }

5
src/app/cli/ArgGroup.php Normal file
View File

@ -0,0 +1,5 @@
<?php
namespace nulib\app\cli;
class ArgGroup extends ArgDefs {
}

View File

@ -0,0 +1,20 @@
<?php
namespace nulib\app\cli;
use nulib\php\types\vbool;
class ArgSection extends ArgDefs {
public bool $show = true;
public ?string $prefix = null;
public ?string $title = null;
public ?string $description = null;
public ?string $suffix = null;
protected function parseParams(?array $params): void {
$this->show = vbool::with($section["show"] ?? true);
$this->prefix ??= $params["prefix"] ?? null;
$this->title ??= $params["name"] ?? null;
$this->description ??= $params["description"] ?? null;
$this->suffix ??= $params["suffix"] ?? null;
}
}

View File

@ -1,6 +1,8 @@
<?php <?php
namespace nulib\app\cli; namespace nulib\app\cli;
use nulib\php\types\vbool;
/** /**
* Class SimpleArgDefs: une définition simple des arguments et des options * Class SimpleArgDefs: une définition simple des arguments et des options
* valides d'un programme: les commandes ne sont pas supportées, ni les suites * valides d'un programme: les commandes ne sont pas supportées, ni les suites
@ -12,38 +14,87 @@ namespace nulib\app\cli;
* cmd1 -x // cmd2 -y n'est PAS supporté * cmd1 -x // cmd2 -y n'est PAS supporté
*/ */
class SimpleArgDefs extends ArgDefs { class SimpleArgDefs extends ArgDefs {
function __construct(array $defs) { public ?string $prefix = null;
self::merge_parse_defs($this, $defs, $argDefs); public ?string $name = null;
public ?string $purpose = null;
public ?string $usage = null;
public ?string $description = null;
public ?string $suffix = null;
public ?string $commandname = null;
public ?string $commandproperty = null;
public ?string $commandkey = null;
public ?string $argsname = null;
public ?string $argsproperty = null;
public ?string $argskey = null;
public ?bool $autohelp = null;
public ?bool $autoremains = null;
protected function parseParams(?array $params): void {
# méta-informations
$this->prefix ??= $params["prefix"] ?? null;
$this->name ??= $params["name"] ?? null;
$this->purpose ??= $params["purpose"] ?? null;
$this->usage ??= $params["usage"] ?? null;
$this->description ??= $params["description"] ?? null;
$this->suffix ??= $params["suffix"] ?? null;
$this->commandname ??= $params["commandname"] ?? null;
$this->commandproperty ??= $params["commandproperty"] ?? null;
$this->commandkey ??= $params["commandkey"] ?? null;
$this->argsname ??= $params["argsname"] ?? null;
$this->argsproperty ??= $params["argsproperty"] ?? null;
$this->argskey ??= $params["argskey"] ?? null;
$this->autohelp ??= vbool::withn($params["autohelp"] ?? null);
$this->autoremains ??= vbool::withn($params["autoremains"] ?? null);
}
protected array $index;
protected function addAll(?array $argDefs): void {
parent::addAll($argDefs);
$index = [];
if ($argDefs !== null) {
# calculer les fusions # calculer les fusions
foreach ($argDefs as $argDef) { foreach ($argDefs as $argDef) {
/** @var ArgDef $argDef */ /** @var ArgDef $argDef */
if (!$argDef->isExtends()) {
$argDef->processOptions(); $argDef->processOptions();
} }
}
foreach ($argDefs as $argDef) {
/** @var ArgDef $argDef */
if ($argDef->isExtends()) {
$argDef->processExtends($this);
}
}
# indexer les arguments # indexer les arguments
$index = [];
foreach ($argDefs as $argDef) { foreach ($argDefs as $argDef) {
if ($argDef->isExtends()) continue;
$options = $argDef->getOptions(); $options = $argDef->getOptions();
foreach ($options as $option) { foreach ($options as $option) {
if (array_key_exists($option, $index)) { /** @var ArgDef $prevArgDef */
$index[$option]->removeOption($option); $prevArgDef = $index[$option] ?? null;
} if ($prevArgDef !== null) $prevArgDef->removeOption($option);
$index[$option] = $argDef; $index[$option] = $argDef;
} }
} }
foreach ($argDefs as $argDef) { foreach ($argDefs as $argDef) {
if ($argDef->isExtends()) continue;
$argDef->processArgs(); $argDef->processArgs();
$argDef->processAction(); $argDef->processAction();
} }
}
$this->index = $index; $this->index = $index;
} }
protected array $index; function get(string $option): ?ArgDef {
function getArgDef(string $option): ?ArgDef {
return $this->index[$option] ?? null; return $this->index[$option] ?? null;
} }

View File

@ -10,8 +10,6 @@ faire une implémentation SimpleArgsParser qui ne supporte pas les commandes, un
puis faire une implémentation ArgsParser qui supporte les commandes, et les options dynamiques puis faire une implémentation ArgsParser qui supporte les commandes, et les options dynamiques
## WIP
documenter que dans les cas simples, on peut tout simplement refaire la définition, e.g documenter que dans les cas simples, on peut tout simplement refaire la définition, e.g
~~~php ~~~php
[ [
@ -20,4 +18,9 @@ documenter que dans les cas simples, on peut tout simplement refaire la définit
] ]
~~~ ~~~
ajouter le support des sections, la section par défaut ayant la clé `0` (c'est
la première section définie implicitement)
ajouter le support des groupes
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary -*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary

View File

@ -100,5 +100,18 @@
valeurs séquentielles sont des clés du schéma", soit un mode automatique valeurs séquentielles sont des clés du schéma", soit un mode automatique
activé par un paramètre où une valeur `"val"` devient `"val"=>true` si la clé activé par un paramètre où une valeur `"val"` devient `"val"=>true` si la clé
"val" existe dans le schéma "val" existe dans le schéma
* pour les schémas associatifs, support de la définition de valeurs de suite avec
la clé "..." e.g
~~~php
const SCHEMA = [
"a" => "string",
"b" => "bool",
"..." => "int",
];
# pourrait modéliser ["first", true, 1, 2, 3]
~~~
et on aurait alors `[[type]]` qui est équivalent à `["..." => type]`
et donc ListSchema est un cas particulier de AssocSchema (qui pourrait être
renommé ArraySchema)
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary -*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary

View File

@ -7,7 +7,7 @@ class ArgDefTest extends TestCase {
protected static function assertArg( protected static function assertArg(
ArgDef $argDef, ArgDef $argDef,
array $options, array $options,
bool $haveShortOptions, bool $haveLongOptions, bool $haveCommands, bool $haveShortOptions, bool $haveLongOptions, bool $isCommand,
bool $haveArgs, ?int $minArgs, ?int $maxArgs, ?string $argsdesc bool $haveArgs, ?int $minArgs, ?int $maxArgs, ?string $argsdesc
) { ) {
$argDef->processOptions(); $argDef->processOptions();
@ -16,7 +16,7 @@ class ArgDefTest extends TestCase {
self::assertSame($options, $argDef->getOptions()); self::assertSame($options, $argDef->getOptions());
self::assertSame($haveShortOptions, $argDef->haveShortOptions, "haveShortOptions"); self::assertSame($haveShortOptions, $argDef->haveShortOptions, "haveShortOptions");
self::assertSame($haveLongOptions, $argDef->haveLongOptions, "haveLongOptions"); self::assertSame($haveLongOptions, $argDef->haveLongOptions, "haveLongOptions");
self::assertSame($haveCommands, $argDef->haveCommands, "haveCommands"); self::assertSame($isCommand, $argDef->isCommand, "isCommand");
self::assertSame($haveArgs, $argDef->haveArgs, "haveArgs"); self::assertSame($haveArgs, $argDef->haveArgs, "haveArgs");
self::assertSame($minArgs, $argDef->minArgs, "minArgs"); self::assertSame($minArgs, $argDef->minArgs, "minArgs");
self::assertSame($maxArgs, $argDef->maxArgs, "maxArgs"); self::assertSame($maxArgs, $argDef->maxArgs, "maxArgs");