/
preprocessor.go
102 lines (93 loc) · 3.45 KB
/
preprocessor.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you 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 generator
import (
"fmt"
"io"
"strings"
"github.com/pkg/errors"
)
// generateParseFlatFieldCode generates code that parses the JSON tags on a flat field,
// queries the nested field to extract the appropriate nested value which refers to the
// flat field, and finally sets the flat field value to the nested value. The code works
// under the assumption that an object is either completely flat or completely nested.
func generateParseFlatFieldCode(w io.Writer, flat structField, nested structField) error {
nestedTag, _ := nested.tag.Lookup("json")
flatTag, _ := flat.tag.Lookup("json")
if !validateJSONTag(flatTag, nestedTag) {
// invalid tags imply that flat field cannot be deduced from the nested source
return nil
}
flatFieldType, err := getFlatFieldType(flat)
if err != nil {
return errors.Wrapf(err, "failed to get type for %s", flat.Name())
}
nestedSrc := nested.Name()
nestedKey := string(flatTag[len(nestedTag)+1:]) // remove the nested tag prefix
nestedKeyParts := strings.Split(nestedKey, ".")
fmt.Fprintf(w, `if tmpVal, ok := val.%s["%s"]`, nestedSrc, nestedKeyParts[0])
for i := 1; i < len(nestedKeyParts); i++ {
fmt.Fprintf(w, `.(map[string]interface{}); ok {
if tmpVal, ok := tmpVal["%s"]`, nestedKeyParts[i])
}
fmt.Fprintf(w, `.(%s); ok {`, flatFieldType)
extraCode, finalVar := generateValueConverterCode(flatFieldType, "tmpVal")
fmt.Fprintf(w, `
%sval.%s.Set(%s)
`, extraCode, flat.Name(), finalVar)
// Close all braces
for i := 0; i < len(nestedKeyParts); i++ {
fmt.Fprintln(w, "}")
}
return nil
}
// validateJSONTag checks if the flat field can be extracted from the nested source
// by validating the JSON tag.
func validateJSONTag(flatTag, nestedTag string) bool {
return flatTag != "" && nestedTag != "" && strings.HasPrefix(flatTag, nestedTag)
}
func getFlatFieldType(f structField) (string, error) {
switch typ := f.Type().String(); typ {
case nullableTypeInt, nullableTypeInt64:
return "json.Number", nil
case nullableTypeString:
return "string", nil
case nullableTypeBool:
return "bool", nil
default:
return "", fmt.Errorf("not implemented for type: %s", typ)
}
}
// generateValueConverterCode takes the field type of the final
// parsed value in the map and returns extra code required to
// convert the value for the flat field. It also returns a new
// variable string as the second argument if the processing requires
// to use a new variable.
func generateValueConverterCode(typ, origVar string) (string, string) {
switch typ {
case "json.Number":
return fmt.Sprintf(`
%[1]s, err := %[1]s.Int64()
if err != nil {
return err
}
newVal := int(%[1]s)
`[1:], origVar), "newVal"
default:
return "", origVar
}
}