diff --git a/src/app/cli/ArgDef.php b/src/app/cli/ArgDef.php index 6036578..614414d 100644 --- a/src/app/cli/ArgDef.php +++ b/src/app/cli/ArgDef.php @@ -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; diff --git a/src/app/cli/ArgDefs.php b/src/app/cli/ArgDefs.php index 55c61ad..1a35879 100644 --- a/src/app/cli/ArgDefs.php +++ b/src/app/cli/ArgDefs.php @@ -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; + } } diff --git a/src/app/cli/ArgGroup.php b/src/app/cli/ArgGroup.php new file mode 100644 index 0000000..8a425e4 --- /dev/null +++ b/src/app/cli/ArgGroup.php @@ -0,0 +1,5 @@ +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; + } +} diff --git a/src/app/cli/SimpleArgDefs.php b/src/app/cli/SimpleArgDefs.php index e4e990f..4bf3d50 100644 --- a/src/app/cli/SimpleArgDefs.php +++ b/src/app/cli/SimpleArgDefs.php @@ -1,6 +1,8 @@ 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; } diff --git a/src/app/cli/TODO.md b/src/app/cli/TODO.md index a2bfd9d..44c1be5 100644 --- a/src/app/cli/TODO.md +++ b/src/app/cli/TODO.md @@ -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 \ No newline at end of file diff --git a/src/schema/TODO.md b/src/schema/TODO.md index c318ab2..b6d7505 100644 --- a/src/schema/TODO.md +++ b/src/schema/TODO.md @@ -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 \ No newline at end of file diff --git a/tests/app/cli/ArgDefTest.php b/tests/app/cli/ArgDefTest.php index 0171f1e..aaea1a0 100644 --- a/tests/app/cli/ArgDefTest.php +++ b/tests/app/cli/ArgDefTest.php @@ -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");