153 lines
4.2 KiB
Go
153 lines
4.2 KiB
Go
|
package console
|
|||
|
|
|||
|
import (
|
|||
|
"fmt"
|
|||
|
"strings"
|
|||
|
|
|||
|
"gitea.zaclys.com/bvaudour/gob/convert"
|
|||
|
. "gitea.zaclys.com/bvaudour/gob/option"
|
|||
|
"gitea.zaclys.com/bvaudour/gob/shell/scanner"
|
|||
|
)
|
|||
|
|
|||
|
// PromptFunc est une fonction qui affiche une invite de commande et retourne la saisie brute.
|
|||
|
type PromptFunc func(string) Result[string]
|
|||
|
|
|||
|
// ParsedPromptFunc est une fonction qui affiche une invite de commande et retourne la saisie parsée.
|
|||
|
type ParsedPromptFunc[T any] func(string) Result[T]
|
|||
|
|
|||
|
// ConvFunc est une fonction qui convertit une chaîne de caractère en un type donné.
|
|||
|
type ConvFunc[T any] func(string) Result[T]
|
|||
|
|
|||
|
// PromptOf transforme un prompter en fonction promptable.
|
|||
|
func PromptOf(p Prompter) PromptFunc {
|
|||
|
return p.Prompt
|
|||
|
}
|
|||
|
|
|||
|
func singleConv[T any](s string) Result[T] {
|
|||
|
var value T
|
|||
|
if !convert.Convert(s, &value) {
|
|||
|
return Err[T](fmt.Errorf("Failed to convert %s", s))
|
|||
|
}
|
|||
|
|
|||
|
return Ok(value)
|
|||
|
}
|
|||
|
|
|||
|
func boolConv(s string) (out Result[bool]) {
|
|||
|
s = strings.ToLower(s)
|
|||
|
switch s {
|
|||
|
case "oui", "o", "yes", "y", "1", "true", "t", "on":
|
|||
|
out = Ok(true)
|
|||
|
case "non", "n", "no", "0", "false", "f", "off":
|
|||
|
out = Ok(false)
|
|||
|
default:
|
|||
|
out = Err[bool](fmt.Errorf("%s: not a boolean", s))
|
|||
|
}
|
|||
|
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func strConv(s string) Result[string] {
|
|||
|
return Ok(s)
|
|||
|
}
|
|||
|
|
|||
|
// ParsedPrompt transforme un prompt en prompt de donnée parsée.
|
|||
|
func ParsedPrompt[T any](sp PromptFunc, conv ConvFunc[T]) ParsedPromptFunc[T] {
|
|||
|
return func(p string) (out Result[T]) {
|
|||
|
sout := sp(p)
|
|||
|
if v, ok := sout.Ok(); ok {
|
|||
|
out = conv(v)
|
|||
|
} else if err, ok := sout.Err(); ok {
|
|||
|
out = Err[T](err)
|
|||
|
}
|
|||
|
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// PromptInt transforme un prompt en prompt d’entier.
|
|||
|
func PromptInt(sp PromptFunc) ParsedPromptFunc[int] {
|
|||
|
return ParsedPrompt(sp, singleConv[int])
|
|||
|
}
|
|||
|
|
|||
|
// PromptUint transforme un prompt en prompt d’entier non signé.
|
|||
|
func PromptUint(sp PromptFunc) ParsedPromptFunc[uint] {
|
|||
|
return ParsedPrompt(sp, singleConv[uint])
|
|||
|
}
|
|||
|
|
|||
|
// PromptFloat transforme un prompt en prompt de nombre décimal.
|
|||
|
func PromptFloat(sp PromptFunc) ParsedPromptFunc[float64] {
|
|||
|
return ParsedPrompt(sp, singleConv[float64])
|
|||
|
}
|
|||
|
|
|||
|
// PromptBool transforme un prompt en prompt de booléen.
|
|||
|
//
|
|||
|
// Valeurs autorisée :
|
|||
|
// - true : O(ui), Y(es), t(rue), 1, on
|
|||
|
// - false : N(on), f(alse), 0, off
|
|||
|
// La valeur est insensible à la casse.
|
|||
|
func PromptBool(sp PromptFunc) ParsedPromptFunc[bool] {
|
|||
|
return ParsedPrompt(sp, boolConv)
|
|||
|
}
|
|||
|
|
|||
|
// MultiplePrompt transforme un prompt en prompt de valeurs multiples.
|
|||
|
// Si aucun tokenizer n’est fourni, il considère la chaîne globale
|
|||
|
// comme des mots séparés par des blancs.
|
|||
|
func MultiplePrompt[T any](
|
|||
|
sp PromptFunc,
|
|||
|
conv ConvFunc[T],
|
|||
|
t ...scanner.Tokenizer,
|
|||
|
) ParsedPromptFunc[[]T] {
|
|||
|
return func(p string) (out Result[[]T]) {
|
|||
|
sout := sp(p)
|
|||
|
if line, ok := sout.Ok(); ok {
|
|||
|
var tk scanner.Tokenizer
|
|||
|
if len(t) > 0 {
|
|||
|
tk = t[0]
|
|||
|
} else {
|
|||
|
tk = scanner.NewTokenizer(false, false)
|
|||
|
}
|
|||
|
sc := scanner.NewScanner(strings.NewReader(line), tk)
|
|||
|
|
|||
|
var tmp []T
|
|||
|
for sc.Scan() {
|
|||
|
elem := conv(sc.Text())
|
|||
|
if v, ok := elem.Ok(); ok {
|
|||
|
tmp = append(tmp, v)
|
|||
|
} else if err, ok := elem.Err(); ok {
|
|||
|
return Err[[]T](err)
|
|||
|
}
|
|||
|
}
|
|||
|
out = Ok(tmp)
|
|||
|
} else if err, ok := sout.Err(); ok {
|
|||
|
out = Err[[]T](err)
|
|||
|
}
|
|||
|
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// PromptSlice transform un prompt en prompt de slice de strings.
|
|||
|
func PromptSlice(sp PromptFunc, t ...scanner.Tokenizer) ParsedPromptFunc[[]string] {
|
|||
|
return MultiplePrompt(sp, strConv, t...)
|
|||
|
}
|
|||
|
|
|||
|
// PromptSliceInt transform un prompt en prompt de slice d’entiers.
|
|||
|
func PromptSliceInt(sp PromptFunc, t ...scanner.Tokenizer) ParsedPromptFunc[[]int] {
|
|||
|
return MultiplePrompt(sp, singleConv[int], t...)
|
|||
|
}
|
|||
|
|
|||
|
// PromptSliceUint transform un prompt en prompt de slice d’entiers non signés.
|
|||
|
func PromptSliceUint(sp PromptFunc, t ...scanner.Tokenizer) ParsedPromptFunc[[]uint] {
|
|||
|
return MultiplePrompt(sp, singleConv[uint], t...)
|
|||
|
}
|
|||
|
|
|||
|
// PromptSliceFloat transform un prompt en prompt de slice de décimaux.
|
|||
|
func PromptSliceFloat(sp PromptFunc, t ...scanner.Tokenizer) ParsedPromptFunc[[]float64] {
|
|||
|
return MultiplePrompt(sp, singleConv[float64], t...)
|
|||
|
}
|
|||
|
|
|||
|
// PromptSliceBool transform un prompt en prompt de slice de booléens.
|
|||
|
func PromptSliceBool(sp PromptFunc, t ...scanner.Tokenizer) ParsedPromptFunc[[]bool] {
|
|||
|
return MultiplePrompt(sp, boolConv, t...)
|
|||
|
}
|