modifs.mineures sans commentaires
This commit is contained in:
parent
b0a677c6b6
commit
605db70cc5
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
namespace nur\sery\cli;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use nulib\ExitException;
|
||||||
|
use nur\sery\output\msg;
|
||||||
|
use nur\sery\output\std\StdMessenger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Application: une application en ligne de commande
|
||||||
|
*/
|
||||||
|
abstract class Application {
|
||||||
|
protected static function _app_init(): void {
|
||||||
|
msg::set_messenger_class(StdMessenger::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function _app_configure(Application $app): void {
|
||||||
|
$app->parseArgs();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function _app_main(Application $app): void {
|
||||||
|
$retcode = $app->main();
|
||||||
|
if (is_int($retcode)) exit($retcode);
|
||||||
|
elseif (is_bool($retcode)) exit($retcode? 0: 1);
|
||||||
|
elseif ($retcode !== null) exit(strval($retcode));
|
||||||
|
}
|
||||||
|
|
||||||
|
static function run(?Application $app=null): void {
|
||||||
|
try {
|
||||||
|
static::_app_init();
|
||||||
|
if ($app === null) $app = new static();
|
||||||
|
static::_app_configure($app);
|
||||||
|
static::_app_main($app);
|
||||||
|
} catch (ExitException $e) {
|
||||||
|
msg::error($e->getUserMessage());
|
||||||
|
exit($e->getCode());
|
||||||
|
} catch (Exception $e) {
|
||||||
|
msg::error($e);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sortir de l'application avec un code d'erreur, qui est 0 par défaut (i.e
|
||||||
|
* pas d'erreur)
|
||||||
|
*
|
||||||
|
* équivalent à lancer l'exception {@link ExitException}
|
||||||
|
*/
|
||||||
|
protected static final function exit(int $exitcode=0, $message=null) {
|
||||||
|
throw new ExitException($exitcode, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sortir de l'application avec un code d'erreur, qui vaut 1 par défaut (i.e
|
||||||
|
* une erreur s'est produite)
|
||||||
|
*
|
||||||
|
* équivalent à lancer l'exception {@link ExitException}
|
||||||
|
*/
|
||||||
|
protected static final function die($message=null, int $exitcode=1) {
|
||||||
|
throw new ExitException($exitcode, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ARGS = [];
|
||||||
|
|
||||||
|
/** @throws ArgsException */
|
||||||
|
function parseArgs(array $args=null): void {
|
||||||
|
$parser = new ArgsParser(static::ARGS);
|
||||||
|
$parser->parse($this, $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract function main();
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
namespace nur\sery\cli;
|
||||||
|
|
||||||
|
use nulib\UserException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ArgsException: exception lancée quand il y a une erreur dans l'analyse
|
||||||
|
* des arguments de la ligne de commande
|
||||||
|
*/
|
||||||
|
class ArgsException extends UserException {
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,82 @@
|
||||||
|
<?php
|
||||||
|
namespace nur\sery\cli;
|
||||||
|
|
||||||
|
use nur\sery\php\func;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class DynamicCommand: implémentation par défaut de {@link IDynamicCommand}
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class DynamicCommand implements IDynamicCommand {
|
||||||
|
/**
|
||||||
|
* retourner la liste des commandes sous la forme d'un tableau associatif avec
|
||||||
|
* des éléments { $command => $cdef }
|
||||||
|
*/
|
||||||
|
protected function COMMANDS(): array {
|
||||||
|
return static::COMMANDS;
|
||||||
|
} const COMMANDS = null;
|
||||||
|
|
||||||
|
private $commands;
|
||||||
|
private $dcommands;
|
||||||
|
private $aliases;
|
||||||
|
|
||||||
|
protected function buildCommands(): void {
|
||||||
|
if ($this->commands !== null) return;
|
||||||
|
$commands = [];
|
||||||
|
$dcommands = [];
|
||||||
|
$aliases = [];
|
||||||
|
$index = 0;
|
||||||
|
foreach ($this->COMMANDS() as $key => $cdef) {
|
||||||
|
if ($key === $index) {
|
||||||
|
$index++;
|
||||||
|
[$cnames, $assoc] = A::split_assoc($cdef);
|
||||||
|
$cname = $cnames[0];
|
||||||
|
if ($cname === null) {
|
||||||
|
# commande complètement dynamique
|
||||||
|
$dcommands[] = $cnames[2];
|
||||||
|
if ($cnames[1] === null) continue;
|
||||||
|
$cdef = [null, $cnames[1]];
|
||||||
|
$cname = $cnames[1][0];
|
||||||
|
$cnames = [];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$cname = $key;
|
||||||
|
$cnames = [$cname];
|
||||||
|
[$seq, $assoc] = A::split_assoc($cdef);
|
||||||
|
A::merge($cnames, $seq);
|
||||||
|
A::merge_assoc($cdef, $cnames, $assoc, true);
|
||||||
|
}
|
||||||
|
$commands[$cname] = $cdef;
|
||||||
|
foreach ($cnames as $key) {
|
||||||
|
$aliases[$key] = $cname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->commands = $commands;
|
||||||
|
$this->dcommands = $dcommands;
|
||||||
|
$this->aliases = $aliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCommands(): ?array {
|
||||||
|
$this->buildCommands();
|
||||||
|
return array_keys($this->commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCommandDefs(string $command, bool $virtual): ?array {
|
||||||
|
$this->buildCommands();
|
||||||
|
$command = A::get($this->aliases, $command, $command);
|
||||||
|
$cdef = A::get($this->commands, $command);
|
||||||
|
if ($cdef !== null) {
|
||||||
|
if ($cdef[0] === null) {
|
||||||
|
if ($virtual) $cdef = $cdef[1];
|
||||||
|
else return null;
|
||||||
|
}
|
||||||
|
return $cdef !== null? [$cdef]: null;
|
||||||
|
}
|
||||||
|
# tester les commandes complètement dynamiques
|
||||||
|
foreach ($this->dcommands as $func) {
|
||||||
|
$cdef = func::call($func, $command);
|
||||||
|
if ($cdef !== null) return [$cdef];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
namespace nur\sery\cli;
|
||||||
|
|
||||||
|
use nur\sery\php\func;
|
||||||
|
|
||||||
|
class DynamicCommandMethod implements IDynamicCommand {
|
||||||
|
function __construct($func) {
|
||||||
|
$this->func = $func;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var object */
|
||||||
|
private $dest;
|
||||||
|
|
||||||
|
function setDest($dest): void {
|
||||||
|
if (!is_object($dest)) $dest = null;
|
||||||
|
$this->dest = $dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCommands(): ?array {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private $func;
|
||||||
|
|
||||||
|
function getCommandDefs(string $command, bool $virtual): ?array {
|
||||||
|
$func = $this->func;
|
||||||
|
$func_args = [$command];
|
||||||
|
func::check_func($func, $this->dest, $func_args);
|
||||||
|
return func::call($func, ...$func_args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
namespace nur\sery\cli;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class IDynamicCommand: gestionnaire de commandes dynamiques
|
||||||
|
*/
|
||||||
|
interface IDynamicCommand {
|
||||||
|
/**
|
||||||
|
* retourner la liste des commandes valides, ou null si cette liste ne peut
|
||||||
|
* pas être construite
|
||||||
|
*/
|
||||||
|
function getCommands(): ?array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retourner les définitions pour la commande spécifiée, ou null si elle n'est
|
||||||
|
* pas valide
|
||||||
|
*/
|
||||||
|
function getCommandDefs(string $command, bool $virtual): ?array;
|
||||||
|
}
|
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
Ce package contient des services permettant d'interagir avec le système d'exploitation
|
Ce package contient des services permettant d'interagir avec le système d'exploitation
|
||||||
|
|
||||||
exemple: sh, args, file, IOException, etc.
|
exemple: args, etc.
|
||||||
|
|
||||||
-*- 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
|
|
@ -0,0 +1,85 @@
|
||||||
|
<?php
|
||||||
|
namespace nur\sery\ref\cli;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ref_args: référence du format des arguments pour une application
|
||||||
|
*/
|
||||||
|
class ref_args {
|
||||||
|
const DEFS_SCHEMA = [
|
||||||
|
"set_defaults" => [null, null, "tableau contenant des paramètres et des options par défaut"],
|
||||||
|
"merge_arrays" => [null, null, "liste de tableaux à merger à celui-ci avant de calculer la liste effective des options"],
|
||||||
|
"merge" => [null, null, "tableau à merger à celui-ci avant de calculer la liste effective des options",
|
||||||
|
# si merge_arrays et merge sont spécifiés tous les deux, "merge" est mergé après "merge_arrays"
|
||||||
|
],
|
||||||
|
"prefix" => [null, null, "texte à afficher avant l'aide générée automatiquement"],
|
||||||
|
"name" => [null, null, "nom du programme, utilisé pour l'affichage de l'aide"],
|
||||||
|
"purpose" => [null, null, "courte description de l'objet de ce programme"],
|
||||||
|
"usage" => [null, null, "exposé textuel des arguments valides du programme",
|
||||||
|
# ce peut être une chaine e.g '[options] SRC DESC'
|
||||||
|
# ou un tableau auquel cas autant de lignes que nécessaire sont affichées
|
||||||
|
],
|
||||||
|
"description" => [null, null, "description longue de l'objet du programme, affiché après usage"],
|
||||||
|
"suffix" => [null, null, "texte à afficher après l'aide générée automatiquement"],
|
||||||
|
"dynamic_command" => [null, null, "fonction indiquant si une commande est valide",
|
||||||
|
# la signature de la fonction est function(string $command):?array
|
||||||
|
# elle doit retourner un tableau au format DEFS_SCHEMA qui définit la
|
||||||
|
# commande spécifiée, ou null si ce n'est pas une commande valide
|
||||||
|
],
|
||||||
|
"sections" => [null, null, "liste de sections permettant de grouper les arguments"],
|
||||||
|
"commandname" => [null, null, "propriété ou clé qui obtient la commande courante",
|
||||||
|
# la valeur par défaut est "command" si ni commandproperty ni commandkey ne sont définis
|
||||||
|
],
|
||||||
|
"commandproperty" => [null, null, "comme commandname mais force l'utilisation d'une propriété"],
|
||||||
|
"commandkey" => [null, null, "comme commandname mais force l'utilisation d'une clé"],
|
||||||
|
"argsname" => [null, null, "propriété ou clé qui obtient les arguments restants",
|
||||||
|
# la valeur par défaut est "args" si ni argsproperty ni argskey ne sont définis
|
||||||
|
],
|
||||||
|
"argsproperty" => [null, null, "comme argsname mais force l'utilisation d'une propriété"],
|
||||||
|
"argskey" => [null, null, "comme argsname mais force l'utilisation d'une clé"],
|
||||||
|
"autohelp" => ["?bool", null, "faut-il ajouter automatiquement le support de l'option --help"],
|
||||||
|
"autoremains" => ["?bool", null, "faut-il ajouter automatiquement la prise en compte des arguments restants"],
|
||||||
|
];
|
||||||
|
|
||||||
|
const SECTION_SCHEMA = [
|
||||||
|
"show" => ["bool", true, "faut-il afficher cette section?"],
|
||||||
|
"title" => [null, null, "titre de la section"],
|
||||||
|
"prefix" => [null, null, "texte à afficher avant l'aide générée automatiquement"],
|
||||||
|
"suffix" => [null, null, "texte à afficher après l'aide générée automatiquement"],
|
||||||
|
|
||||||
|
# ces valeurs sont calculées
|
||||||
|
"defs" => [null, null, "(interne) liste des définitions de cette section"],
|
||||||
|
];
|
||||||
|
|
||||||
|
const DEF_SCHEMA = [
|
||||||
|
"set_defaults" => [null, null, "tableau contenant des paramètres par défaut"],
|
||||||
|
"merge_arrays" => [null, null, "liste de tableaux à merger à celui-ci"],
|
||||||
|
"merge" => [null, null, "tableau à merger à celui-ci",
|
||||||
|
# si merge_arrays et merge sont spécifiés tous les deux, "merge" est mergé après "merge_arrays"
|
||||||
|
],
|
||||||
|
"kind" => [null, null, "type de définition: 'option' ou 'command'"],
|
||||||
|
"arg" => [null, null, "type de l'argument attendu par l'option"],
|
||||||
|
"args" => [null, null, "type des arguments attendus par l'option",
|
||||||
|
# si args est spécifié, arg est ignoré
|
||||||
|
],
|
||||||
|
"argsdesc" => [null, null, "description textuelle des arguments, utilisé pour l'affichage de l'aide"],
|
||||||
|
"type" => [null, null, "types dans lesquels convertir les arguments avant de les fournir à l'utilisateur"],
|
||||||
|
"action" => [null, null, "fonction à appeler quand cette option est utilisée",
|
||||||
|
# la signature de la fonction est ($value, $name, $arg, $dest, $def)
|
||||||
|
],
|
||||||
|
"name" => [null, null, "propriété ou clé à initialiser en réponse à l'utilisation de cette option",
|
||||||
|
# le nom à spécifier est au format under_score, qui est transformée en camelCase si la destination est un objet
|
||||||
|
],
|
||||||
|
"property" => [null, null, "comme name mais force l'utilisation d'une propriété"],
|
||||||
|
"key" => [null, null, "comme name mais force l'utilisation d'une clé"],
|
||||||
|
"inverse" => ["bool", false, "décrémenter la destination au lieu de l'incrémenter pour une option sans argument"],
|
||||||
|
"value" => ["mixed", null, "valeur à forcer au lieu d'incrémenter la destination"],
|
||||||
|
"ensure_array" => [null, null, "forcer la destination à être un tableau"],
|
||||||
|
"help" => [null, null, "description de cette option, utilisé pour l'affichage de l'aide"],
|
||||||
|
"cmd_args" => [null, null, "définition des sous-options pour une commande"],
|
||||||
|
|
||||||
|
# ces valeurs sont calculées
|
||||||
|
"cmd_defs" => [null, null, "(interne) liste des définitions correspondant au paramètre options"],
|
||||||
|
];
|
||||||
|
|
||||||
|
const ARGS_ALLOWED_VALUES = ["value", "path", "dir", "file", "host"];
|
||||||
|
}
|
Loading…
Reference in New Issue