automation-tests/vendor/github.com/pquerna/ffjson/inception/decoder_tpl.go

774 lines
18 KiB
Go

/**
* Copyright 2014 Paul Querna
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ffjsoninception
import (
"reflect"
"strconv"
"text/template"
)
var decodeTpl map[string]*template.Template
func init() {
decodeTpl = make(map[string]*template.Template)
funcs := map[string]string{
"handlerNumeric": handlerNumericTxt,
"allowTokens": allowTokensTxt,
"handleFallback": handleFallbackTxt,
"handleString": handleStringTxt,
"handleObject": handleObjectTxt,
"handleArray": handleArrayTxt,
"handleSlice": handleSliceTxt,
"handleByteSlice": handleByteSliceTxt,
"handleBool": handleBoolTxt,
"handlePtr": handlePtrTxt,
"header": headerTxt,
"ujFunc": ujFuncTxt,
"handleUnmarshaler": handleUnmarshalerTxt,
}
tplFuncs := template.FuncMap{
"getAllowTokens": getAllowTokens,
"getNumberSize": getNumberSize,
"getType": getType,
"handleField": handleField,
"handleFieldAddr": handleFieldAddr,
"unquoteField": unquoteField,
"getTmpVarFor": getTmpVarFor,
}
for k, v := range funcs {
decodeTpl[k] = template.Must(template.New(k).Funcs(tplFuncs).Parse(v))
}
}
type handlerNumeric struct {
IC *Inception
Name string
ParseFunc string
Typ reflect.Type
TakeAddr bool
}
var handlerNumericTxt = `
{
{{$ic := .IC}}
if tok == fflib.FFTok_null {
{{if eq .TakeAddr true}}
{{.Name}} = nil
{{end}}
} else {
{{if eq .ParseFunc "ParseFloat" }}
tval, err := fflib.{{ .ParseFunc}}(fs.Output.Bytes(), {{getNumberSize .Typ}})
{{else}}
tval, err := fflib.{{ .ParseFunc}}(fs.Output.Bytes(), 10, {{getNumberSize .Typ}})
{{end}}
if err != nil {
return fs.WrapErr(err)
}
{{if eq .TakeAddr true}}
ttypval := {{getType $ic .Name .Typ}}(tval)
{{.Name}} = &ttypval
{{else}}
{{.Name}} = {{getType $ic .Name .Typ}}(tval)
{{end}}
}
}
`
type allowTokens struct {
Name string
Tokens []string
}
var allowTokensTxt = `
{
if {{range $index, $element := .Tokens}}{{if ne $index 0 }}&&{{end}} tok != fflib.{{$element}}{{end}} {
return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for {{.Name}}", tok))
}
}
`
type handleFallback struct {
Name string
Typ reflect.Type
Kind reflect.Kind
}
var handleFallbackTxt = `
{
/* Falling back. type={{printf "%v" .Typ}} kind={{printf "%v" .Kind}} */
tbuf, err := fs.CaptureField(tok)
if err != nil {
return fs.WrapErr(err)
}
err = json.Unmarshal(tbuf, &{{.Name}})
if err != nil {
return fs.WrapErr(err)
}
}
`
type handleString struct {
IC *Inception
Name string
Typ reflect.Type
TakeAddr bool
Quoted bool
}
var handleStringTxt = `
{
{{$ic := .IC}}
{{getAllowTokens .Typ.Name "FFTok_string" "FFTok_null"}}
if tok == fflib.FFTok_null {
{{if eq .TakeAddr true}}
{{.Name}} = nil
{{end}}
} else {
{{if eq .TakeAddr true}}
var tval {{getType $ic .Name .Typ}}
outBuf := fs.Output.Bytes()
{{unquoteField .Quoted}}
tval = {{getType $ic .Name .Typ}}(string(outBuf))
{{.Name}} = &tval
{{else}}
outBuf := fs.Output.Bytes()
{{unquoteField .Quoted}}
{{.Name}} = {{getType $ic .Name .Typ}}(string(outBuf))
{{end}}
}
}
`
type handleObject struct {
IC *Inception
Name string
Typ reflect.Type
Ptr reflect.Kind
TakeAddr bool
}
var handleObjectTxt = `
{
{{$ic := .IC}}
{{getAllowTokens .Typ.Name "FFTok_left_bracket" "FFTok_null"}}
if tok == fflib.FFTok_null {
{{.Name}} = nil
} else {
{{if eq .TakeAddr true}}
{{if eq .Typ.Elem.Kind .Ptr }}
{{if eq .Typ.Key.Kind .Ptr }}
var tval = make(map[*{{getType $ic .Name .Typ.Key.Elem}}]*{{getType $ic .Name .Typ.Elem.Elem}}, 0)
{{else}}
var tval = make(map[{{getType $ic .Name .Typ.Key}}]*{{getType $ic .Name .Typ.Elem.Elem}}, 0)
{{end}}
{{else}}
{{if eq .Typ.Key.Kind .Ptr }}
var tval = make(map[*{{getType $ic .Name .Typ.Key.Elem}}]{{getType $ic .Name .Typ.Elem}}, 0)
{{else}}
var tval = make(map[{{getType $ic .Name .Typ.Key}}]{{getType $ic .Name .Typ.Elem}}, 0)
{{end}}
{{end}}
{{else}}
{{if eq .Typ.Elem.Kind .Ptr }}
{{if eq .Typ.Key.Kind .Ptr }}
{{.Name}} = make(map[*{{getType $ic .Name .Typ.Key.Elem}}]*{{getType $ic .Name .Typ.Elem.Elem}}, 0)
{{else}}
{{.Name}} = make(map[{{getType $ic .Name .Typ.Key}}]*{{getType $ic .Name .Typ.Elem.Elem}}, 0)
{{end}}
{{else}}
{{if eq .Typ.Key.Kind .Ptr }}
{{.Name}} = make(map[*{{getType $ic .Name .Typ.Key.Elem}}]{{getType $ic .Name .Typ.Elem}}, 0)
{{else}}
{{.Name}} = make(map[{{getType $ic .Name .Typ.Key}}]{{getType $ic .Name .Typ.Elem}}, 0)
{{end}}
{{end}}
{{end}}
wantVal := true
for {
{{$keyPtr := false}}
{{if eq .Typ.Key.Kind .Ptr }}
{{$keyPtr := true}}
var k *{{getType $ic .Name .Typ.Key.Elem}}
{{else}}
var k {{getType $ic .Name .Typ.Key}}
{{end}}
{{$valPtr := false}}
{{$tmpVar := getTmpVarFor .Name}}
{{if eq .Typ.Elem.Kind .Ptr }}
{{$valPtr := true}}
var {{$tmpVar}} *{{getType $ic .Name .Typ.Elem.Elem}}
{{else}}
var {{$tmpVar}} {{getType $ic .Name .Typ.Elem}}
{{end}}
tok = fs.Scan()
if tok == fflib.FFTok_error {
goto tokerror
}
if tok == fflib.FFTok_right_bracket {
break
}
if tok == fflib.FFTok_comma {
if wantVal == true {
// TODO(pquerna): this isn't an ideal error message, this handles
// things like [,,,] as an array value.
return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok))
}
continue
} else {
wantVal = true
}
{{handleField .IC "k" .Typ.Key $keyPtr false}}
// Expect ':' after key
tok = fs.Scan()
if tok != fflib.FFTok_colon {
return fs.WrapErr(fmt.Errorf("wanted colon token, but got token: %v", tok))
}
tok = fs.Scan()
{{handleField .IC $tmpVar .Typ.Elem $valPtr false}}
{{if eq .TakeAddr true}}
tval[k] = {{$tmpVar}}
{{else}}
{{.Name}}[k] = {{$tmpVar}}
{{end}}
wantVal = false
}
{{if eq .TakeAddr true}}
{{.Name}} = &tval
{{end}}
}
}
`
type handleArray struct {
IC *Inception
Name string
Typ reflect.Type
Ptr reflect.Kind
UseReflectToSet bool
IsPtr bool
}
var handleArrayTxt = `
{
{{$ic := .IC}}
{{getAllowTokens .Typ.Name "FFTok_left_brace" "FFTok_null"}}
{{if eq .Typ.Elem.Kind .Ptr}}
{{.Name}} = [{{.Typ.Len}}]*{{getType $ic .Name .Typ.Elem.Elem}}{}
{{else}}
{{.Name}} = [{{.Typ.Len}}]{{getType $ic .Name .Typ.Elem}}{}
{{end}}
if tok != fflib.FFTok_null {
wantVal := true
idx := 0
for {
{{$ptr := false}}
{{$tmpVar := getTmpVarFor .Name}}
{{if eq .Typ.Elem.Kind .Ptr }}
{{$ptr := true}}
var {{$tmpVar}} *{{getType $ic .Name .Typ.Elem.Elem}}
{{else}}
var {{$tmpVar}} {{getType $ic .Name .Typ.Elem}}
{{end}}
tok = fs.Scan()
if tok == fflib.FFTok_error {
goto tokerror
}
if tok == fflib.FFTok_right_brace {
break
}
if tok == fflib.FFTok_comma {
if wantVal == true {
// TODO(pquerna): this isn't an ideal error message, this handles
// things like [,,,] as an array value.
return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok))
}
continue
} else {
wantVal = true
}
{{handleField .IC $tmpVar .Typ.Elem $ptr false}}
// Standard json.Unmarshal ignores elements out of array bounds,
// that what we do as well.
if idx < {{.Typ.Len}} {
{{.Name}}[idx] = {{$tmpVar}}
idx++
}
wantVal = false
}
}
}
`
var handleSliceTxt = `
{
{{$ic := .IC}}
{{getAllowTokens .Typ.Name "FFTok_left_brace" "FFTok_null"}}
if tok == fflib.FFTok_null {
{{.Name}} = nil
} else {
{{if eq .Typ.Elem.Kind .Ptr }}
{{if eq .IsPtr true}}
{{.Name}} = &[]*{{getType $ic .Name .Typ.Elem.Elem}}{}
{{else}}
{{.Name}} = []*{{getType $ic .Name .Typ.Elem.Elem}}{}
{{end}}
{{else}}
{{if eq .IsPtr true}}
{{.Name}} = &[]{{getType $ic .Name .Typ.Elem}}{}
{{else}}
{{.Name}} = []{{getType $ic .Name .Typ.Elem}}{}
{{end}}
{{end}}
wantVal := true
for {
{{$ptr := false}}
{{$tmpVar := getTmpVarFor .Name}}
{{if eq .Typ.Elem.Kind .Ptr }}
{{$ptr := true}}
var {{$tmpVar}} *{{getType $ic .Name .Typ.Elem.Elem}}
{{else}}
var {{$tmpVar}} {{getType $ic .Name .Typ.Elem}}
{{end}}
tok = fs.Scan()
if tok == fflib.FFTok_error {
goto tokerror
}
if tok == fflib.FFTok_right_brace {
break
}
if tok == fflib.FFTok_comma {
if wantVal == true {
// TODO(pquerna): this isn't an ideal error message, this handles
// things like [,,,] as an array value.
return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok))
}
continue
} else {
wantVal = true
}
{{handleField .IC $tmpVar .Typ.Elem $ptr false}}
{{if eq .IsPtr true}}
*{{.Name}} = append(*{{.Name}}, {{$tmpVar}})
{{else}}
{{.Name}} = append({{.Name}}, {{$tmpVar}})
{{end}}
wantVal = false
}
}
}
`
var handleByteSliceTxt = `
{
{{getAllowTokens .Typ.Name "FFTok_string" "FFTok_null"}}
if tok == fflib.FFTok_null {
{{.Name}} = nil
} else {
b := make([]byte, base64.StdEncoding.DecodedLen(fs.Output.Len()))
n, err := base64.StdEncoding.Decode(b, fs.Output.Bytes())
if err != nil {
return fs.WrapErr(err)
}
{{if eq .UseReflectToSet true}}
v := reflect.ValueOf(&{{.Name}}).Elem()
v.SetBytes(b[0:n])
{{else}}
{{.Name}} = append([]byte(), b[0:n]...)
{{end}}
}
}
`
type handleBool struct {
Name string
Typ reflect.Type
TakeAddr bool
}
var handleBoolTxt = `
{
if tok == fflib.FFTok_null {
{{if eq .TakeAddr true}}
{{.Name}} = nil
{{end}}
} else {
tmpb := fs.Output.Bytes()
{{if eq .TakeAddr true}}
var tval bool
{{end}}
if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 {
{{if eq .TakeAddr true}}
tval = true
{{else}}
{{.Name}} = true
{{end}}
} else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 {
{{if eq .TakeAddr true}}
tval = false
{{else}}
{{.Name}} = false
{{end}}
} else {
err = errors.New("unexpected bytes for true/false value")
return fs.WrapErr(err)
}
{{if eq .TakeAddr true}}
{{.Name}} = &tval
{{end}}
}
}
`
type handlePtr struct {
IC *Inception
Name string
Typ reflect.Type
Quoted bool
}
var handlePtrTxt = `
{
{{$ic := .IC}}
if tok == fflib.FFTok_null {
{{.Name}} = nil
} else {
if {{.Name}} == nil {
{{.Name}} = new({{getType $ic .Typ.Elem.Name .Typ.Elem}})
}
{{handleFieldAddr .IC .Name true .Typ.Elem false .Quoted}}
}
}
`
type header struct {
IC *Inception
SI *StructInfo
}
var headerTxt = `
const (
ffjt{{.SI.Name}}base = iota
ffjt{{.SI.Name}}nosuchkey
{{with $si := .SI}}
{{range $index, $field := $si.Fields}}
{{if ne $field.JsonName "-"}}
ffjt{{$si.Name}}{{$field.Name}}
{{end}}
{{end}}
{{end}}
)
{{with $si := .SI}}
{{range $index, $field := $si.Fields}}
{{if ne $field.JsonName "-"}}
var ffjKey{{$si.Name}}{{$field.Name}} = []byte({{$field.JsonName}})
{{end}}
{{end}}
{{end}}
`
type ujFunc struct {
IC *Inception
SI *StructInfo
ValidValues []string
ResetFields bool
}
var ujFuncTxt = `
{{$si := .SI}}
{{$ic := .IC}}
// UnmarshalJSON umarshall json - template of ffjson
func (j *{{.SI.Name}}) UnmarshalJSON(input []byte) error {
fs := fflib.NewFFLexer(input)
return j.UnmarshalJSONFFLexer(fs, fflib.FFParse_map_start)
}
// UnmarshalJSONFFLexer fast json unmarshall - template ffjson
func (j *{{.SI.Name}}) UnmarshalJSONFFLexer(fs *fflib.FFLexer, state fflib.FFParseState) error {
var err error
currentKey := ffjt{{.SI.Name}}base
_ = currentKey
tok := fflib.FFTok_init
wantedTok := fflib.FFTok_init
{{if eq .ResetFields true}}
{{range $index, $field := $si.Fields}}
var ffjSet{{$si.Name}}{{$field.Name}} = false
{{end}}
{{end}}
mainparse:
for {
tok = fs.Scan()
// println(fmt.Sprintf("debug: tok: %v state: %v", tok, state))
if tok == fflib.FFTok_error {
goto tokerror
}
switch state {
case fflib.FFParse_map_start:
if tok != fflib.FFTok_left_bracket {
wantedTok = fflib.FFTok_left_bracket
goto wrongtokenerror
}
state = fflib.FFParse_want_key
continue
case fflib.FFParse_after_value:
if tok == fflib.FFTok_comma {
state = fflib.FFParse_want_key
} else if tok == fflib.FFTok_right_bracket {
goto done
} else {
wantedTok = fflib.FFTok_comma
goto wrongtokenerror
}
case fflib.FFParse_want_key:
// json {} ended. goto exit. woo.
if tok == fflib.FFTok_right_bracket {
goto done
}
if tok != fflib.FFTok_string {
wantedTok = fflib.FFTok_string
goto wrongtokenerror
}
kn := fs.Output.Bytes()
if len(kn) <= 0 {
// "" case. hrm.
currentKey = ffjt{{.SI.Name}}nosuchkey
state = fflib.FFParse_want_colon
goto mainparse
} else {
switch kn[0] {
{{range $byte, $fields := $si.FieldsByFirstByte}}
case '{{$byte}}':
{{range $index, $field := $fields}}
{{if ne $index 0 }}} else if {{else}}if {{end}} bytes.Equal(ffjKey{{$si.Name}}{{$field.Name}}, kn) {
currentKey = ffjt{{$si.Name}}{{$field.Name}}
state = fflib.FFParse_want_colon
goto mainparse
{{end}} }
{{end}}
}
{{range $index, $field := $si.ReverseFields}}
if {{$field.FoldFuncName}}(ffjKey{{$si.Name}}{{$field.Name}}, kn) {
currentKey = ffjt{{$si.Name}}{{$field.Name}}
state = fflib.FFParse_want_colon
goto mainparse
}
{{end}}
currentKey = ffjt{{.SI.Name}}nosuchkey
state = fflib.FFParse_want_colon
goto mainparse
}
case fflib.FFParse_want_colon:
if tok != fflib.FFTok_colon {
wantedTok = fflib.FFTok_colon
goto wrongtokenerror
}
state = fflib.FFParse_want_value
continue
case fflib.FFParse_want_value:
if {{range $index, $v := .ValidValues}}{{if ne $index 0 }}||{{end}}tok == fflib.{{$v}}{{end}} {
switch currentKey {
{{range $index, $field := $si.Fields}}
case ffjt{{$si.Name}}{{$field.Name}}:
goto handle_{{$field.Name}}
{{end}}
case ffjt{{$si.Name}}nosuchkey:
err = fs.SkipField(tok)
if err != nil {
return fs.WrapErr(err)
}
state = fflib.FFParse_after_value
goto mainparse
}
} else {
goto wantedvalue
}
}
}
{{range $index, $field := $si.Fields}}
handle_{{$field.Name}}:
{{with $fieldName := $field.Name | printf "j.%s"}}
{{handleField $ic $fieldName $field.Typ $field.Pointer $field.ForceString}}
{{if eq $.ResetFields true}}
ffjSet{{$si.Name}}{{$field.Name}} = true
{{end}}
state = fflib.FFParse_after_value
goto mainparse
{{end}}
{{end}}
wantedvalue:
return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok))
wrongtokenerror:
return fs.WrapErr(fmt.Errorf("ffjson: wanted token: %v, but got token: %v output=%s", wantedTok, tok, fs.Output.String()))
tokerror:
if fs.BigError != nil {
return fs.WrapErr(fs.BigError)
}
err = fs.Error.ToError()
if err != nil {
return fs.WrapErr(err)
}
panic("ffjson-generated: unreachable, please report bug.")
done:
{{if eq .ResetFields true}}
{{range $index, $field := $si.Fields}}
if !ffjSet{{$si.Name}}{{$field.Name}} {
{{with $fieldName := $field.Name | printf "j.%s"}}
{{if eq $field.Pointer true}}
{{$fieldName}} = nil
{{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Interface), 10) + `}}
{{$fieldName}} = nil
{{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Slice), 10) + `}}
{{$fieldName}} = nil
{{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Array), 10) + `}}
{{$fieldName}} = [{{$field.Typ.Len}}]{{getType $ic $fieldName $field.Typ.Elem}}{}
{{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Map), 10) + `}}
{{$fieldName}} = nil
{{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Bool), 10) + `}}
{{$fieldName}} = false
{{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.String), 10) + `}}
{{$fieldName}} = ""
{{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Struct), 10) + `}}
{{$fieldName}} = {{getType $ic $fieldName $field.Typ}}{}
{{else}}
{{$fieldName}} = {{getType $ic $fieldName $field.Typ}}(0)
{{end}}
{{end}}
}
{{end}}
{{end}}
return nil
}
`
type handleUnmarshaler struct {
IC *Inception
Name string
Typ reflect.Type
Ptr reflect.Kind
TakeAddr bool
UnmarshalJSONFFLexer bool
Unmarshaler bool
}
var handleUnmarshalerTxt = `
{{$ic := .IC}}
{{if eq .UnmarshalJSONFFLexer true}}
{
if tok == fflib.FFTok_null {
{{if eq .Typ.Kind .Ptr }}
{{.Name}} = nil
{{end}}
{{if eq .TakeAddr true }}
{{.Name}} = nil
{{end}}
} else {
{{if eq .Typ.Kind .Ptr }}
if {{.Name}} == nil {
{{.Name}} = new({{getType $ic .Typ.Elem.Name .Typ.Elem}})
}
{{end}}
{{if eq .TakeAddr true }}
if {{.Name}} == nil {
{{.Name}} = new({{getType $ic .Typ.Name .Typ}})
}
{{end}}
err = {{.Name}}.UnmarshalJSONFFLexer(fs, fflib.FFParse_want_key)
if err != nil {
return err
}
}
state = fflib.FFParse_after_value
}
{{else}}
{{if eq .Unmarshaler true}}
{
if tok == fflib.FFTok_null {
{{if eq .TakeAddr true }}
{{.Name}} = nil
{{end}}
} else {
tbuf, err := fs.CaptureField(tok)
if err != nil {
return fs.WrapErr(err)
}
{{if eq .TakeAddr true }}
if {{.Name}} == nil {
{{.Name}} = new({{getType $ic .Typ.Name .Typ}})
}
{{end}}
err = {{.Name}}.UnmarshalJSON(tbuf)
if err != nil {
return fs.WrapErr(err)
}
}
state = fflib.FFParse_after_value
}
{{end}}
{{end}}
`