modifs.mineures sans commentaires
This commit is contained in:
parent
4dd7b60229
commit
94c268d81a
@ -11,29 +11,29 @@ 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;
|
||||||
|
|
||||||
function __construct(array $def) {
|
protected static function parse_def(ArgDef $dest, array $def): void {
|
||||||
[$options, $params] = cl::split_assoc($def);
|
[$options, $params] = cl::split_assoc($def);
|
||||||
|
|
||||||
$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;
|
||||||
if (is_int($args)) $args = array_fill(0, $args, "value");
|
if (is_int($args)) $args = array_fill(0, $args, "value");
|
||||||
$this->args = cl::withn($args);
|
$dest->args ??= cl::withn($args);
|
||||||
|
|
||||||
$this->argsdesc = $params["argsdesc"] ?? null;
|
$dest->argsdesc ??= $params["argsdesc"] ?? null;
|
||||||
|
|
||||||
$extends = $params["extends"] ?? null;
|
$parent = $params["parent"] ?? null;
|
||||||
if ($extends !== null) {
|
if ($parent !== null) {
|
||||||
A::merge($extends["add"], $options);
|
A::merge($parent["add"], $options);
|
||||||
$this->extends = $extends;
|
A::merge($dest->parents, [$parent]);
|
||||||
} else {
|
} else {
|
||||||
$this->addOptions($options);
|
$dest->addOptions($options);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->ensureArray = $params["ensure_array"] ?? null;
|
$dest->ensureArray ??= $params["ensure_array"] ?? null;
|
||||||
$action = $params["action"] ?? null;
|
$action = $params["action"] ?? null;
|
||||||
$func = null;
|
|
||||||
if ($action !== null) {
|
if ($action !== null) {
|
||||||
|
$func = null;
|
||||||
switch ($action) {
|
switch ($action) {
|
||||||
case "--set":
|
case "--set":
|
||||||
$action = self::ACTION_SET;
|
$action = self::ACTION_SET;
|
||||||
@ -49,16 +49,32 @@ class ArgDef {
|
|||||||
$action = self::ACTION_FUNC;
|
$action = self::ACTION_FUNC;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
$dest->action ??= $action;
|
||||||
|
$dest->func ??= $func;
|
||||||
}
|
}
|
||||||
$this->action = $action;
|
$dest->inverse ??= $params["inverse"] ?? false;
|
||||||
$this->func = $func;
|
$dest->value ??= $params["value"] ?? null;
|
||||||
$this->inverse = $params["inverse"] ?? false;
|
$dest->name ??= $params["name"] ?? null;
|
||||||
$this->value = $params["value"] ?? null;
|
$dest->property ??= $params["property"] ?? null;
|
||||||
$this->name = $params["name"] ?? null;
|
$dest->key ??= $params["key"] ?? null;
|
||||||
$this->property = $params["property"] ?? null;
|
|
||||||
$this->key = $params["key"] ?? null;
|
|
||||||
|
|
||||||
$this->help = $params["help"] ?? null;
|
$dest->help ??= $params["help"] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function merge_parse_def(ArgDef $dest, array $def): void {
|
||||||
|
$defaults = $defs["defaults"] ?? null;
|
||||||
|
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) self::merge_parse_def($dest, $merges);
|
||||||
|
}
|
||||||
|
|
||||||
|
function __construct(array $def) {
|
||||||
|
self::merge_parse_def($this, $def);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ?array $options = [];
|
protected ?array $options = [];
|
||||||
@ -67,6 +83,7 @@ class ArgDef {
|
|||||||
return array_keys($this->options);
|
return array_keys($this->options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
@ -159,18 +176,20 @@ class ArgDef {
|
|||||||
unset($this->options[$option]);
|
unset($this->options[$option]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ?array $extends = null;
|
protected ?array $parents = null;
|
||||||
|
|
||||||
/** traiter le paramètre extends */
|
/** traiter le paramètre parent */
|
||||||
function processExtends(): void {
|
function processParents(?ArgDefs $argDefs=null): void {
|
||||||
$extends = $this->extends;
|
$parents = $this->parents;
|
||||||
if ($extends === null) return;
|
if ($parents === null) return;
|
||||||
$base = $extends["arg"] ?? null;
|
foreach ($parents as $parent) {
|
||||||
if ($base === null) return;
|
$argDef = $parent["arg"] ?? null;
|
||||||
$base = new self($base);
|
if ($argDef === null) continue;
|
||||||
$this->options = $base->options;
|
$argDef = new self($argDef);
|
||||||
$this->removeOptions(varray::withn($extends["remove"] ?? null));
|
$this->options = $argDef->options;
|
||||||
$this->addOptions(varray::withn($extends["add"] ?? null));
|
$this->removeOptions(varray::withn($parent["remove"] ?? null));
|
||||||
|
$this->addOptions(varray::withn($parent["add"] ?? null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** mettre à jour le type d'option */
|
/** mettre à jour le type d'option */
|
||||||
@ -178,6 +197,7 @@ class ArgDef {
|
|||||||
$haveShortOptions = false;
|
$haveShortOptions = false;
|
||||||
$haveLongOptions = false;
|
$haveLongOptions = false;
|
||||||
$haveCommands = false;
|
$haveCommands = false;
|
||||||
|
$isHelp = false;
|
||||||
$isRemains = true;
|
$isRemains = true;
|
||||||
foreach ($this->options as $option) {
|
foreach ($this->options as $option) {
|
||||||
$isRemains = false;
|
$isRemains = false;
|
||||||
@ -192,11 +212,13 @@ class ArgDef {
|
|||||||
$haveCommands = true;
|
$haveCommands = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if ($option["option"] === "--help") $isHelp = true;
|
||||||
}
|
}
|
||||||
$this->isRemains = $isRemains;
|
|
||||||
$this->haveShortOptions = $haveShortOptions;
|
$this->haveShortOptions = $haveShortOptions;
|
||||||
$this->haveLongOptions = $haveLongOptions;
|
$this->haveLongOptions = $haveLongOptions;
|
||||||
$this->haveCommands = $haveCommands;
|
$this->haveCommands = $haveCommands;
|
||||||
|
$this->isHelp = $isHelp;
|
||||||
|
$this->isRemains = $isRemains;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ?array $args = null;
|
protected ?array $args = null;
|
||||||
@ -207,8 +229,8 @@ class ArgDef {
|
|||||||
*/
|
*/
|
||||||
function processArgs(): void {
|
function processArgs(): void {
|
||||||
$args = $this->args;
|
$args = $this->args;
|
||||||
|
$haveArgs = boolval($args);
|
||||||
if ($args === null) {
|
if ($args === null) {
|
||||||
$haveArgs = false;
|
|
||||||
$optionalArgs = null;
|
$optionalArgs = null;
|
||||||
foreach ($this->options as $option) {
|
foreach ($this->options as $option) {
|
||||||
switch ($option["args_type"]) {
|
switch ($option["args_type"]) {
|
||||||
|
@ -2,47 +2,67 @@
|
|||||||
namespace nulib\app\cli;
|
namespace nulib\app\cli;
|
||||||
|
|
||||||
use nulib\cl;
|
use nulib\cl;
|
||||||
|
use nulib\php\types\vbool;
|
||||||
|
|
||||||
class ArgDefs {
|
abstract class ArgDefs {
|
||||||
function __construct(array $defs) {
|
protected static function parse_defs(SimpleArgDefs $dest, array $defs, ?array &$argDefs): void {
|
||||||
[$defs, $params] = cl::split_assoc($defs);
|
[$defs, $params] = cl::split_assoc($defs);
|
||||||
|
|
||||||
$argDefs = [];
|
# 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) {
|
foreach ($defs as $def) {
|
||||||
$argDefs[] = new ArgDef($def);
|
$argDefs[] = new ArgDef($def);
|
||||||
}
|
}
|
||||||
foreach ($argDefs as $argDef) {
|
|
||||||
$argDef->processExtends();
|
|
||||||
}
|
|
||||||
|
|
||||||
$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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($argDefs as $argDef) {
|
|
||||||
$argDef->processArgs();
|
|
||||||
$argDef->processAction();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->argDefs = $argDefs;
|
|
||||||
$this->index = $index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected array $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 $index;
|
self::parse_defs($dest, $defs, $argDefs);
|
||||||
|
|
||||||
function getArgDef(string $option): ?ArgDef {
|
$merges = $defs["merges"] ?? null;
|
||||||
return $this->index[$option] ?? null;
|
$merge = $defs["merge"] ?? null;
|
||||||
|
if ($merge !== null) $merges[] = $merge;
|
||||||
|
if ($merges !== null) self::merge_parse_defs($dest, $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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
* $dest à partir de $desti. si $desti est plus grand que 0, celà veut dire
|
* $dest à partir de $desti. si $desti est plus grand que 0, celà veut dire
|
||||||
@ -59,7 +79,7 @@ class ArgDefs {
|
|||||||
* disponible, ou jusqu'à la présence de '--'. Si $keepsep, l'argument '--'
|
* disponible, ou jusqu'à la présence de '--'. Si $keepsep, l'argument '--'
|
||||||
* est gardé dans la liste des arguments optionnels.
|
* est gardé dans la liste des arguments optionnels.
|
||||||
*/
|
*/
|
||||||
private static function consume_args($src, &$srci, &$dest, $desti, $destmin, $destmax, bool $keepsep): int {
|
protected static function consume_args($src, &$srci, &$dest, $desti, $destmin, $destmax, bool $keepsep): int {
|
||||||
$srcmax = count($src);
|
$srcmax = count($src);
|
||||||
# arguments obligatoires
|
# arguments obligatoires
|
||||||
while ($desti < $destmin) {
|
while ($desti < $destmin) {
|
||||||
@ -88,158 +108,11 @@ class ArgDefs {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function check_missing(?string $option, int $count) {
|
protected static function check_missing(?string $option, int $count) {
|
||||||
if ($count > 0) {
|
if ($count > 0) {
|
||||||
throw new ArgException("$option: nombre d'arguments insuffisant (manque $count)");
|
throw new ArgException("$option: nombre d'arguments insuffisant (manque $count)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalize(array $args): array {
|
abstract function normalize(array $args): array;
|
||||||
$i = 0;
|
|
||||||
$max = count($args);
|
|
||||||
$options = [];
|
|
||||||
$remains = [];
|
|
||||||
$parseOpts = true;
|
|
||||||
while ($i < $max) {
|
|
||||||
$arg = $args[$i++];
|
|
||||||
if (!$parseOpts) {
|
|
||||||
# le reste n'est que des arguments
|
|
||||||
$remains[] = $arg;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($arg === "--") {
|
|
||||||
# fin des options
|
|
||||||
$parseOpts = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (substr($arg, 0, 2) === "--") {
|
|
||||||
#######################################################################
|
|
||||||
# option longue
|
|
||||||
$pos = strpos($arg, "=");
|
|
||||||
if ($pos !== false) {
|
|
||||||
# option avec valeur
|
|
||||||
$option = substr($arg, 0, $pos);
|
|
||||||
$value = substr($arg, $pos + 1);
|
|
||||||
} else {
|
|
||||||
# option sans valeur
|
|
||||||
$option = $arg;
|
|
||||||
$value = null;
|
|
||||||
}
|
|
||||||
/** @var ArgDef $argDef */
|
|
||||||
$argDef = $this->index[$option] ?? null;
|
|
||||||
if ($argDef === null) {
|
|
||||||
# chercher une correspondance
|
|
||||||
$len = strlen($option);
|
|
||||||
$candidates = [];
|
|
||||||
foreach (array_keys($this->index) as $candidate) {
|
|
||||||
if (substr($candidate, 0, $len) === $option) {
|
|
||||||
$candidates[] = $candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch (count($candidates)) {
|
|
||||||
case 0:
|
|
||||||
throw new ArgException("$option: option invalide");
|
|
||||||
case 1:
|
|
||||||
$option = $candidates[0];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$candidates = implode(", ", $candidates);
|
|
||||||
throw new ArgException("$option: option ambigue (les options possibles sont $candidates)");
|
|
||||||
}
|
|
||||||
$argDef = $this->index[$option];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($argDef->haveArgs) {
|
|
||||||
$minArgs = $argDef->minArgs;
|
|
||||||
$maxArgs = $argDef->maxArgs;
|
|
||||||
$values = [];
|
|
||||||
if ($value !== null) {
|
|
||||||
$values[] = $value;
|
|
||||||
$offset = 1;
|
|
||||||
} elseif ($minArgs == 0) {
|
|
||||||
# cas particulier: la première valeur doit être collée à l'option
|
|
||||||
# si $maxArgs == 1
|
|
||||||
$offset = $maxArgs == 1 ? 1 : 0;
|
|
||||||
} else {
|
|
||||||
$offset = 0;
|
|
||||||
}
|
|
||||||
$this->check_missing($option,
|
|
||||||
self::consume_args($args, $i, $values, $offset, $minArgs, $maxArgs, true));
|
|
||||||
|
|
||||||
if ($minArgs == 0 && $maxArgs == 1) {
|
|
||||||
# cas particulier: la première valeur doit être collée à l'option
|
|
||||||
if (count($values) > 0) {
|
|
||||||
$options[] = "$option=$values[0]";
|
|
||||||
$values = array_slice($values, 1);
|
|
||||||
} else {
|
|
||||||
$options[] = $option;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$options[] = $option;
|
|
||||||
}
|
|
||||||
$options = array_merge($options, $values);
|
|
||||||
} elseif ($value !== null) {
|
|
||||||
throw new ArgException("$option: cette option ne prend pas d'arguments");
|
|
||||||
} else {
|
|
||||||
$options[] = $option;
|
|
||||||
}
|
|
||||||
|
|
||||||
} elseif (substr($arg, 0, 1) === "-") {
|
|
||||||
#######################################################################
|
|
||||||
# option courte
|
|
||||||
$pos = 1;
|
|
||||||
$len = strlen($arg);
|
|
||||||
while ($pos < $len) {
|
|
||||||
$option = "-".substr($arg, $pos, 1);
|
|
||||||
/** @var ArgDef $argDef */
|
|
||||||
$argDef = $this->index[$option] ?? null;
|
|
||||||
if ($argDef === null) {
|
|
||||||
throw new ArgException("$option: option invalide");
|
|
||||||
}
|
|
||||||
if ($argDef->haveArgs) {
|
|
||||||
$minArgs = $argDef->minArgs;
|
|
||||||
$maxArgs = $argDef->maxArgs;
|
|
||||||
$values = [];
|
|
||||||
if ($len > $pos + 1) {
|
|
||||||
$values[] = substr($arg, $pos + 1);
|
|
||||||
$offset = 1;
|
|
||||||
$pos = $len;
|
|
||||||
} elseif ($minArgs == 0) {
|
|
||||||
# cas particulier: la première valeur doit être collée à l'option
|
|
||||||
# si $maxArgs == 1
|
|
||||||
$offset = $maxArgs == 1 ? 1 : 0;
|
|
||||||
} else {
|
|
||||||
$offset = 0;
|
|
||||||
}
|
|
||||||
$this->check_missing($option,
|
|
||||||
self::consume_args($args, $i, $values, $offset, $minArgs, $maxArgs, true));
|
|
||||||
|
|
||||||
if ($minArgs == 0 && $maxArgs == 1) {
|
|
||||||
# cas particulier: la première valeur doit être collée à l'option
|
|
||||||
if (count($values) > 0) {
|
|
||||||
$options[] = "$option$values[0]";
|
|
||||||
$values = array_slice($values, 1);
|
|
||||||
} else {
|
|
||||||
$options[] = $option;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$options[] = $option;
|
|
||||||
}
|
|
||||||
$options = array_merge($options, $values);
|
|
||||||
} else {
|
|
||||||
$options[] = $option;
|
|
||||||
}
|
|
||||||
$pos++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
#XXX implémenter les commandes
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# argument
|
|
||||||
$remains[] = $arg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return array_merge($options, ["--"], $remains);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
198
src/app/cli/SimpleArgDefs.php
Normal file
198
src/app/cli/SimpleArgDefs.php
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\app\cli;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* de commandes
|
||||||
|
*
|
||||||
|
* i.e
|
||||||
|
* -x --long est supporté
|
||||||
|
* cmd -a -b n'est PAS supporté
|
||||||
|
* cmd1 -x // cmd2 -y n'est PAS supporté
|
||||||
|
*/
|
||||||
|
class SimpleArgDefs extends ArgDefs {
|
||||||
|
function __construct(array $defs) {
|
||||||
|
self::merge_parse_defs($this, $defs, $argDefs);
|
||||||
|
|
||||||
|
# calculer les héritages
|
||||||
|
foreach ($argDefs as $argDef) {
|
||||||
|
/** @var ArgDef $argDef */
|
||||||
|
$argDef->processParents($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($argDefs as $argDef) {
|
||||||
|
$argDef->processArgs();
|
||||||
|
$argDef->processAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->index = $index;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected array $index;
|
||||||
|
|
||||||
|
function getArgDef(string $option): ?ArgDef {
|
||||||
|
return $this->index[$option] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalize(array $args): array {
|
||||||
|
$i = 0;
|
||||||
|
$max = count($args);
|
||||||
|
$options = [];
|
||||||
|
$remains = [];
|
||||||
|
$parseOpts = true;
|
||||||
|
while ($i < $max) {
|
||||||
|
$arg = $args[$i++];
|
||||||
|
if (!$parseOpts) {
|
||||||
|
# le reste n'est que des arguments
|
||||||
|
$remains[] = $arg;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($arg === "--") {
|
||||||
|
# fin des options
|
||||||
|
$parseOpts = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (substr($arg, 0, 2) === "--") {
|
||||||
|
#######################################################################
|
||||||
|
# option longue
|
||||||
|
$pos = strpos($arg, "=");
|
||||||
|
if ($pos !== false) {
|
||||||
|
# option avec valeur
|
||||||
|
$option = substr($arg, 0, $pos);
|
||||||
|
$value = substr($arg, $pos + 1);
|
||||||
|
} else {
|
||||||
|
# option sans valeur
|
||||||
|
$option = $arg;
|
||||||
|
$value = null;
|
||||||
|
}
|
||||||
|
/** @var ArgDef $argDef */
|
||||||
|
$argDef = $this->index[$option] ?? null;
|
||||||
|
if ($argDef === null) {
|
||||||
|
# chercher une correspondance
|
||||||
|
$len = strlen($option);
|
||||||
|
$candidates = [];
|
||||||
|
foreach (array_keys($this->index) as $candidate) {
|
||||||
|
if (substr($candidate, 0, $len) === $option) {
|
||||||
|
$candidates[] = $candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (count($candidates)) {
|
||||||
|
case 0:
|
||||||
|
throw new ArgException("$option: option invalide");
|
||||||
|
case 1:
|
||||||
|
$option = $candidates[0];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$candidates = implode(", ", $candidates);
|
||||||
|
throw new ArgException("$option: option ambigue (les options possibles sont $candidates)");
|
||||||
|
}
|
||||||
|
$argDef = $this->index[$option];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($argDef->haveArgs) {
|
||||||
|
$minArgs = $argDef->minArgs;
|
||||||
|
$maxArgs = $argDef->maxArgs;
|
||||||
|
$values = [];
|
||||||
|
if ($value !== null) {
|
||||||
|
$values[] = $value;
|
||||||
|
$offset = 1;
|
||||||
|
} elseif ($minArgs == 0) {
|
||||||
|
# cas particulier: la première valeur doit être collée à l'option
|
||||||
|
# si $maxArgs == 1
|
||||||
|
$offset = $maxArgs == 1 ? 1 : 0;
|
||||||
|
} else {
|
||||||
|
$offset = 0;
|
||||||
|
}
|
||||||
|
$this->check_missing($option,
|
||||||
|
self::consume_args($args, $i, $values, $offset, $minArgs, $maxArgs, true));
|
||||||
|
|
||||||
|
if ($minArgs == 0 && $maxArgs == 1) {
|
||||||
|
# cas particulier: la première valeur doit être collée à l'option
|
||||||
|
if (count($values) > 0) {
|
||||||
|
$options[] = "$option=$values[0]";
|
||||||
|
$values = array_slice($values, 1);
|
||||||
|
} else {
|
||||||
|
$options[] = $option;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$options[] = $option;
|
||||||
|
}
|
||||||
|
$options = array_merge($options, $values);
|
||||||
|
} elseif ($value !== null) {
|
||||||
|
throw new ArgException("$option: cette option ne prend pas d'arguments");
|
||||||
|
} else {
|
||||||
|
$options[] = $option;
|
||||||
|
}
|
||||||
|
|
||||||
|
} elseif (substr($arg, 0, 1) === "-") {
|
||||||
|
#######################################################################
|
||||||
|
# option courte
|
||||||
|
$pos = 1;
|
||||||
|
$len = strlen($arg);
|
||||||
|
while ($pos < $len) {
|
||||||
|
$option = "-".substr($arg, $pos, 1);
|
||||||
|
/** @var ArgDef $argDef */
|
||||||
|
$argDef = $this->index[$option] ?? null;
|
||||||
|
if ($argDef === null) {
|
||||||
|
throw new ArgException("$option: option invalide");
|
||||||
|
}
|
||||||
|
if ($argDef->haveArgs) {
|
||||||
|
$minArgs = $argDef->minArgs;
|
||||||
|
$maxArgs = $argDef->maxArgs;
|
||||||
|
$values = [];
|
||||||
|
if ($len > $pos + 1) {
|
||||||
|
$values[] = substr($arg, $pos + 1);
|
||||||
|
$offset = 1;
|
||||||
|
$pos = $len;
|
||||||
|
} elseif ($minArgs == 0) {
|
||||||
|
# cas particulier: la première valeur doit être collée à l'option
|
||||||
|
# si $maxArgs == 1
|
||||||
|
$offset = $maxArgs == 1 ? 1 : 0;
|
||||||
|
} else {
|
||||||
|
$offset = 0;
|
||||||
|
}
|
||||||
|
$this->check_missing($option,
|
||||||
|
self::consume_args($args, $i, $values, $offset, $minArgs, $maxArgs, true));
|
||||||
|
|
||||||
|
if ($minArgs == 0 && $maxArgs == 1) {
|
||||||
|
# cas particulier: la première valeur doit être collée à l'option
|
||||||
|
if (count($values) > 0) {
|
||||||
|
$options[] = "$option$values[0]";
|
||||||
|
$values = array_slice($values, 1);
|
||||||
|
} else {
|
||||||
|
$options[] = $option;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$options[] = $option;
|
||||||
|
}
|
||||||
|
$options = array_merge($options, $values);
|
||||||
|
} else {
|
||||||
|
$options[] = $option;
|
||||||
|
}
|
||||||
|
$pos++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#XXX implémenter les commandes
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
# argument
|
||||||
|
$remains[] = $arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return array_merge($options, ["--"], $remains);
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ class ArgDefTest extends TestCase {
|
|||||||
bool $haveShortOptions, bool $haveLongOptions, bool $haveCommands,
|
bool $haveShortOptions, bool $haveLongOptions, bool $haveCommands,
|
||||||
bool $haveArgs, ?int $minArgs, ?int $maxArgs, ?string $argsdesc
|
bool $haveArgs, ?int $minArgs, ?int $maxArgs, ?string $argsdesc
|
||||||
) {
|
) {
|
||||||
$argDef->processExtends();
|
$argDef->processParents();
|
||||||
$argDef->processArgs();
|
$argDef->processArgs();
|
||||||
$argDef->processAction();
|
$argDef->processAction();
|
||||||
self::assertSame($options, $argDef->getOptions());
|
self::assertSame($options, $argDef->getOptions());
|
||||||
@ -23,50 +23,128 @@ class ArgDefTest extends TestCase {
|
|||||||
self::assertSame($argsdesc, $argDef->argsdesc, "argsdesc");
|
self::assertSame($argsdesc, $argDef->argsdesc, "argsdesc");
|
||||||
}
|
}
|
||||||
|
|
||||||
function testBase() {
|
function testArgsNone() {
|
||||||
|
$argDef = new ArgDef(["-o"]);
|
||||||
|
self::assertArg($argDef,
|
||||||
|
["-o"],
|
||||||
|
true, false, false,
|
||||||
|
false, 0, 0, "");
|
||||||
|
|
||||||
|
$argDef = new ArgDef(["--longo"]);
|
||||||
|
self::assertArg($argDef,
|
||||||
|
["--longo"],
|
||||||
|
false, true, false,
|
||||||
|
false, 0, 0, "");
|
||||||
|
|
||||||
$argDef = new ArgDef(["-o", "--longo"]);
|
$argDef = new ArgDef(["-o", "--longo"]);
|
||||||
self::assertArg($argDef,
|
self::assertArg($argDef,
|
||||||
["-o", "--longo"],
|
["-o", "--longo"],
|
||||||
true, true, false,
|
true, true, false,
|
||||||
false, 0, 0, "");
|
false, 0, 0, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function testArgsMandatory() {
|
||||||
$argDef = new ArgDef(["-o:", "--longo"]);
|
$argDef = new ArgDef(["-o:", "--longo"]);
|
||||||
self::assertArg($argDef,
|
self::assertArg($argDef,
|
||||||
["-o", "--longo"],
|
["-o", "--longo"],
|
||||||
true, true, false,
|
true, true, false,
|
||||||
true, 1, 1, "VALUE");
|
true, 1, 1, "VALUE");
|
||||||
|
|
||||||
|
$argDef = new ArgDef(["-a:", "-b:"]);
|
||||||
|
self::assertArg($argDef,
|
||||||
|
["-a", "-b"],
|
||||||
|
true, false, false,
|
||||||
|
true, 1, 1, "VALUE");
|
||||||
|
|
||||||
|
$argDef = new ArgDef(["-a:", "-b::"]);
|
||||||
|
self::assertArg($argDef,
|
||||||
|
["-a", "-b"],
|
||||||
|
true, false, false,
|
||||||
|
true, 1, 1, "VALUE");
|
||||||
|
|
||||||
|
$argDef = new ArgDef(["-a::", "-b:"]);
|
||||||
|
self::assertArg($argDef,
|
||||||
|
["-a", "-b"],
|
||||||
|
true, false, false,
|
||||||
|
true, 1, 1, "VALUE");
|
||||||
|
|
||||||
|
$argDef = new ArgDef(["-o", "--longo", "args" => true]);
|
||||||
|
self::assertArg($argDef,
|
||||||
|
["-o", "--longo"],
|
||||||
|
true, true, false,
|
||||||
|
true, 1, 1, "VALUE");
|
||||||
|
|
||||||
|
$argDef = new ArgDef(["-o", "--longo", "args" => 1]);
|
||||||
|
self::assertArg($argDef,
|
||||||
|
["-o", "--longo"],
|
||||||
|
true, true, false,
|
||||||
|
true, 1, 1, "VALUE");
|
||||||
|
|
||||||
|
$argDef = new ArgDef(["-o", "--longo", "args" => "value"]);
|
||||||
|
self::assertArg($argDef,
|
||||||
|
["-o", "--longo"],
|
||||||
|
true, true, false,
|
||||||
|
true, 1, 1, "VALUE");
|
||||||
|
|
||||||
|
$argDef = new ArgDef(["-o", "--longo", "args" => ["value"]]);
|
||||||
|
self::assertArg($argDef,
|
||||||
|
["-o", "--longo"],
|
||||||
|
true, true, false,
|
||||||
|
true, 1, 1, "VALUE");
|
||||||
|
}
|
||||||
|
|
||||||
|
function testArgsOptional() {
|
||||||
$argDef = new ArgDef(["-o::", "--longo"]);
|
$argDef = new ArgDef(["-o::", "--longo"]);
|
||||||
self::assertArg($argDef,
|
self::assertArg($argDef,
|
||||||
["-o", "--longo"],
|
["-o", "--longo"],
|
||||||
true, true, false,
|
true, true, false,
|
||||||
true, 0, 1, "[VALUE]");
|
true, 0, 1, "[VALUE]");
|
||||||
|
|
||||||
$argDef = new ArgDef(["-o:", "--longo:"]);
|
$argDef = new ArgDef(["-o", "--longo", "args" => [["value"]]]);
|
||||||
self::assertArg($argDef,
|
self::assertArg($argDef,
|
||||||
["-o", "--longo"],
|
["-o", "--longo"],
|
||||||
true, true, false,
|
true, true, false,
|
||||||
true, 1, 1, "VALUE");
|
true, 0, 1, "[VALUE]");
|
||||||
|
|
||||||
$argDef = new ArgDef(["-o:", "--longo::"]);
|
$argDef = new ArgDef(["-o", "--longo", "args" => [[null]]]);
|
||||||
self::assertArg($argDef,
|
self::assertArg($argDef,
|
||||||
["-o", "--longo"],
|
["-o", "--longo"],
|
||||||
true, true, false,
|
true, true, false,
|
||||||
true, 1, 1, "VALUE");
|
true, 0, PHP_INT_MAX, "[VALUEs...]");
|
||||||
|
|
||||||
|
$argDef = new ArgDef(["-o", "--longo", "args" => ["value", null]]);
|
||||||
|
self::assertArg($argDef,
|
||||||
|
["-o", "--longo"],
|
||||||
|
true, true, false,
|
||||||
|
true, 1, PHP_INT_MAX, "VALUE [VALUEs...]");
|
||||||
}
|
}
|
||||||
|
|
||||||
function testExtends() {
|
function testParent() {
|
||||||
$argDef = [
|
$BASE = ["-o:", "--longo"];
|
||||||
"extends" => [
|
|
||||||
"arg" => ["-o:", "--longo"],
|
$argDef = new ArgDef([
|
||||||
|
"parent" => [
|
||||||
|
"arg" => $BASE,
|
||||||
"add" => ["-a", "--longa"],
|
"add" => ["-a", "--longa"],
|
||||||
"remove" => ["-o", "--longo"],
|
"remove" => ["-o", "--longo"],
|
||||||
],
|
],
|
||||||
];
|
]);
|
||||||
$arg = new ArgDef($argDef);
|
self::assertArg($argDef,
|
||||||
self::assertArg($arg,
|
|
||||||
["-a", "--longa"],
|
["-a", "--longa"],
|
||||||
true, true, false,
|
true, true, false,
|
||||||
false, 0, 0, "");
|
false, 0, 0, "");
|
||||||
|
|
||||||
|
$argDef = new ArgDef([
|
||||||
|
"parent" => [
|
||||||
|
"arg" => $BASE,
|
||||||
|
"add" => ["-a", "--longa"],
|
||||||
|
"remove" => ["-o", "--longo"],
|
||||||
|
],
|
||||||
|
"-x",
|
||||||
|
]);
|
||||||
|
self::assertArg($argDef,
|
||||||
|
["-a", "--longa", "-x"],
|
||||||
|
true, true, false,
|
||||||
|
false, 0, 0, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@ namespace nulib\app\cli;
|
|||||||
|
|
||||||
use nur\t\TestCase;
|
use nur\t\TestCase;
|
||||||
|
|
||||||
class ArgDefsTest extends TestCase {
|
class SimpleArgDefsTest extends TestCase {
|
||||||
function testBase() {
|
function testNormalize() {
|
||||||
$argDefs = new ArgDefs([
|
$argDefs = new SimpleArgDefs([
|
||||||
["-a"],
|
["-a"],
|
||||||
["--longb"],
|
["--longb"],
|
||||||
["-c", "--longc"],
|
["-c", "--longc"],
|
Loading…
x
Reference in New Issue
Block a user