gomap/slice.go

138 lines
3.7 KiB
Go

package gomap
import (
"reflect"
"strconv"
"strings"
)
// The ContentHandler structure
// is a registration structure
// to hold a handler and a remapper.
type ContentHandler struct {
// Handler is the handler.
Handler Handler
// Mapper is the remapper to `interface{}`.
Mapper RemapHandler
}
var (
// The registered handles.
handles = make(map[string]Handler, 0)
// The registered remappers.
remappers = make(map[string]RemapHandler, 0)
// StringHandler global instance.
StringHandler = PutHandler("string", ContentHandler{handleString, remapString})
// IntHandler global instance.
IntHandler = PutHandler("int", ContentHandler{handleInt, remapInt})
// UintHandler global instance.
UintHandler = PutHandler("uint", ContentHandler{handleUint, remapUint})
// FloatHandler global instance.
FloatHandler = PutHandler("float", ContentHandler{handleFloat, remapFloat})
// ComplexHandler global instance.
ComplexHandler = PutHandler("complex", ContentHandler{handleComplex, remapComplex})
// UintptrHandler global instance.
UintptrHandler = PutHandler("uintptr", ContentHandler{handleUintptr, remapUintptr})
)
// NewFromPrimitiveSlice handles slice support, however, to
// avoid the reflect solution to this,
// we try to handle type assertion ourself.
//
// The parameter element takes the first element.
//
// Note that if the slice is of []interface{},
// the New function may be used instead.
func NewFromPrimitiveSlice(slice interface{}, element interface{}) *List {
// The type of the first element.
ty := reflect.TypeOf(element)
// The name of the type.
name := ty.Name()
// The argument to pass.
var arg interface{}
// The list that will eventually be returned.
var list *List
// setList is the embedded local function to
// set the data in the list.
setList := func() {
// Find the handler.
handler, ok := handles[name]
if !ok {
return
}
// Set the list.
list, _ = handler(slice, arg)
}
// If it's a uintptr, exit early.
if name == "uintptr" {
setList()
return list
}
// Check if the value is an integer.
handleNaming(name, "int", func(s []string) {
name = s[0]
arg, _ = strconv.Atoi(s[1])
}).OnFail(
// Check if the value is a float.
func() {
handleNaming(name, "float", func(s []string) {
name = s[0]
arg, _ = strconv.Atoi(s[1])
})
},
).OnFail(func() {
// Check if the value is complex.
handleNaming(name, "complex", func(s []string) {
name = s[0]
arg, _ = strconv.Atoi(s[1])
})
}).Cleanup(setList)
return list
}
// handleNaming checks the a condition and calls the respective callback
// after the string split.
func handleNaming(str string, expected string, callback func(s []string)) Callable {
// If the string contains the expected string,
// we can split it, then invoke the callback.
if strings.Contains(str, expected) {
split := strings.SplitAfterN(str, expected, 2)
callback(split)
return Callable{Passed: true}
}
return Callable{Passed: false}
}
// PutHandler puts a handler into the registered handlers map,
// registered by name.
func PutHandler(name string, handler ContentHandler) ContentHandler {
handles[name] = handler.Handler
remappers[name] = handler.Mapper
return handler
}
// The remap function remaps a slice given remapper arguments and a remapper.
func remap(remapper *RemapHandler, slice interface{}, args ...interface{}) []interface{} {
// If the remapper is nil, we can't go forward.
if remapper == nil {
return nil
}
// If there are no arguments, we don't have to include them.
// We can pass only the slice into the invocation.
if args == nil || args[0] == nil {
return (*remapper)(slice)
}
// There are arguments, so we will spread the arguments and pass
// them forward.
return (*remapper)(slice, args...)
}