pip compatible server to serve Python packages out of GitHub
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

249 lines
6.4 KiB

package param
import (
"encoding"
"fmt"
"reflect"
"strconv"
"strings"
)
var textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
// Generic parse dispatcher. This function's signature is the interface of all
// parse functions. `key` is the entire key that is currently being parsed, such
// as "foo[bar][]". `keytail` is the portion of the string that the current
// parser is responsible for, for instance "[bar][]". `values` is the list of
// values assigned to this key, and `target` is where the resulting typed value
// should be Set() to.
func parse(key, keytail string, values []string, target reflect.Value) {
t := target.Type()
if reflect.PtrTo(t).Implements(textUnmarshalerType) {
parseTextUnmarshaler(key, keytail, values, target)
return
}
switch k := target.Kind(); k {
case reflect.Bool:
parseBool(key, keytail, values, target)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
parseInt(key, keytail, values, target)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
parseUint(key, keytail, values, target)
case reflect.Float32, reflect.Float64:
parseFloat(key, keytail, values, target)
case reflect.Map:
parseMap(key, keytail, values, target)
case reflect.Ptr:
parsePtr(key, keytail, values, target)
case reflect.Slice:
parseSlice(key, keytail, values, target)
case reflect.String:
parseString(key, keytail, values, target)
case reflect.Struct:
parseStruct(key, keytail, values, target)
default:
pebkac("unsupported object of type %v and kind %v.",
target.Type(), k)
}
}
// We pass down both the full key ("foo[bar][]") and the part the current layer
// is responsible for making sense of ("[bar][]"). This computes the other thing
// you probably want to know, which is the path you took to get here ("foo").
func kpath(key, keytail string) string {
l, t := len(key), len(keytail)
return key[:l-t]
}
// Helper for validating that a value has been passed exactly once, and that the
// user is not attempting to nest on the key.
func primitive(key, keytail string, tipe reflect.Type, values []string) {
if keytail != "" {
panic(NestingError{
Key: kpath(key, keytail),
Type: tipe,
Nesting: keytail,
})
}
if len(values) != 1 {
panic(SingletonError{
Key: kpath(key, keytail),
Type: tipe,
Values: values,
})
}
}
func keyed(tipe reflect.Type, key, keytail string) (string, string) {
if keytail[0] != '[' {
panic(SyntaxError{
Key: kpath(key, keytail),
Subtype: MissingOpeningBracket,
ErrorPart: keytail,
})
}
idx := strings.IndexRune(keytail, ']')
if idx == -1 {
panic(SyntaxError{
Key: kpath(key, keytail),
Subtype: MissingClosingBracket,
ErrorPart: keytail[1:],
})
}
return keytail[1:idx], keytail[idx+1:]
}
func parseTextUnmarshaler(key, keytail string, values []string, target reflect.Value) {
primitive(key, keytail, target.Type(), values)
tu := target.Addr().Interface().(encoding.TextUnmarshaler)
err := tu.UnmarshalText([]byte(values[0]))
if err != nil {
panic(TypeError{
Key: kpath(key, keytail),
Type: target.Type(),
Err: err,
})
}
}
func parseBool(key, keytail string, values []string, target reflect.Value) {
primitive(key, keytail, target.Type(), values)
switch values[0] {
case "true", "1", "on":
target.SetBool(true)
case "false", "0", "":
target.SetBool(false)
default:
panic(TypeError{
Key: kpath(key, keytail),
Type: target.Type(),
})
}
}
func parseInt(key, keytail string, values []string, target reflect.Value) {
t := target.Type()
primitive(key, keytail, t, values)
i, err := strconv.ParseInt(values[0], 10, t.Bits())
if err != nil {
panic(TypeError{
Key: kpath(key, keytail),
Type: t,
Err: err,
})
}
target.SetInt(i)
}
func parseUint(key, keytail string, values []string, target reflect.Value) {
t := target.Type()
primitive(key, keytail, t, values)
i, err := strconv.ParseUint(values[0], 10, t.Bits())
if err != nil {
panic(TypeError{
Key: kpath(key, keytail),
Type: t,
Err: err,
})
}
target.SetUint(i)
}
func parseFloat(key, keytail string, values []string, target reflect.Value) {
t := target.Type()
primitive(key, keytail, t, values)
f, err := strconv.ParseFloat(values[0], t.Bits())
if err != nil {
panic(TypeError{
Key: kpath(key, keytail),
Type: t,
Err: err,
})
}
target.SetFloat(f)
}
func parseString(key, keytail string, values []string, target reflect.Value) {
primitive(key, keytail, target.Type(), values)
target.SetString(values[0])
}
func parseSlice(key, keytail string, values []string, target reflect.Value) {
t := target.Type()
// BUG(carl): We currently do not handle slices of nested types. If
// support is needed, the implementation probably could be fleshed out.
if keytail != "[]" {
panic(NestingError{
Key: kpath(key, keytail),
Type: t,
Nesting: keytail,
})
}
slice := reflect.MakeSlice(t, len(values), len(values))
kp := kpath(key, keytail)
for i := range values {
// We actually cheat a little bit and modify the key so we can
// generate better debugging messages later
key := fmt.Sprintf("%s[%d]", kp, i)
parse(key, "", values[i:i+1], slice.Index(i))
}
target.Set(slice)
}
func parseMap(key, keytail string, values []string, target reflect.Value) {
t := target.Type()
mapkey, maptail := keyed(t, key, keytail)
// BUG(carl): We don't support any map keys except strings, although
// there's no reason we shouldn't be able to throw the value through our
// unparsing stack.
var mk reflect.Value
if t.Key().Kind() == reflect.String {
mk = reflect.ValueOf(mapkey).Convert(t.Key())
} else {
pebkac("key for map %v isn't a string (it's a %v).", t, t.Key())
}
if target.IsNil() {
target.Set(reflect.MakeMap(t))
}
val := target.MapIndex(mk)
if !val.IsValid() || !val.CanSet() {
// It's a teensy bit annoying that the value returned by
// MapIndex isn't Set()table if the key exists.
val = reflect.New(t.Elem()).Elem()
}
parse(key, maptail, values, val)
target.SetMapIndex(mk, val)
}
func parseStruct(key, keytail string, values []string, target reflect.Value) {
t := target.Type()
sk, skt := keyed(t, key, keytail)
cache := cacheStruct(t)
parseStructField(cache, key, sk, skt, values, target)
}
func parsePtr(key, keytail string, values []string, target reflect.Value) {
t := target.Type()
if target.IsNil() {
target.Set(reflect.New(t.Elem()))
}
parse(key, keytail, values, target.Elem())
}