You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

198 lines
4.4 KiB
Go

package input
import (
"encoding"
"fmt"
"strconv"
"golang.org/x/exp/slices"
)
// ListBinder describes an interface that simple lists can implement to be compatible with list based UI components.
type ListBinder interface {
ListKeyValuePairs() []ListKeyValuePair
ListDeleteKey(key string) bool // Removes the entry with the given key, and returns true if it got deleted successfully.
ListAddKeyValuePair(key, value string) error // Adds the key value pair to the list.
ListLen() int // Returns the length of the list.
}
type ListKeyValuePair struct {
Key, Value string
}
// ListBindGenericSlice implements ListBinder for slices that are of some arbitrary type.
// For this to work, you need to supply a function that returns key value pairs for every slice entry.
type ListBindGenericSlice[T any] struct {
Slice *[]T
}
func (l ListBindGenericSlice[T]) ListKeyValuePairs() []ListKeyValuePair {
if l.Slice == nil {
return nil
}
result := make([]ListKeyValuePair, 0, len(*l.Slice))
for i, entry := range *l.Slice {
var str string
switch v := any(entry).(type) {
case encoding.TextMarshaler:
if text, err := v.MarshalText(); err != nil {
str = fmt.Sprintf("%d: %s", i, err.Error())
} else {
str = string(text)
}
case string:
str = v
case int:
str = strconv.FormatInt(int64(v), 10)
case int8:
str = strconv.FormatInt(int64(v), 10)
case int16:
str = strconv.FormatInt(int64(v), 10)
case int32:
str = strconv.FormatInt(int64(v), 10)
case int64:
str = strconv.FormatInt(int64(v), 10)
case uint:
str = strconv.FormatUint(uint64(v), 10)
case uint8:
str = strconv.FormatUint(uint64(v), 10)
case uint16:
str = strconv.FormatUint(uint64(v), 10)
case uint32:
str = strconv.FormatUint(uint64(v), 10)
case uint64:
str = strconv.FormatUint(uint64(v), 10)
case float32:
str = strconv.FormatFloat(float64(v), 'f', -1, 32) // TODO: Format number in current user's locale
case float64:
str = strconv.FormatFloat(float64(v), 'f', -1, 64)
}
result = append(result, ListKeyValuePair{Key: strconv.Itoa(i), Value: str})
}
return result
}
func (l ListBindGenericSlice[T]) ListDeleteKey(key string) bool {
if l.Slice == nil {
return false
}
index, err := strconv.Atoi(key)
if err != nil {
return false
}
if index < 0 || index >= len(*l.Slice) {
return false
}
*l.Slice = slices.Delete(*l.Slice, index, index+1)
return true
}
func (l ListBindGenericSlice[T]) ListAddKeyValuePair(key, value string) error {
var val T
switch v := any(&val).(type) {
case encoding.TextUnmarshaler:
if err := v.UnmarshalText([]byte(value)); err != nil {
return err
}
case *string:
*v = value
case *int:
val, err := strconv.ParseInt(value, 10, 0)
if err != nil {
return err
}
*v = int(val)
case *int8:
val, err := strconv.ParseInt(value, 10, 8)
if err != nil {
return err
}
*v = int8(val)
case *int16:
val, err := strconv.ParseInt(value, 10, 16)
if err != nil {
return err
}
*v = int16(val)
case *int32:
val, err := strconv.ParseInt(value, 10, 32)
if err != nil {
return err
}
*v = int32(val)
case *int64:
val, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
*v = int64(val)
case *uint:
val, err := strconv.ParseUint(value, 10, 0)
if err != nil {
return err
}
*v = uint(val)
case *uint8:
val, err := strconv.ParseUint(value, 10, 8)
if err != nil {
return err
}
*v = uint8(val)
case *uint16:
val, err := strconv.ParseUint(value, 10, 16)
if err != nil {
return err
}
*v = uint16(val)
case *uint32:
val, err := strconv.ParseUint(value, 10, 32)
if err != nil {
return err
}
*v = uint32(val)
case *uint64:
val, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
*v = uint64(val)
case *float32:
val, err := strconv.ParseFloat(value, 32)
if err != nil {
return err
}
*v = float32(val)
case *float64:
val, err := strconv.ParseFloat(value, 64)
if err != nil {
return err
}
*v = float64(val)
default:
return fmt.Errorf("bound type %T is not supported", v)
}
index, err := strconv.Atoi(key)
if err != nil {
return fmt.Errorf("failed to parse index: %w", err)
}
if index < 0 || index > len(*l.Slice) {
return fmt.Errorf("index %d out of bounds", index)
}
*l.Slice = slices.Insert(*l.Slice, index, val)
return nil
}
func (l ListBindGenericSlice[T]) ListLen() int {
return len(*l.Slice)
}