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 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);
$this->extends = $params["extends"] ?? null;
$args = $params["args"] ?? null;
$args ??= $params["arg"] ?? null;
if ($args === true) $args = 1;
elseif ($args === "*") $args = [null];
elseif ($args === "+") $args = ["value", null];
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);
A::merge($dest->adds, $adds);
A::merge($dest->adds, $options);
A::merge($this->adds, $adds);
A::merge($this->adds, $options);
$removes = varray::withn($params["remove"] ?? null);
A::merge($dest->removes, $removes);
$dest->disabled = vbool::withn($params["disabled"] ?? null);
A::merge($this->removes, $removes);
$dest->ensureArray ??= $params["ensure_array"] ?? null;
$this->ensureArray ??= $params["ensure_array"] ?? null;
$action = $params["action"] ?? null;
if ($action !== null) {
$func = null;
@ -51,72 +85,44 @@ class ArgDef {
$action = self::ACTION_FUNC;
break;
}
$dest->action ??= $action;
$dest->func ??= $func;
$this->action ??= $action;
$this->func ??= $func;
}
$dest->inverse ??= $params["inverse"] ?? false;
$dest->value ??= $params["value"] ?? null;
$dest->name ??= $params["name"] ?? null;
$dest->property ??= $params["property"] ?? null;
$dest->key ??= $params["key"] ?? null;
$this->inverse ??= $params["inverse"] ?? null;
$this->value ??= $params["value"] ?? null;
$this->name ??= $params["name"] ?? null;
$this->property ??= $params["property"] ?? 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 $isRemains = false;
public bool $haveShortOptions = false;
public bool $haveLongOptions = false;
public bool $haveCommands = false;
public bool $isCommand = false;
public bool $haveArgs = false;
public ?int $minArgs = null;
public ?int $maxArgs = null;
public ?string $argsdesc = null;
protected ?bool $disabled = null;
protected ?array $options = [];
function getOptions(): array {
return array_keys($this->options);
if ($this->disabled) return [];
else return array_keys($this->options);
}
protected ?array $removes = null;
protected ?array $adds = null;
protected ?bool $disabled = null;
/** traiter le paramètre parent */
function processOptions(): void {
if ($this->disabled) {
$this->options = [];
} else {
$this->removeOptions($this->removes);
$this->addOptions($this->adds);
}
$this->removeOptions($this->removes);
$this->addOptions($this->adds);
}
function addOptions(?array $options): void {
@ -205,7 +211,7 @@ class ArgDef {
protected function updateType(): void {
$haveShortOptions = false;
$haveLongOptions = false;
$haveCommands = false;
$isCommand = false;
$isHelp = false;
$isRemains = true;
foreach ($this->options as $option) {
@ -218,18 +224,48 @@ class ArgDef {
$haveLongOptions = true;
break;
case self::TYPE_COMMAND:
$haveCommands = true;
$isCommand = true;
break;
}
if ($option["option"] === "--help") $isHelp = true;
}
$this->haveShortOptions = $haveShortOptions;
$this->haveLongOptions = $haveLongOptions;
$this->haveCommands = $haveCommands;
$this->isCommand = $isCommand;
$this->isHelp = $isHelp;
$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;
/**
@ -330,15 +366,6 @@ class ArgDef {
$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 {
$longest = null;
$maxlen = 0;

View File

@ -2,68 +2,58 @@
namespace nulib\app\cli;
use nulib\cl;
use nulib\php\types\vbool;
abstract class ArgDefs {
protected static function parse_defs(SimpleArgDefs $dest, array $defs, ?array &$argDefs): void {
[$defs, $params] = cl::split_assoc($defs);
# méta-informations
$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);
}
function __construct(array $defs) {
$this->defs = $defs;
$this->mergeParse($defs, $argDefs);
$this->addAll($argDefs);
}
protected static function merge_parse_defs(SimpleArgDefs $dest, array $defs, ?array &$argDefs): void {
$defaults = $defs["defaults"] ?? null;
if ($defaults !== null) self::merge_parse_defs($dest, $defaults, $argDefs);
protected array $defs;
self::parse_defs($dest, $defs, $argDefs);
protected function mergeParse(array $defs, ?array &$argDefs): void {
$defaults = $defs["defaults"] ?? null;
if ($defaults !== null) $this->mergeParse($defaults, $argDefs);
$this->parse($defs, $argDefs);
$merges = $defs["merges"] ?? null;
$merge = $defs["merge"] ?? null;
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;
public ?string $name = null;
public ?string $purpose = null;
public ?string $usage = null;
public ?string $description = null;
public ?string $suffix = null;
protected function parseParams(?array $params): void {
}
public ?string $commandname = null;
public ?string $commandproperty = null;
public ?string $commandkey = null;
protected function parse(array $defs, ?array &$argDefs): void {
[$defs, $params] = cl::split_assoc($defs);
foreach ($defs as $def) {
$argDefs[] = new ArgDef($def);
}
$this->parseParams($params);
}
public ?string $argsname = null;
public ?string $argsproperty = null;
public ?string $argskey = null;
protected array $all;
public ?bool $autohelp = null;
public ?bool $autoremains = null;
protected function addAll(?array $argDefs): void {
$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
@ -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
namespace nulib\app\cli;
use nulib\php\types\vbool;
/**
* 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
@ -12,38 +14,87 @@ namespace nulib\app\cli;
* cmd1 -x // cmd2 -y n'est PAS supporté
*/
class SimpleArgDefs extends ArgDefs {
function __construct(array $defs) {
self::merge_parse_defs($this, $defs, $argDefs);
public ?string $prefix = null;
public ?string $name = null;
public ?string $purpose = null;
public ?string $usage = null;
public ?string $description = null;
public ?string $suffix = null;
# calculer les fusions
foreach ($argDefs as $argDef) {
/** @var ArgDef $argDef */
$argDef->processOptions();
}
public ?string $commandname = null;
public ?string $commandproperty = null;
public ?string $commandkey = null;
# indexer les arguments
$index = [];
foreach ($argDefs as $argDef) {
$options = $argDef->getOptions();
foreach ($options as $option) {
if (array_key_exists($option, $index)) {
$index[$option]->removeOption($option);
}
$index[$option] = $argDef;
}
}
public ?string $argsname = null;
public ?string $argsproperty = null;
public ?string $argskey = null;
foreach ($argDefs as $argDef) {
$argDef->processArgs();
$argDef->processAction();
}
public ?bool $autohelp = null;
public ?bool $autoremains = null;
$this->index = $index;
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;
function getArgDef(string $option): ?ArgDef {
protected function addAll(?array $argDefs): void {
parent::addAll($argDefs);
$index = [];
if ($argDefs !== null) {
# calculer les fusions
foreach ($argDefs as $argDef) {
/** @var ArgDef $argDef */
if (!$argDef->isExtends()) {
$argDef->processOptions();
}
}
foreach ($argDefs as $argDef) {
/** @var ArgDef $argDef */
if ($argDef->isExtends()) {
$argDef->processExtends($this);
}
}
# indexer les arguments
foreach ($argDefs as $argDef) {
if ($argDef->isExtends()) continue;
$options = $argDef->getOptions();
foreach ($options as $option) {
/** @var ArgDef $prevArgDef */
$prevArgDef = $index[$option] ?? null;
if ($prevArgDef !== null) $prevArgDef->removeOption($option);
$index[$option] = $argDef;
}
}
foreach ($argDefs as $argDef) {
if ($argDef->isExtends()) continue;
$argDef->processArgs();
$argDef->processAction();
}
}
$this->index = $index;
}
function get(string $option): ?ArgDef {
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
## WIP
documenter que dans les cas simples, on peut tout simplement refaire la définition, e.g
~~~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

View File

@ -100,5 +100,18 @@
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é
"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

View File

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