vendor ffjson deps required during generation

ffjson requires some packages during intermediate go generation steps
that are not present in the `./vendor` directory since the final files
do not require it.  To trick go modules into believing that we need
those dependencies, add a `ffjson_deps.go` file referencing them.

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
Valentin Rothberg 2019-08-05 10:43:25 +02:00
parent d85da313ef
commit fada7dd6e2
12 changed files with 2430 additions and 0 deletions

10
ffjson_deps.go Normal file
View File

@ -0,0 +1,10 @@
package storage
// NOTE: this is a hack to trick go modules into vendoring the below
// dependencies. Those are required during ffjson generation
// but do NOT end up in the final file.
import (
_ "github.com/pquerna/ffjson/inception" // nolint:typecheck
_ "github.com/pquerna/ffjson/shared" // nolint:typecheck
)

323
vendor/github.com/pquerna/ffjson/inception/decoder.go generated vendored Normal file
View File

@ -0,0 +1,323 @@
/**
* 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 (
"fmt"
"reflect"
"strings"
"github.com/pquerna/ffjson/shared"
)
var validValues []string = []string{
"FFTok_left_brace",
"FFTok_left_bracket",
"FFTok_integer",
"FFTok_double",
"FFTok_string",
"FFTok_bool",
"FFTok_null",
}
func CreateUnmarshalJSON(ic *Inception, si *StructInfo) error {
out := ""
ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true
if len(si.Fields) > 0 {
ic.OutputImports[`"bytes"`] = true
}
ic.OutputImports[`"fmt"`] = true
out += tplStr(decodeTpl["header"], header{
IC: ic,
SI: si,
})
out += tplStr(decodeTpl["ujFunc"], ujFunc{
SI: si,
IC: ic,
ValidValues: validValues,
ResetFields: ic.ResetFields,
})
ic.OutputFuncs = append(ic.OutputFuncs, out)
return nil
}
func handleField(ic *Inception, name string, typ reflect.Type, ptr bool, quoted bool) string {
return handleFieldAddr(ic, name, false, typ, ptr, quoted)
}
func handleFieldAddr(ic *Inception, name string, takeAddr bool, typ reflect.Type, ptr bool, quoted bool) string {
out := fmt.Sprintf("/* handler: %s type=%v kind=%v quoted=%t*/\n", name, typ, typ.Kind(), quoted)
umlx := typ.Implements(unmarshalFasterType) || typeInInception(ic, typ, shared.MustDecoder)
umlx = umlx || reflect.PtrTo(typ).Implements(unmarshalFasterType)
umlstd := typ.Implements(unmarshalerType) || reflect.PtrTo(typ).Implements(unmarshalerType)
out += tplStr(decodeTpl["handleUnmarshaler"], handleUnmarshaler{
IC: ic,
Name: name,
Typ: typ,
Ptr: reflect.Ptr,
TakeAddr: takeAddr || ptr,
UnmarshalJSONFFLexer: umlx,
Unmarshaler: umlstd,
})
if umlx || umlstd {
return out
}
// TODO(pquerna): generic handling of token type mismatching struct type
switch typ.Kind() {
case reflect.Int,
reflect.Int8,
reflect.Int16,
reflect.Int32,
reflect.Int64:
allowed := buildTokens(quoted, "FFTok_string", "FFTok_integer", "FFTok_null")
out += getAllowTokens(typ.Name(), allowed...)
out += getNumberHandler(ic, name, takeAddr || ptr, typ, "ParseInt")
case reflect.Uint,
reflect.Uint8,
reflect.Uint16,
reflect.Uint32,
reflect.Uint64:
allowed := buildTokens(quoted, "FFTok_string", "FFTok_integer", "FFTok_null")
out += getAllowTokens(typ.Name(), allowed...)
out += getNumberHandler(ic, name, takeAddr || ptr, typ, "ParseUint")
case reflect.Float32,
reflect.Float64:
allowed := buildTokens(quoted, "FFTok_string", "FFTok_double", "FFTok_integer", "FFTok_null")
out += getAllowTokens(typ.Name(), allowed...)
out += getNumberHandler(ic, name, takeAddr || ptr, typ, "ParseFloat")
case reflect.Bool:
ic.OutputImports[`"bytes"`] = true
ic.OutputImports[`"errors"`] = true
allowed := buildTokens(quoted, "FFTok_string", "FFTok_bool", "FFTok_null")
out += getAllowTokens(typ.Name(), allowed...)
out += tplStr(decodeTpl["handleBool"], handleBool{
Name: name,
Typ: typ,
TakeAddr: takeAddr || ptr,
})
case reflect.Ptr:
out += tplStr(decodeTpl["handlePtr"], handlePtr{
IC: ic,
Name: name,
Typ: typ,
Quoted: quoted,
})
case reflect.Array,
reflect.Slice:
out += getArrayHandler(ic, name, typ, ptr)
case reflect.String:
// Is it a json.Number?
if typ.PkgPath() == "encoding/json" && typ.Name() == "Number" {
// Fall back to json package to rely on the valid number check.
// See: https://github.com/golang/go/blob/f05c3aa24d815cd3869153750c9875e35fc48a6e/src/encoding/json/decode.go#L897
ic.OutputImports[`"encoding/json"`] = true
out += tplStr(decodeTpl["handleFallback"], handleFallback{
Name: name,
Typ: typ,
Kind: typ.Kind(),
})
} else {
out += tplStr(decodeTpl["handleString"], handleString{
IC: ic,
Name: name,
Typ: typ,
TakeAddr: takeAddr || ptr,
Quoted: quoted,
})
}
case reflect.Interface:
ic.OutputImports[`"encoding/json"`] = true
out += tplStr(decodeTpl["handleFallback"], handleFallback{
Name: name,
Typ: typ,
Kind: typ.Kind(),
})
case reflect.Map:
out += tplStr(decodeTpl["handleObject"], handleObject{
IC: ic,
Name: name,
Typ: typ,
Ptr: reflect.Ptr,
TakeAddr: takeAddr || ptr,
})
default:
ic.OutputImports[`"encoding/json"`] = true
out += tplStr(decodeTpl["handleFallback"], handleFallback{
Name: name,
Typ: typ,
Kind: typ.Kind(),
})
}
return out
}
func getArrayHandler(ic *Inception, name string, typ reflect.Type, ptr bool) string {
if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
ic.OutputImports[`"encoding/base64"`] = true
useReflectToSet := false
if typ.Elem().Name() != "byte" {
ic.OutputImports[`"reflect"`] = true
useReflectToSet = true
}
return tplStr(decodeTpl["handleByteSlice"], handleArray{
IC: ic,
Name: name,
Typ: typ,
Ptr: reflect.Ptr,
UseReflectToSet: useReflectToSet,
})
}
if typ.Elem().Kind() == reflect.Struct && typ.Elem().Name() != "" {
goto sliceOrArray
}
if (typ.Elem().Kind() == reflect.Struct || typ.Elem().Kind() == reflect.Map) ||
typ.Elem().Kind() == reflect.Array || typ.Elem().Kind() == reflect.Slice &&
typ.Elem().Name() == "" {
ic.OutputImports[`"encoding/json"`] = true
return tplStr(decodeTpl["handleFallback"], handleFallback{
Name: name,
Typ: typ,
Kind: typ.Kind(),
})
}
sliceOrArray:
if typ.Kind() == reflect.Array {
return tplStr(decodeTpl["handleArray"], handleArray{
IC: ic,
Name: name,
Typ: typ,
IsPtr: ptr,
Ptr: reflect.Ptr,
})
}
return tplStr(decodeTpl["handleSlice"], handleArray{
IC: ic,
Name: name,
Typ: typ,
IsPtr: ptr,
Ptr: reflect.Ptr,
})
}
func getAllowTokens(name string, tokens ...string) string {
return tplStr(decodeTpl["allowTokens"], allowTokens{
Name: name,
Tokens: tokens,
})
}
func getNumberHandler(ic *Inception, name string, takeAddr bool, typ reflect.Type, parsefunc string) string {
return tplStr(decodeTpl["handlerNumeric"], handlerNumeric{
IC: ic,
Name: name,
ParseFunc: parsefunc,
TakeAddr: takeAddr,
Typ: typ,
})
}
func getNumberSize(typ reflect.Type) string {
return fmt.Sprintf("%d", typ.Bits())
}
func getType(ic *Inception, name string, typ reflect.Type) string {
s := typ.Name()
if typ.PkgPath() != "" && typ.PkgPath() != ic.PackagePath {
path := removeVendor(typ.PkgPath())
ic.OutputImports[`"`+path+`"`] = true
s = typ.String()
}
if s == "" {
return typ.String()
}
return s
}
// removeVendor removes everything before and including a '/vendor/'
// substring in the package path.
// This is needed becuase that full path can't be used in the
// import statement.
func removeVendor(path string) string {
i := strings.Index(path, "/vendor/")
if i == -1 {
return path
}
return path[i+8:]
}
func buildTokens(containsOptional bool, optional string, required ...string) []string {
if containsOptional {
return append(required, optional)
}
return required
}
func unquoteField(quoted bool) string {
// The outer quote of a string is already stripped out by
// the lexer. We need to check if the inner string is also
// quoted. If so, we will decode it as json string. If decoding
// fails, we will use the original string
if quoted {
return `
unquoted, ok := fflib.UnquoteBytes(outBuf)
if ok {
outBuf = unquoted
}
`
}
return ""
}
func getTmpVarFor(name string) string {
return "tmp" + strings.Replace(strings.Title(name), ".", "", -1)
}

View File

@ -0,0 +1,773 @@
/**
* 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}}
`

544
vendor/github.com/pquerna/ffjson/inception/encoder.go generated vendored Normal file
View File

@ -0,0 +1,544 @@
/**
* 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 (
"fmt"
"reflect"
"github.com/pquerna/ffjson/shared"
)
func typeInInception(ic *Inception, typ reflect.Type, f shared.Feature) bool {
for _, v := range ic.objs {
if v.Typ == typ {
return v.Options.HasFeature(f)
}
if typ.Kind() == reflect.Ptr {
if v.Typ == typ.Elem() {
return v.Options.HasFeature(f)
}
}
}
return false
}
func getOmitEmpty(ic *Inception, sf *StructField) string {
ptname := "j." + sf.Name
if sf.Pointer {
ptname = "*" + ptname
return "if true {\n"
}
switch sf.Typ.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return "if len(" + ptname + ") != 0 {" + "\n"
case reflect.Int,
reflect.Int8,
reflect.Int16,
reflect.Int32,
reflect.Int64,
reflect.Uint,
reflect.Uint8,
reflect.Uint16,
reflect.Uint32,
reflect.Uint64,
reflect.Uintptr,
reflect.Float32,
reflect.Float64:
return "if " + ptname + " != 0 {" + "\n"
case reflect.Bool:
return "if " + ptname + " != false {" + "\n"
case reflect.Interface, reflect.Ptr:
return "if " + ptname + " != nil {" + "\n"
default:
// TODO(pquerna): fix types
return "if true {" + "\n"
}
}
func getMapValue(ic *Inception, name string, typ reflect.Type, ptr bool, forceString bool) string {
var out = ""
if typ.Key().Kind() != reflect.String {
out += fmt.Sprintf("/* Falling back. type=%v kind=%v */\n", typ, typ.Kind())
out += ic.q.Flush()
out += "err = buf.Encode(" + name + ")" + "\n"
out += "if err != nil {" + "\n"
out += " return err" + "\n"
out += "}" + "\n"
return out
}
var elemKind reflect.Kind
elemKind = typ.Elem().Kind()
switch elemKind {
case reflect.String,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
reflect.Float32,
reflect.Float64,
reflect.Bool:
ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true
out += "if " + name + " == nil {" + "\n"
ic.q.Write("null")
out += ic.q.GetQueued()
ic.q.DeleteLast()
out += "} else {" + "\n"
out += ic.q.WriteFlush("{ ")
out += " for key, value := range " + name + " {" + "\n"
out += " fflib.WriteJsonString(buf, key)" + "\n"
out += " buf.WriteString(`:`)" + "\n"
out += getGetInnerValue(ic, "value", typ.Elem(), false, forceString)
out += " buf.WriteByte(',')" + "\n"
out += " }" + "\n"
out += "buf.Rewind(1)" + "\n"
out += ic.q.WriteFlush("}")
out += "}" + "\n"
default:
out += ic.q.Flush()
out += fmt.Sprintf("/* Falling back. type=%v kind=%v */\n", typ, typ.Kind())
out += "err = buf.Encode(" + name + ")" + "\n"
out += "if err != nil {" + "\n"
out += " return err" + "\n"
out += "}" + "\n"
}
return out
}
func getGetInnerValue(ic *Inception, name string, typ reflect.Type, ptr bool, forceString bool) string {
var out = ""
// Flush if not bool or maps
if typ.Kind() != reflect.Bool && typ.Kind() != reflect.Map && typ.Kind() != reflect.Struct {
out += ic.q.Flush()
}
if typ.Implements(marshalerFasterType) ||
reflect.PtrTo(typ).Implements(marshalerFasterType) ||
typeInInception(ic, typ, shared.MustEncoder) ||
typ.Implements(marshalerType) ||
reflect.PtrTo(typ).Implements(marshalerType) {
out += ic.q.Flush()
out += tplStr(encodeTpl["handleMarshaler"], handleMarshaler{
IC: ic,
Name: name,
Typ: typ,
Ptr: reflect.Ptr,
MarshalJSONBuf: typ.Implements(marshalerFasterType) || reflect.PtrTo(typ).Implements(marshalerFasterType) || typeInInception(ic, typ, shared.MustEncoder),
Marshaler: typ.Implements(marshalerType) || reflect.PtrTo(typ).Implements(marshalerType),
})
return out
}
ptname := name
if ptr {
ptname = "*" + name
}
switch typ.Kind() {
case reflect.Int,
reflect.Int8,
reflect.Int16,
reflect.Int32,
reflect.Int64:
ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true
out += "fflib.FormatBits2(buf, uint64(" + ptname + "), 10, " + ptname + " < 0)" + "\n"
case reflect.Uint,
reflect.Uint8,
reflect.Uint16,
reflect.Uint32,
reflect.Uint64,
reflect.Uintptr:
ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true
out += "fflib.FormatBits2(buf, uint64(" + ptname + "), 10, false)" + "\n"
case reflect.Float32:
ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true
out += "fflib.AppendFloat(buf, float64(" + ptname + "), 'g', -1, 32)" + "\n"
case reflect.Float64:
ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true
out += "fflib.AppendFloat(buf, float64(" + ptname + "), 'g', -1, 64)" + "\n"
case reflect.Array,
reflect.Slice:
// Arrays cannot be nil
if typ.Kind() != reflect.Array {
out += "if " + name + "!= nil {" + "\n"
}
// Array and slice values encode as JSON arrays, except that
// []byte encodes as a base64-encoded string, and a nil slice
// encodes as the null JSON object.
if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
ic.OutputImports[`"encoding/base64"`] = true
out += "buf.WriteString(`\"`)" + "\n"
out += `{` + "\n"
out += `enc := base64.NewEncoder(base64.StdEncoding, buf)` + "\n"
if typ.Elem().Name() != "byte" {
ic.OutputImports[`"reflect"`] = true
out += `enc.Write(reflect.Indirect(reflect.ValueOf(` + ptname + `)).Bytes())` + "\n"
} else {
out += `enc.Write(` + ptname + `)` + "\n"
}
out += `enc.Close()` + "\n"
out += `}` + "\n"
out += "buf.WriteString(`\"`)" + "\n"
} else {
out += "buf.WriteString(`[`)" + "\n"
out += "for i, v := range " + ptname + "{" + "\n"
out += "if i != 0 {" + "\n"
out += "buf.WriteString(`,`)" + "\n"
out += "}" + "\n"
out += getGetInnerValue(ic, "v", typ.Elem(), false, false)
out += "}" + "\n"
out += "buf.WriteString(`]`)" + "\n"
}
if typ.Kind() != reflect.Array {
out += "} else {" + "\n"
out += "buf.WriteString(`null`)" + "\n"
out += "}" + "\n"
}
case reflect.String:
// Is it a json.Number?
if typ.PkgPath() == "encoding/json" && typ.Name() == "Number" {
// Fall back to json package to rely on the valid number check.
// See: https://github.com/golang/go/blob/92cd6e3af9f423ab4d8ac78f24e7fd81c31a8ce6/src/encoding/json/encode.go#L550
out += fmt.Sprintf("/* json.Number */\n")
out += "err = buf.Encode(" + name + ")" + "\n"
out += "if err != nil {" + "\n"
out += " return err" + "\n"
out += "}" + "\n"
} else {
ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true
if forceString {
// Forcestring on strings does double-escaping of the entire value.
// We create a temporary buffer, encode to that an re-encode it.
out += "{" + "\n"
out += "tmpbuf := fflib.Buffer{}" + "\n"
out += "tmpbuf.Grow(len(" + ptname + ") + 16)" + "\n"
out += "fflib.WriteJsonString(&tmpbuf, string(" + ptname + "))" + "\n"
out += "fflib.WriteJsonString(buf, string( tmpbuf.Bytes() " + `))` + "\n"
out += "}" + "\n"
} else {
out += "fflib.WriteJsonString(buf, string(" + ptname + "))" + "\n"
}
}
case reflect.Ptr:
out += "if " + name + "!= nil {" + "\n"
switch typ.Elem().Kind() {
case reflect.Struct:
out += getGetInnerValue(ic, name, typ.Elem(), false, false)
default:
out += getGetInnerValue(ic, "*"+name, typ.Elem(), false, false)
}
out += "} else {" + "\n"
out += "buf.WriteString(`null`)" + "\n"
out += "}" + "\n"
case reflect.Bool:
out += "if " + ptname + " {" + "\n"
ic.q.Write("true")
out += ic.q.GetQueued()
out += "} else {" + "\n"
// Delete 'true'
ic.q.DeleteLast()
out += ic.q.WriteFlush("false")
out += "}" + "\n"
case reflect.Interface:
out += fmt.Sprintf("/* Interface types must use runtime reflection. type=%v kind=%v */\n", typ, typ.Kind())
out += "err = buf.Encode(" + name + ")" + "\n"
out += "if err != nil {" + "\n"
out += " return err" + "\n"
out += "}" + "\n"
case reflect.Map:
out += getMapValue(ic, ptname, typ, ptr, forceString)
case reflect.Struct:
if typ.Name() == "" {
ic.q.Write("{")
ic.q.Write(" ")
out += fmt.Sprintf("/* Inline struct. type=%v kind=%v */\n", typ, typ.Kind())
newV := reflect.Indirect(reflect.New(typ)).Interface()
fields := extractFields(newV)
// Output all fields
for _, field := range fields {
// Adjust field name
field.Name = name + "." + field.Name
out += getField(ic, field, "")
}
if lastConditional(fields) {
out += ic.q.Flush()
out += `buf.Rewind(1)` + "\n"
} else {
ic.q.DeleteLast()
}
out += ic.q.WriteFlush("}")
} else {
out += fmt.Sprintf("/* Struct fall back. type=%v kind=%v */\n", typ, typ.Kind())
out += ic.q.Flush()
if ptr {
out += "err = buf.Encode(" + name + ")" + "\n"
} else {
// We send pointer to avoid copying entire struct
out += "err = buf.Encode(&" + name + ")" + "\n"
}
out += "if err != nil {" + "\n"
out += " return err" + "\n"
out += "}" + "\n"
}
default:
out += fmt.Sprintf("/* Falling back. type=%v kind=%v */\n", typ, typ.Kind())
out += "err = buf.Encode(" + name + ")" + "\n"
out += "if err != nil {" + "\n"
out += " return err" + "\n"
out += "}" + "\n"
}
return out
}
func getValue(ic *Inception, sf *StructField, prefix string) string {
closequote := false
if sf.ForceString {
switch sf.Typ.Kind() {
case reflect.Int,
reflect.Int8,
reflect.Int16,
reflect.Int32,
reflect.Int64,
reflect.Uint,
reflect.Uint8,
reflect.Uint16,
reflect.Uint32,
reflect.Uint64,
reflect.Uintptr,
reflect.Float32,
reflect.Float64,
reflect.Bool:
ic.q.Write(`"`)
closequote = true
}
}
out := getGetInnerValue(ic, prefix+sf.Name, sf.Typ, sf.Pointer, sf.ForceString)
if closequote {
if sf.Pointer {
out += ic.q.WriteFlush(`"`)
} else {
ic.q.Write(`"`)
}
}
return out
}
func p2(v uint32) uint32 {
v--
v |= v >> 1
v |= v >> 2
v |= v >> 4
v |= v >> 8
v |= v >> 16
v++
return v
}
func getTypeSize(t reflect.Type) uint32 {
switch t.Kind() {
case reflect.String:
// TODO: consider runtime analysis.
return 32
case reflect.Array, reflect.Map, reflect.Slice:
// TODO: consider runtime analysis.
return 4 * getTypeSize(t.Elem())
case reflect.Int,
reflect.Int8,
reflect.Int16,
reflect.Int32,
reflect.Uint,
reflect.Uint8,
reflect.Uint16,
reflect.Uint32:
return 8
case reflect.Int64,
reflect.Uint64,
reflect.Uintptr:
return 16
case reflect.Float32,
reflect.Float64:
return 16
case reflect.Bool:
return 4
case reflect.Ptr:
return getTypeSize(t.Elem())
default:
return 16
}
}
func getTotalSize(si *StructInfo) uint32 {
rv := uint32(si.Typ.Size())
for _, f := range si.Fields {
rv += getTypeSize(f.Typ)
}
return rv
}
func getBufGrowSize(si *StructInfo) uint32 {
// TOOD(pquerna): automatically calc a better grow size based on history
// of a struct.
return p2(getTotalSize(si))
}
func isIntish(t reflect.Type) bool {
if t.Kind() >= reflect.Int && t.Kind() <= reflect.Uintptr {
return true
}
if t.Kind() == reflect.Array || t.Kind() == reflect.Slice || t.Kind() == reflect.Ptr {
if t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 {
// base64 special case.
return false
} else {
return isIntish(t.Elem())
}
}
return false
}
func getField(ic *Inception, f *StructField, prefix string) string {
out := ""
if f.OmitEmpty {
out += ic.q.Flush()
if f.Pointer {
out += "if " + prefix + f.Name + " != nil {" + "\n"
}
out += getOmitEmpty(ic, f)
}
if f.Pointer && !f.OmitEmpty {
// Pointer values encode as the value pointed to. A nil pointer encodes as the null JSON object.
out += "if " + prefix + f.Name + " != nil {" + "\n"
}
// JsonName is already escaped and quoted.
// getInnervalue should flush
ic.q.Write(f.JsonName + ":")
// We save a copy in case we need it
t := ic.q
out += getValue(ic, f, prefix)
ic.q.Write(",")
if f.Pointer && !f.OmitEmpty {
out += "} else {" + "\n"
out += t.WriteFlush("null")
out += "}" + "\n"
}
if f.OmitEmpty {
out += ic.q.Flush()
if f.Pointer {
out += "}" + "\n"
}
out += "}" + "\n"
}
return out
}
// We check if the last field is conditional.
func lastConditional(fields []*StructField) bool {
if len(fields) > 0 {
f := fields[len(fields)-1]
return f.OmitEmpty
}
return false
}
func CreateMarshalJSON(ic *Inception, si *StructInfo) error {
conditionalWrites := lastConditional(si.Fields)
out := ""
out += "// MarshalJSON marshal bytes to json - template\n"
out += `func (j *` + si.Name + `) MarshalJSON() ([]byte, error) {` + "\n"
out += `var buf fflib.Buffer` + "\n"
out += `if j == nil {` + "\n"
out += ` buf.WriteString("null")` + "\n"
out += " return buf.Bytes(), nil" + "\n"
out += `}` + "\n"
out += `err := j.MarshalJSONBuf(&buf)` + "\n"
out += `if err != nil {` + "\n"
out += " return nil, err" + "\n"
out += `}` + "\n"
out += `return buf.Bytes(), nil` + "\n"
out += `}` + "\n"
out += "// MarshalJSONBuf marshal buff to json - template\n"
out += `func (j *` + si.Name + `) MarshalJSONBuf(buf fflib.EncodingBuffer) (error) {` + "\n"
out += ` if j == nil {` + "\n"
out += ` buf.WriteString("null")` + "\n"
out += " return nil" + "\n"
out += ` }` + "\n"
out += `var err error` + "\n"
out += `var obj []byte` + "\n"
out += `_ = obj` + "\n"
out += `_ = err` + "\n"
ic.q.Write("{")
// The extra space is inserted here.
// If nothing is written to the field this will be deleted
// instead of the last comma.
if conditionalWrites || len(si.Fields) == 0 {
ic.q.Write(" ")
}
for _, f := range si.Fields {
out += getField(ic, f, "j.")
}
// Handling the last comma is tricky.
// If the last field has omitempty, conditionalWrites is set.
// If something has been written, we delete the last comma,
// by backing up the buffer, otherwise it will delete a space.
if conditionalWrites {
out += ic.q.Flush()
out += `buf.Rewind(1)` + "\n"
} else {
ic.q.DeleteLast()
}
out += ic.q.WriteFlush("}")
out += `return nil` + "\n"
out += `}` + "\n"
ic.OutputFuncs = append(ic.OutputFuncs, out)
return nil
}

View File

@ -0,0 +1,73 @@
/**
* 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"
"text/template"
)
var encodeTpl map[string]*template.Template
func init() {
encodeTpl = make(map[string]*template.Template)
funcs := map[string]string{
"handleMarshaler": handleMarshalerTxt,
}
tplFuncs := template.FuncMap{}
for k, v := range funcs {
encodeTpl[k] = template.Must(template.New(k).Funcs(tplFuncs).Parse(v))
}
}
type handleMarshaler struct {
IC *Inception
Name string
Typ reflect.Type
Ptr reflect.Kind
MarshalJSONBuf bool
Marshaler bool
}
var handleMarshalerTxt = `
{
{{if eq .Typ.Kind .Ptr}}
if {{.Name}} == nil {
buf.WriteString("null")
} else {
{{end}}
{{if eq .MarshalJSONBuf true}}
err = {{.Name}}.MarshalJSONBuf(buf)
if err != nil {
return err
}
{{else if eq .Marshaler true}}
obj, err = {{.Name}}.MarshalJSON()
if err != nil {
return err
}
buf.Write(obj)
{{end}}
{{if eq .Typ.Kind .Ptr}}
}
{{end}}
}
`

160
vendor/github.com/pquerna/ffjson/inception/inception.go generated vendored Normal file
View File

@ -0,0 +1,160 @@
/**
* 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 (
"errors"
"fmt"
"github.com/pquerna/ffjson/shared"
"io/ioutil"
"os"
"reflect"
"sort"
)
type Inception struct {
objs []*StructInfo
InputPath string
OutputPath string
PackageName string
PackagePath string
OutputImports map[string]bool
OutputFuncs []string
q ConditionalWrite
ResetFields bool
}
func NewInception(inputPath string, packageName string, outputPath string, resetFields bool) *Inception {
return &Inception{
objs: make([]*StructInfo, 0),
InputPath: inputPath,
OutputPath: outputPath,
PackageName: packageName,
OutputFuncs: make([]string, 0),
OutputImports: make(map[string]bool),
ResetFields: resetFields,
}
}
func (i *Inception) AddMany(objs []shared.InceptionType) {
for _, obj := range objs {
i.Add(obj)
}
}
func (i *Inception) Add(obj shared.InceptionType) {
i.objs = append(i.objs, NewStructInfo(obj))
i.PackagePath = i.objs[0].Typ.PkgPath()
}
func (i *Inception) wantUnmarshal(si *StructInfo) bool {
if si.Options.SkipDecoder {
return false
}
typ := si.Typ
umlx := typ.Implements(unmarshalFasterType) || reflect.PtrTo(typ).Implements(unmarshalFasterType)
umlstd := typ.Implements(unmarshalerType) || reflect.PtrTo(typ).Implements(unmarshalerType)
if umlstd && !umlx {
// structure has UnmarshalJSON, but not our faster version -- skip it.
return false
}
return true
}
func (i *Inception) wantMarshal(si *StructInfo) bool {
if si.Options.SkipEncoder {
return false
}
typ := si.Typ
mlx := typ.Implements(marshalerFasterType) || reflect.PtrTo(typ).Implements(marshalerFasterType)
mlstd := typ.Implements(marshalerType) || reflect.PtrTo(typ).Implements(marshalerType)
if mlstd && !mlx {
// structure has MarshalJSON, but not our faster version -- skip it.
return false
}
return true
}
type sortedStructs []*StructInfo
func (p sortedStructs) Len() int { return len(p) }
func (p sortedStructs) Less(i, j int) bool { return p[i].Name < p[j].Name }
func (p sortedStructs) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p sortedStructs) Sort() { sort.Sort(p) }
func (i *Inception) generateCode() error {
// We sort the structs by name, so output if predictable.
sorted := sortedStructs(i.objs)
sorted.Sort()
for _, si := range sorted {
if i.wantMarshal(si) {
err := CreateMarshalJSON(i, si)
if err != nil {
return err
}
}
if i.wantUnmarshal(si) {
err := CreateUnmarshalJSON(i, si)
if err != nil {
return err
}
}
}
return nil
}
func (i *Inception) handleError(err error) {
fmt.Fprintf(os.Stderr, "Error: %s:\n\n", err)
os.Exit(1)
}
func (i *Inception) Execute() {
if len(os.Args) != 1 {
i.handleError(errors.New(fmt.Sprintf("Internal ffjson error: inception executable takes no args: %v", os.Args)))
return
}
err := i.generateCode()
if err != nil {
i.handleError(err)
return
}
data, err := RenderTemplate(i)
if err != nil {
i.handleError(err)
return
}
stat, err := os.Stat(i.InputPath)
if err != nil {
i.handleError(err)
return
}
err = ioutil.WriteFile(i.OutputPath, data, stat.Mode())
if err != nil {
i.handleError(err)
return
}
}

290
vendor/github.com/pquerna/ffjson/inception/reflect.go generated vendored Normal file
View File

@ -0,0 +1,290 @@
/**
* 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 (
fflib "github.com/pquerna/ffjson/fflib/v1"
"github.com/pquerna/ffjson/shared"
"bytes"
"encoding/json"
"reflect"
"unicode/utf8"
)
type StructField struct {
Name string
JsonName string
FoldFuncName string
Typ reflect.Type
OmitEmpty bool
ForceString bool
HasMarshalJSON bool
HasUnmarshalJSON bool
Pointer bool
Tagged bool
}
type FieldByJsonName []*StructField
func (a FieldByJsonName) Len() int { return len(a) }
func (a FieldByJsonName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a FieldByJsonName) Less(i, j int) bool { return a[i].JsonName < a[j].JsonName }
type StructInfo struct {
Name string
Obj interface{}
Typ reflect.Type
Fields []*StructField
Options shared.StructOptions
}
func NewStructInfo(obj shared.InceptionType) *StructInfo {
t := reflect.TypeOf(obj.Obj)
return &StructInfo{
Obj: obj.Obj,
Name: t.Name(),
Typ: t,
Fields: extractFields(obj.Obj),
Options: obj.Options,
}
}
func (si *StructInfo) FieldsByFirstByte() map[string][]*StructField {
rv := make(map[string][]*StructField)
for _, f := range si.Fields {
b := string(f.JsonName[1])
rv[b] = append(rv[b], f)
}
return rv
}
func (si *StructInfo) ReverseFields() []*StructField {
var i int
rv := make([]*StructField, 0)
for i = len(si.Fields) - 1; i >= 0; i-- {
rv = append(rv, si.Fields[i])
}
return rv
}
const (
caseMask = ^byte(0x20) // Mask to ignore case in ASCII.
)
func foldFunc(key []byte) string {
nonLetter := false
special := false // special letter
for _, b := range key {
if b >= utf8.RuneSelf {
return "bytes.EqualFold"
}
upper := b & caseMask
if upper < 'A' || upper > 'Z' {
nonLetter = true
} else if upper == 'K' || upper == 'S' {
// See above for why these letters are special.
special = true
}
}
if special {
return "fflib.EqualFoldRight"
}
if nonLetter {
return "fflib.AsciiEqualFold"
}
return "fflib.SimpleLetterEqualFold"
}
type MarshalerFaster interface {
MarshalJSONBuf(buf fflib.EncodingBuffer) error
}
type UnmarshalFaster interface {
UnmarshalJSONFFLexer(l *fflib.FFLexer, state fflib.FFParseState) error
}
var marshalerType = reflect.TypeOf(new(json.Marshaler)).Elem()
var marshalerFasterType = reflect.TypeOf(new(MarshalerFaster)).Elem()
var unmarshalerType = reflect.TypeOf(new(json.Unmarshaler)).Elem()
var unmarshalFasterType = reflect.TypeOf(new(UnmarshalFaster)).Elem()
// extractFields returns a list of fields that JSON should recognize for the given type.
// The algorithm is breadth-first search over the set of structs to include - the top struct
// and then any reachable anonymous structs.
func extractFields(obj interface{}) []*StructField {
t := reflect.TypeOf(obj)
// Anonymous fields to explore at the current level and the next.
current := []StructField{}
next := []StructField{{Typ: t}}
// Count of queued names for current level and the next.
count := map[reflect.Type]int{}
nextCount := map[reflect.Type]int{}
// Types already visited at an earlier level.
visited := map[reflect.Type]bool{}
// Fields found.
var fields []*StructField
for len(next) > 0 {
current, next = next, current[:0]
count, nextCount = nextCount, map[reflect.Type]int{}
for _, f := range current {
if visited[f.Typ] {
continue
}
visited[f.Typ] = true
// Scan f.typ for fields to include.
for i := 0; i < f.Typ.NumField(); i++ {
sf := f.Typ.Field(i)
if sf.PkgPath != "" { // unexported
continue
}
tag := sf.Tag.Get("json")
if tag == "-" {
continue
}
name, opts := parseTag(tag)
if !isValidTag(name) {
name = ""
}
ft := sf.Type
ptr := false
if ft.Kind() == reflect.Ptr {
ptr = true
}
if ft.Name() == "" && ft.Kind() == reflect.Ptr {
// Follow pointer.
ft = ft.Elem()
}
// Record found field and index sequence.
if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
tagged := name != ""
if name == "" {
name = sf.Name
}
var buf bytes.Buffer
fflib.WriteJsonString(&buf, name)
field := &StructField{
Name: sf.Name,
JsonName: string(buf.Bytes()),
FoldFuncName: foldFunc([]byte(name)),
Typ: ft,
HasMarshalJSON: ft.Implements(marshalerType),
HasUnmarshalJSON: ft.Implements(unmarshalerType),
OmitEmpty: opts.Contains("omitempty"),
ForceString: opts.Contains("string"),
Pointer: ptr,
Tagged: tagged,
}
fields = append(fields, field)
if count[f.Typ] > 1 {
// If there were multiple instances, add a second,
// so that the annihilation code will see a duplicate.
// It only cares about the distinction between 1 or 2,
// so don't bother generating any more copies.
fields = append(fields, fields[len(fields)-1])
}
continue
}
// Record new anonymous struct to explore in next round.
nextCount[ft]++
if nextCount[ft] == 1 {
next = append(next, StructField{
Name: ft.Name(),
Typ: ft,
})
}
}
}
}
// Delete all fields that are hidden by the Go rules for embedded fields,
// except that fields with JSON tags are promoted.
// The fields are sorted in primary order of name, secondary order
// of field index length. Loop over names; for each name, delete
// hidden fields by choosing the one dominant field that survives.
out := fields[:0]
for advance, i := 0, 0; i < len(fields); i += advance {
// One iteration per name.
// Find the sequence of fields with the name of this first field.
fi := fields[i]
name := fi.JsonName
for advance = 1; i+advance < len(fields); advance++ {
fj := fields[i+advance]
if fj.JsonName != name {
break
}
}
if advance == 1 { // Only one field with this name
out = append(out, fi)
continue
}
dominant, ok := dominantField(fields[i : i+advance])
if ok {
out = append(out, dominant)
}
}
fields = out
return fields
}
// dominantField looks through the fields, all of which are known to
// have the same name, to find the single field that dominates the
// others using Go's embedding rules, modified by the presence of
// JSON tags. If there are multiple top-level fields, the boolean
// will be false: This condition is an error in Go and we skip all
// the fields.
func dominantField(fields []*StructField) (*StructField, bool) {
tagged := -1 // Index of first tagged field.
for i, f := range fields {
if f.Tagged {
if tagged >= 0 {
// Multiple tagged fields at the same level: conflict.
// Return no field.
return nil, false
}
tagged = i
}
}
if tagged >= 0 {
return fields[tagged], true
}
// All remaining fields have the same length. If there's more than one,
// we have a conflict (two fields named "X" at the same level) and we
// return no field.
if len(fields) > 1 {
return nil, false
}
return fields[0], true
}

79
vendor/github.com/pquerna/ffjson/inception/tags.go generated vendored Normal file
View File

@ -0,0 +1,79 @@
/**
* 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 (
"strings"
"unicode"
)
// from: http://golang.org/src/pkg/encoding/json/tags.go
// tagOptions is the string following a comma in a struct field's "json"
// tag, or the empty string. It does not include the leading comma.
type tagOptions string
// parseTag splits a struct field's json tag into its name and
// comma-separated options.
func parseTag(tag string) (string, tagOptions) {
if idx := strings.Index(tag, ","); idx != -1 {
return tag[:idx], tagOptions(tag[idx+1:])
}
return tag, tagOptions("")
}
// Contains reports whether a comma-separated list of options
// contains a particular substr flag. substr must be surrounded by a
// string boundary or commas.
func (o tagOptions) Contains(optionName string) bool {
if len(o) == 0 {
return false
}
s := string(o)
for s != "" {
var next string
i := strings.Index(s, ",")
if i >= 0 {
s, next = s[:i], s[i+1:]
}
if s == optionName {
return true
}
s = next
}
return false
}
func isValidTag(s string) bool {
if s == "" {
return false
}
for _, c := range s {
switch {
case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
// Backslash and quote chars are reserved, but
// otherwise any punctuation chars are allowed
// in a tag name.
default:
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
return false
}
}
}
return true
}

60
vendor/github.com/pquerna/ffjson/inception/template.go generated vendored Normal file
View File

@ -0,0 +1,60 @@
/**
* 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 (
"bytes"
"go/format"
"text/template"
)
const ffjsonTemplate = `
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
// source: {{.InputPath}}
package {{.PackageName}}
import (
{{range $k, $v := .OutputImports}}{{$k}}
{{end}}
)
{{range .OutputFuncs}}
{{.}}
{{end}}
`
func RenderTemplate(ic *Inception) ([]byte, error) {
t := template.Must(template.New("ffjson.go").Parse(ffjsonTemplate))
buf := new(bytes.Buffer)
err := t.Execute(buf, ic)
if err != nil {
return nil, err
}
return format.Source(buf.Bytes())
}
func tplStr(t *template.Template, data interface{}) string {
buf := bytes.Buffer{}
err := t.Execute(&buf, data)
if err != nil {
panic(err)
}
return buf.String()
}

View File

@ -0,0 +1,65 @@
package ffjsoninception
import "strings"
// ConditionalWrite is a stack containing a number of pending writes
type ConditionalWrite struct {
Queued []string
}
// Write will add a string to be written
func (w *ConditionalWrite) Write(s string) {
w.Queued = append(w.Queued, s)
}
// DeleteLast will delete the last added write
func (w *ConditionalWrite) DeleteLast() {
if len(w.Queued) == 0 {
return
}
w.Queued = w.Queued[:len(w.Queued)-1]
}
// Last will return the last added write
func (w *ConditionalWrite) Last() string {
if len(w.Queued) == 0 {
return ""
}
return w.Queued[len(w.Queued)-1]
}
// Flush will return all queued writes, and return
// "" (empty string) in nothing has been queued
// "buf.WriteByte('" + byte + "')" + '\n' if one bute has been queued.
// "buf.WriteString(`" + string + "`)" + "\n" if more than one byte has been queued.
func (w *ConditionalWrite) Flush() string {
combined := strings.Join(w.Queued, "")
if len(combined) == 0 {
return ""
}
w.Queued = nil
if len(combined) == 1 {
return "buf.WriteByte('" + combined + "')" + "\n"
}
return "buf.WriteString(`" + combined + "`)" + "\n"
}
func (w *ConditionalWrite) FlushTo(out string) string {
out += w.Flush()
return out
}
// WriteFlush will add a string and return the Flush result for the queue
func (w *ConditionalWrite) WriteFlush(s string) string {
w.Write(s)
return w.Flush()
}
// GetQueued will return the current queued content without flushing.
func (w *ConditionalWrite) GetQueued() string {
t := w.Queued
s := w.Flush()
w.Queued = t
return s
}

51
vendor/github.com/pquerna/ffjson/shared/options.go generated vendored Normal file
View File

@ -0,0 +1,51 @@
/**
* Copyright 2014 Paul Querna, Klaus Post
*
* 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 shared
type StructOptions struct {
SkipDecoder bool
SkipEncoder bool
}
type InceptionType struct {
Obj interface{}
Options StructOptions
}
type Feature int
const (
Nothing Feature = 0
MustDecoder = 1 << 1
MustEncoder = 1 << 2
MustEncDec = MustDecoder | MustEncoder
)
func (i InceptionType) HasFeature(f Feature) bool {
return i.HasFeature(f)
}
func (s StructOptions) HasFeature(f Feature) bool {
hasNeeded := true
if f&MustDecoder != 0 && s.SkipDecoder {
hasNeeded = false
}
if f&MustEncoder != 0 && s.SkipEncoder {
hasNeeded = false
}
return hasNeeded
}

2
vendor/modules.txt vendored
View File

@ -66,6 +66,8 @@ github.com/pkg/errors
github.com/pmezard/go-difflib/difflib
# github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7
github.com/pquerna/ffjson/fflib/v1
github.com/pquerna/ffjson/inception
github.com/pquerna/ffjson/shared
github.com/pquerna/ffjson/fflib/v1/internal
# github.com/sirupsen/logrus v1.4.2
github.com/sirupsen/logrus