go-itg/generate-structs/vendor/jflect/main.go

133 lines
2.9 KiB
Go

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
}