D3vugu-components/components/input/list-binder.go
David Vogel 4d18fad115 Add input component for tags
- Change ListBinder interface
- Improve ListBindGenericSlice and make it the default
- Update input example page
2023-06-02 11:17:22 +02:00

187 lines
4.1 KiB
Go

package input
import (
"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 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 *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)
}