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. // // List keys will be bound to slice indices, and list values will be bound to a string representation of the slice elements. 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 { str := ValueBindAny{entry}.StringValue() 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 { 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) } var val T if err := (ValueBindAny{&val}).SetStringValue(value); err != nil { return err } *l.Slice = slices.Insert(*l.Slice, index, val) return nil } func (l ListBindGenericSlice[T]) ListLen() int { return len(*l.Slice) } // ListBindGenericValues implements ListBinder for slices that are of some arbitrary type. // // List keys and values will be bound to the string representation of the slice elements. // // This is useful for dropdown components, as the key of the selected element will be set to the bound value. // // This can not modify the bound list. type ListBindGenericValues[T any] []T func (l ListBindGenericValues[T]) ListKeyValuePairs() []ListKeyValuePair { result := make([]ListKeyValuePair, 0, len(l)) for _, entry := range l { str := ValueBindAny{entry}.StringValue() result = append(result, ListKeyValuePair{Key: str, Value: str}) } return result } func (l ListBindGenericValues[T]) ListDeleteKey(key string) bool { return false } func (l ListBindGenericValues[T]) ListAddKeyValuePair(key, value string) error { return fmt.Errorf("the list binder doesn't support list modifications") } func (l ListBindGenericValues[T]) ListLen() int { return len(l) }