gob/shell/console/console.go

176 lines
4.6 KiB
Go
Raw Normal View History

2023-10-07 19:13:39 +00:00
package console
import (
"fmt"
"io"
"os"
"regexp"
"strings"
. "gitea.zaclys.com/bvaudour/gob/option"
)
// Cycler est une interface permettant de cycler dans les entrées dune liste.
type Cycler interface {
Len() int // Retourne la longueur de la liste
Cursor() int // Retourne lindex de lentrée pointée
Index(int) Option[string] // Retourne lentrée à lindex donné
SetCursor(int) bool // Déplace lentrée pointée à lindex demandé et retourne vrai si cet index existe
Append(string) // Ajoute une entrée dans la liste
Clear() // Efface la liste
Next() bool // Pointe vers lentrée suivante
Prev() bool // Pointe vers lentrée précédente
}
// History est une interface pour gérer lhistorique des commandes saisies.
type History interface {
Cycler
Read(io.Reader) Result[int]
Write(io.Writer) Result[int]
}
// Prompter est une interface pour représenter un prompt.
type Prompter interface {
Prompt(string) Result[string] // Affiche le prompt et retourne la saisie
}
// Console est un prompt avec gestion de lhistorique.
type Console interface {
Prompter
LoadHistory(io.Reader) Result[int]
SaveHistory(io.Writer) Result[int]
ClearHistory()
AppendHistory(string)
}
// Error représente une erreur terminal
type Error interface {
Error() string
Code() int
}
// Exit quitte le programme en affichant une erreur.
// Si aucun descripteur nest fourni, le message derreur
// est écrit sur la sortie erreur standard.
func Exit(code int, msg string, w ...io.Writer) {
var out io.Writer
if len(w) > 0 {
out = w[0]
} else {
out = os.Stderr
}
fmt.Fprintln(out, msg)
os.Exit(code)
}
// ExitfOn agit comme Exit mais mais formate le message.
func ExitfOn(code int, tmpl string, w io.Writer, args ...any) {
msg := fmt.Sprintf(tmpl, args...)
Exit(code, msg, w)
}
// Exitf agit comme ExitfOn sur la sortie erreur standard.
func Exitf(code int, tmpl string, args ...any) {
ExitfOn(code, tmpl, os.Stderr, args...)
}
// ExitWithError quitte le programme en utilisant
// lerreur fournie.
func ExitWithError(err Error, w ...io.Writer) {
Exit(err.Code(), err.Error(), w...)
}
// HistoryList retourne le contenu de lhistorique.
func HistoryList(h History) (out []string) {
l := h.Len()
out = make([]string, l)
for i := 0; i < l; i++ {
if line, ok := h.Index(i).Get(); ok {
out[i] = line
}
}
return
}
func hfilter(h History, motive string, cb func(string, string) bool, insensitive ...bool) (out []string) {
l := h.Len()
if l == 0 {
return
}
ins := false
if len(insensitive) > 0 {
ins = insensitive[0]
}
var cmp func(string) bool
if ins {
motive = strings.ToLower(motive)
cmp = func(e string) bool {
return cb(strings.ToLower(e), motive)
}
} else {
cmp = func(e string) bool { return cb(e, motive) }
}
for i := 0; i < l; i++ {
if e, ok := h.Index(i).Get(); ok && cmp(e) {
out = append(out, e)
}
}
return
}
// HistoryFilterMotive retourne les entrées de lhistorique contenant le motif donné.
// Il est possible de faire une recherche insensible à la casse.
func HistoryFilterMotive(h History, motive string, insensitive ...bool) (out []string) {
return hfilter(h, motive, strings.Contains, insensitive...)
}
// HistoryFilterPrefix retourne les entrées de lhistorique commençant le préfixe donné.
// Il est possible de faire une recherche insensible à la casse.
func HistoryFilterPrefix(h History, prefix string, insensitive ...bool) (out []string) {
return hfilter(h, prefix, strings.HasPrefix, insensitive...)
}
// HistoryFilterSuffix retourne les entrées de lhistorique terminant par le suffixe donné.
// Il est possible de faire une recherche insensible à la casse.
func HistoryFilterSuffix(h History, suffix string, insensitive ...bool) (out []string) {
return hfilter(h, suffix, strings.HasSuffix, insensitive...)
}
// HistoryFilter retourne les entrées de lhistorique vérifiant la regexp donnée.
func HistoryFilter(h History, r *regexp.Regexp) (out []string) {
l := h.Len()
if l == 0 {
return
}
for i := 0; i < l; i++ {
if e, ok := h.Index(i).Get(); ok && r.MatchString(e) {
out = append(out, e)
}
}
return
}
// IsFocused retourne vrai si le cycler pointe sur un élément existant.
func IsFocused(c Cycler) bool {
l, n := c.Len(), c.Cursor()
return n >= 0 && n < l
}
// FocusedElement retourne lélément pointé par le cycler.
func FocusedElement(c Cycler) Option[string] {
return c.Index(c.Cursor())
}
// Unfocus enlève le pointage de lhistorique.
func Unfocus(h History) {
h.SetCursor(h.Len())
}