github.com/benma/gogen@v0.0.0-20160826115606-cf49914b915a/unmarshalmap/generator.go (about)

     1  package unmarshalmap
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"go/types"
     7  	"io"
     8  	"path/filepath"
     9  	"text/template"
    10  
    11  	"github.com/ernesto-jimenez/gogen/cleanimports"
    12  	"github.com/ernesto-jimenez/gogen/gogenutil"
    13  	"github.com/ernesto-jimenez/gogen/importer"
    14  	"github.com/ernesto-jimenez/gogen/imports"
    15  )
    16  
    17  // Generator will generate the UnmarshalMap function
    18  type Generator struct {
    19  	name       string
    20  	targetName string
    21  	namePkg    string
    22  	pkg        *types.Package
    23  	target     *types.Struct
    24  }
    25  
    26  // NewGenerator initializes a Generator
    27  func NewGenerator(pkg, target string) (*Generator, error) {
    28  	var err error
    29  	if pkg == "" || pkg[0] == '.' {
    30  		pkg, err = filepath.Abs(filepath.Clean(pkg))
    31  		if err != nil {
    32  			return nil, err
    33  		}
    34  		pkg = gogenutil.StripGopath(pkg)
    35  	}
    36  	p, err := importer.Default().Import(pkg)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  	obj := p.Scope().Lookup(target)
    41  	if obj == nil {
    42  		return nil, fmt.Errorf("struct %s missing", target)
    43  	}
    44  	if _, ok := obj.Type().Underlying().(*types.Struct); !ok {
    45  		return nil, fmt.Errorf("%s should be an struct, was %s", target, obj.Type().Underlying())
    46  	}
    47  	return &Generator{
    48  		targetName: target,
    49  		pkg:        p,
    50  		target:     obj.Type().Underlying().(*types.Struct),
    51  	}, nil
    52  }
    53  
    54  func (g Generator) Fields() []Field {
    55  	numFields := g.target.NumFields()
    56  	fields := make([]Field, 0)
    57  	for i := 0; i < numFields; i++ {
    58  		f := Field{&g, g.target.Tag(i), g.target.Field(i)}
    59  		if f.Field() != "" {
    60  			fields = append(fields, f)
    61  		}
    62  	}
    63  	return fields
    64  }
    65  
    66  func (g Generator) qf(pkg *types.Package) string {
    67  	if g.pkg == pkg {
    68  		return ""
    69  	}
    70  	return pkg.Name()
    71  }
    72  
    73  func (g Generator) Name() string {
    74  	name := g.targetName
    75  	return name
    76  }
    77  
    78  func (g Generator) Package() string {
    79  	if g.namePkg != "" {
    80  		return g.namePkg
    81  	}
    82  	return g.pkg.Name()
    83  }
    84  
    85  func (g *Generator) SetPackage(name string) {
    86  	g.namePkg = name
    87  }
    88  
    89  func (g Generator) Imports() map[string]string {
    90  	imports := imports.New(g.Package())
    91  	fields := g.Fields()
    92  	for i := 0; i < len(fields); i++ {
    93  		m := fields[i]
    94  		imports.AddImportsFrom(m.v.Type())
    95  		imports.AddImportsFrom(m.UnderlyingType())
    96  		if sub := m.UnderlyingTarget(); sub != nil {
    97  			fields = append(fields, sub.Fields()...)
    98  		}
    99  	}
   100  	return imports.Imports()
   101  }
   102  
   103  func (g Generator) Write(wr io.Writer) error {
   104  	var buf bytes.Buffer
   105  	if err := fnTmpl.Execute(&buf, g); err != nil {
   106  		return err
   107  	}
   108  	return cleanimports.Clean(wr, buf.Bytes())
   109  }
   110  
   111  func (g Generator) WriteTest(wr io.Writer) error {
   112  	var buf bytes.Buffer
   113  	if err := testTmpl.Execute(&buf, g); err != nil {
   114  		return err
   115  	}
   116  	return cleanimports.Clean(wr, buf.Bytes())
   117  }
   118  
   119  var (
   120  	testTmpl = template.Must(template.New("test").Parse(`/*
   121  * CODE GENERATED AUTOMATICALLY WITH github.com/ernesto-jimenez/gogen/unmarshalmap
   122  * THIS FILE SHOULD NOT BE EDITED BY HAND
   123  */
   124  
   125  package {{.Package}}
   126  
   127  import (
   128  	"testing"
   129  	test "github.com/ernesto-jimenez/gogen/unmarshalmap/testunmarshalmap"
   130  )
   131  
   132  func Test{{.Name}}UnmarshalMap(t *testing.T) {
   133  	test.Run(t, &{{.Name}}{})
   134  }
   135  `))
   136  	fnTmpl = template.Must(template.New("func").Parse(`/*
   137  * CODE GENERATED AUTOMATICALLY WITH github.com/ernesto-jimenez/gogen/unmarshalmap
   138  * THIS FILE SHOULD NOT BE EDITED BY HAND
   139  */
   140  
   141  package {{.Package}}
   142  
   143  import (
   144  	"fmt"
   145  {{range $path, $name := .Imports}}
   146  	{{$name}} "{{$path}}"{{end}}
   147  )
   148  
   149  {{define "UNMARSHALFIELDS"}}
   150  {{range .Fields}}
   151  {{if .IsAnonymous}}
   152  	// Anonymous {{.Name}}
   153  	if scoped := true; scoped {
   154  		var s *{{.Type}} = &s.{{.Name}}
   155  		// Fill object
   156  		{{template "UNMARSHALFIELDS" .UnderlyingTarget}}
   157  	}
   158  {{else if .IsArrayOrSlice}}
   159  	// ArrayOrSlice {{.Name}}
   160  	{{if .UnderlyingIsBasic}}
   161  	if v, ok := m["{{.Field}}"].([]{{.UnderlyingType}}); ok {
   162  		{{if .IsSlice}}
   163  		s.{{.Name}} = make({{.Type}}, len(v))
   164  		{{else}}
   165  		if len(s.{{.Name}}) < len(v) {
   166  			return fmt.Errorf("expected field {{.Field}} to be an array with %d elements, but got an array with %d", len(s.{{.Name}}), len(v))
   167  		}
   168  		{{end}}
   169  		for i, el := range v {
   170  			s.{{.Name}}[i] = el
   171  		}
   172  	} else if v, ok := m["{{.Field}}"].([]interface{}); ok {
   173  		{{if .IsSlice}}
   174  		s.{{.Name}} = make({{.Type}}, len(v))
   175  		{{else}}
   176  		if len(s.{{.Name}}) < len(v) {
   177  			return fmt.Errorf("expected field {{.Field}} to be an array with %d elements, but got an array with %d", len(s.{{.Name}}), len(v))
   178  		}
   179  		{{end}}
   180  		for i, el := range v {
   181  			if v, ok := el.({{.UnderlyingType}}); ok {
   182  				s.{{.Name}}[i] = v
   183  			{{if .UnderlyingConvertibleFromFloat64}}
   184  			} else if m, ok := el.(float64); ok {
   185  				v := {{.UnderlyingType}}(m)
   186  				s.{{.Name}} = v
   187  			{{end}}
   188  			} else {
   189  				return fmt.Errorf("expected field {{.Field}}[%d] to be {{.UnderlyingType}} but got %T", i, el)
   190  			}
   191  		}
   192  	} else if v, exists := m["{{.Field}}"]; exists && v != nil {
   193  		return fmt.Errorf("expected field {{.Field}} to be []{{.UnderlyingType}} but got %T", m["{{.Field}}"])
   194  	}
   195  	{{else}}
   196  	if v, ok := m["{{.Field}}"].([]interface{}); ok {
   197  		{{if .IsSlice}}
   198  		s.{{.Name}} = make({{.Type}}, len(v))
   199  		{{else}}
   200  		if len(s.{{.Name}}) < len(v) {
   201  			return fmt.Errorf("expected field {{.Field}} to be an array with %d elements, but got an array with %d", len(s.{{.Name}}), len(v))
   202  		}
   203  		{{end}}
   204  		prev := s
   205  		for i, el := range v {
   206  			var s *{{.UnderlyingTypeName}}
   207  			{{if .UnderlyingIsPointer}}
   208  			if el == nil {
   209  				continue
   210  			}
   211  			prev.{{.Name}}[i] = &{{.UnderlyingTypeName}}{}
   212  			s = prev.{{.Name}}[i]
   213  			{{else}}
   214  			s = &prev.{{.Name}}[i]
   215  			{{end}}
   216  			if m, ok := el.(map[string]interface{}); ok {
   217  				// Fill object
   218  				{{template "UNMARSHALFIELDS" .UnderlyingTarget}}
   219  			}
   220  		}
   221  	} else if v, exists := m["{{.Field}}"]; exists && v != nil {
   222  		return fmt.Errorf("expected field {{.Field}} to be []interface{} but got %T", m["{{.Field}}"])
   223  	}
   224  	{{end}}
   225  {{else if .IsPointer}}
   226  	// Pointer {{.Name}}
   227  	if p, ok := m["{{.Field}}"]; ok {
   228  		{{if .UnderlyingIsBasic}}
   229  		if m, ok := p.({{.UnderlyingType}}); ok {
   230  			s.{{.Name}} = &m
   231  		{{if .UnderlyingConvertibleFromFloat64}}
   232  		} else if m, ok := p.(float64); ok {
   233  			v := {{.UnderlyingType}}(m)
   234  			s.{{.Name}} = &v
   235  		{{end}}
   236  		} else if p == nil {
   237  			s.{{.Name}} = nil
   238  		}
   239  		{{else}}
   240  		if m, ok := p.(map[string]interface{}); ok {
   241  			if s.{{.Name}} == nil {
   242  				s.{{.Name}} = &{{.UnderlyingTypeName}}{}
   243  			}
   244  			s := s.{{.Name}}
   245  			{{template "UNMARSHALFIELDS" .UnderlyingTarget}}
   246  		} else if p == nil {
   247  			s.{{.Name}} = nil
   248  		} else {
   249  			return fmt.Errorf("expected field {{.Field}} to be map[string]interface{} but got %T", p)
   250  		}
   251  		{{end}}
   252  	}
   253  {{else if .IsStruct}}
   254  	// Struct {{.Name}}
   255  	if m, ok := m["{{.Field}}"].(map[string]interface{}); ok {
   256  		var s *{{.Type}} = &s.{{.Name}}
   257  		// Fill object
   258  		{{template "UNMARSHALFIELDS" .UnderlyingTarget}}
   259  	} else if v, exists := m["{{.Field}}"]; exists && v != nil {
   260  		return fmt.Errorf("expected field {{.Field}} to be map[string]interface{} but got %T", m["{{.Field}}"])
   261  	}
   262  {{else}}
   263  	if v, ok := m["{{.Field}}"].({{.Type}}); ok {
   264  		s.{{.Name}} = v
   265  	{{if .ConvertibleFromFloat64}}
   266  	} else if p, ok := m["{{.Field}}"].(float64); ok {
   267  		v := {{.Type}}(p)
   268  		s.{{.Name}} = v
   269  	{{end}}
   270  	} else if v, exists := m["{{.Field}}"]; exists && v != nil {
   271  		return fmt.Errorf("expected field {{.Field}} to be {{.Type}} but got %T", m["{{.Field}}"])
   272  	}
   273  {{end}}
   274  {{end}}
   275  {{end}}
   276  
   277  // UnmarshalMap takes a map and unmarshals the fieds into the struct
   278  func (s *{{.Name}}) UnmarshalMap(m map[string]interface{}) error {
   279  	{{template "UNMARSHALFIELDS" .}}
   280  	return nil
   281  }
   282  `))
   283  )