modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2024-01-01 00:45:31 +04:00
parent b0a677c6b6
commit 605db70cc5
8 changed files with 1847 additions and 1 deletions

72
src/cli/Application.php Normal file
View File

@ -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();
}

11
src/cli/ArgsException.php Normal file
View File

@ -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 {
}

1546
src/cli/ArgsParser.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -2,6 +2,6 @@
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

85
src/ref/cli/ref_args.php Normal file
View File

@ -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"];
}