gob/shell/console/prompt.go

153 lines
4.2 KiB
Go
Raw Normal View History

2023-10-07 19:13:39 +00:00
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 dentier.
func PromptInt(sp PromptFunc) ParsedPromptFunc[int] {
return ParsedPrompt(sp, singleConv[int])
}
// PromptUint transforme un prompt en prompt dentier 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 nest 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 dentiers.
func PromptSliceInt(sp PromptFunc, t ...scanner.Tokenizer) ParsedPromptFunc[[]int] {
return MultiplePrompt(sp, singleConv[int], t...)
}
// PromptSliceUint transform un prompt en prompt de slice dentiers 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...)
}