From 8fec1c86c7bcc54f6453bcdfc9fa3dbd52dab2ab Mon Sep 17 00:00:00 2001 From: Steven Polley Date: Wed, 15 Aug 2018 15:15:19 -0600 Subject: [PATCH] Adding jflect and converted to package for generate-structs --- generate-structs/main.go | 19 +--- generate-structs/vendor/jflect/field.go | 59 +++++++++++ generate-structs/vendor/jflect/main.go | 132 ++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 18 deletions(-) create mode 100644 generate-structs/vendor/jflect/field.go create mode 100644 generate-structs/vendor/jflect/main.go diff --git a/generate-structs/main.go b/generate-structs/main.go index e05e961..d6d28e3 100644 --- a/generate-structs/main.go +++ b/generate-structs/main.go @@ -1,14 +1,12 @@ package main import ( - "bytes" "fmt" "log" "os" "strconv" "deadbeef.codes/steven/go-itg/itglue" - "github.com/ChimeraCoder/gojson" ) var itg *itglue.ITGAPI @@ -40,21 +38,6 @@ func main() { log.Fatalf("could get flexible asset with type ID %d: %s", id, err) } - name := &fat.FlexibleAssetTypeData.Attributes.Name - pkg := "itglue" - subStruct := false // try changing to true? - tagList := make([]string, 0) - tagList = append(tagList, "json") - var convertFloats bool - var parser gojson.Parser - parser = gojson.ParseJson - convertFloats = true - - input := bytes.NewReader(fa) - - output, err := gojson.Generate(input, parser, *name, pkg, tagList, subStruct, convertFloats) - - fmt.Print(string(output)) - + //name := &fat.FlexibleAssetTypeData.Attributes.Name } } diff --git a/generate-structs/vendor/jflect/field.go b/generate-structs/vendor/jflect/field.go new file mode 100644 index 0000000..795b760 --- /dev/null +++ b/generate-structs/vendor/jflect/field.go @@ -0,0 +1,59 @@ +package jflect + +import ( + "fmt" + "unicode" +) + +// Field data type +type Field struct { + name string + gtype string + tag string +} + +// Simplifies Field construction +func NewField(name, gtype string, body ...byte) Field { + if gtype == "struct" { + gtype = fmt.Sprintf("%s {%s}", gtype, body) + } + return Field{goField(name), gtype, goTag(name)} +} + +// Provides Sorter interface so we can keep field order +type FieldSort []Field + +func (s FieldSort) Len() int { return len(s) } + +func (s FieldSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +func (s FieldSort) Less(i, j int) bool { + return s[i].name < s[j].name +} + +// Return lower_case json fields to camel case fields. +func goField(jf string) string { + mkUpper := true + gf := "" + for _, c := range jf { + if mkUpper { + c = unicode.ToUpper(c) + mkUpper = false + } + if c == '_' { + mkUpper = true + continue + } + if c == '-' { + mkUpper = true + continue + } + gf += string(c) + } + return fmt.Sprintf("%s", gf) +} + +// Returns the json tag from a json field. +func goTag(jf string) string { + return fmt.Sprintf("`json:\"%s\"`", jf) +} diff --git a/generate-structs/vendor/jflect/main.go b/generate-structs/vendor/jflect/main.go new file mode 100644 index 0000000..eb272a2 --- /dev/null +++ b/generate-structs/vendor/jflect/main.go @@ -0,0 +1,132 @@ +package jflect + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "go/format" + "io" + "log" + "os" + "sort" +) + +// TODO: write proper Usage and README +var ( + ErrNotValidSyntax = errors.New("Json reflection is not valid Go syntax") +) + +//Read accepts a structName, json io.Reader and outputs a golang struct to an io.Writer +func Read(structName string, r io.Reader, w io.Writer) error { + var v interface{} + err := json.NewDecoder(r).Decode(&v) + if err != nil { + log.Println(err) + return err + } + buf := new(bytes.Buffer) + // Open struct + b, err := xreflect(v) + if err != nil { + log.Println(err) + return err + } + field := NewField(structName, "struct", b...) + fmt.Fprintf(buf, "type %s %s", field.name, field.gtype) + + // Pass through gofmt for uniform formatting, and weak syntax check. + b, err = format.Source(buf.Bytes()) + if err != nil { + log.Println(err) + fmt.Println("Final Go Code") + fmt.Println() + os.Stderr.Write(buf.Bytes()) + fmt.Println() + return ErrNotValidSyntax + } + w.Write(b) + return nil +} + +func xreflect(v interface{}) ([]byte, error) { + var ( + buf = new(bytes.Buffer) + ) + fields := []Field{} + switch root := v.(type) { + case map[string]interface{}: + for key, val := range root { + switch j := val.(type) { + case nil: + // FIXME: sometimes json service will return nil even though the type is string. + // go can not convert string to nil and vs versa. Can we assume its a string? + continue + case float64: + fields = append(fields, NewField(key, "int")) + case map[string]interface{}: + // If type is map[string]interface{} then we have nested object, Recurse + o, err := xreflect(j) + if err != nil { + log.Println(err) + return nil, err + } + fields = append(fields, NewField(key, "struct", o...)) + case []interface{}: + gtype, err := sliceType(j) + if err != nil { + log.Println(err) + return nil, err + } + fields = append(fields, NewField(key, gtype)) + default: + fields = append(fields, NewField(key, fmt.Sprintf("%T", val))) + } + } + default: + return nil, fmt.Errorf("%T: unexpected type", root) + } + // Sort and write field buffer last to keep order and formatting. + sort.Sort(FieldSort(fields)) + for _, f := range fields { + fmt.Fprintf(buf, "%s %s %s\n", f.name, f.gtype, f.tag) + } + return buf.Bytes(), nil +} + +// if all entries in j are the same type, return slice of that type +func sliceType(j []interface{}) (string, error) { + dft := "[]interface{}" + if len(j) == 0 { + return dft, nil + } + var t, t2 string + for _, v := range j { + switch v.(type) { + case string: + t2 = "[]string" + case float64: + t2 = "[]int" + case map[string]interface{}: + t2 = "[]struct" + default: + // something else, just return default + return dft, nil + } + if t != "" && t != t2 { + return dft, nil + } + t = t2 + } + + if t == "[]struct" { + o, err := xreflect(j[0]) + if err != nil { + log.Println(err) + return "", err + } + f := NewField("", "struct", o...) + t = "[]" + f.gtype + } + return t, nil +}