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...)
|
||
}
|