2023-05-27 11:37:40 +00:00
|
|
|
package input
|
|
|
|
|
2023-06-02 09:17:22 +00:00
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
2023-05-27 11:37:40 +00:00
|
|
|
|
2023-06-02 09:17:22 +00:00
|
|
|
"golang.org/x/exp/slices"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ListBinder describes an interface that simple lists can implement to be compatible with list based UI components.
|
2023-05-27 11:37:40 +00:00
|
|
|
type ListBinder interface {
|
|
|
|
ListKeyValuePairs() []ListKeyValuePair
|
2023-06-02 09:17:22 +00:00
|
|
|
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.
|
2023-05-27 11:37:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2023-06-02 09:17:22 +00:00
|
|
|
Slice *[]T
|
2023-05-27 11:37:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l ListBindGenericSlice[T]) ListKeyValuePairs() []ListKeyValuePair {
|
2023-06-02 09:17:22 +00:00
|
|
|
if l.Slice == nil {
|
2023-05-27 11:37:40 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
result := make([]ListKeyValuePair, 0, len(*l.Slice))
|
|
|
|
for i, entry := range *l.Slice {
|
2023-06-02 09:17:22 +00:00
|
|
|
|
|
|
|
var str string
|
|
|
|
switch v := any(entry).(type) {
|
|
|
|
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})
|
2023-05-27 11:37:40 +00:00
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2023-06-02 09:17:22 +00:00
|
|
|
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
|
2023-05-27 11:37:40 +00:00
|
|
|
}
|
|
|
|
|
2023-06-02 09:17:22 +00:00
|
|
|
func (l ListBindGenericSlice[T]) ListAddKeyValuePair(key, value string) error {
|
|
|
|
var val T
|
|
|
|
switch v := any(&val).(type) {
|
|
|
|
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)
|
2023-05-27 11:37:40 +00:00
|
|
|
}
|
|
|
|
|
2023-06-02 09:17:22 +00:00
|
|
|
index, err := strconv.Atoi(key)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to parse index: %w", err)
|
2023-05-27 11:37:40 +00:00
|
|
|
}
|
2023-06-02 09:17:22 +00:00
|
|
|
|
|
|
|
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)
|
2023-05-27 11:37:40 +00:00
|
|
|
}
|