├── .gitignore ├── testfile.go ├── LICENSE ├── README.md └── generator.go /.gitignore: -------------------------------------------------------------------------------- 1 | generator 2 | *_bindings.go -------------------------------------------------------------------------------- /testfile.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "errors" 4 | 5 | type TestStruct struct { 6 | Field1 int `json:"field_1" sql:"test"` 7 | TestField2 string `json:"field_2"` 8 | NewField string 9 | } 10 | 11 | type TestStruct2 struct { 12 | Field1 int `json:"field_1"` 13 | TestField2 string `json:"field_2"` 14 | Embed 15 | } 16 | 17 | type Embed struct { 18 | EmbedField int 19 | } 20 | 21 | type Item struct { 22 | A string 23 | B int 24 | } 25 | 26 | var ErrNoSuchItem = errors.New("no such item") 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Matt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Autobindings 2 | 3 | [![Join the chat at https://gitter.im/rainingclouds/autobindings](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/rainingclouds/autobindings?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | Autobindings is a simple extention to the amazing library [Binding](https://github.com/mholt/binding). So binding is a reflectionless data binding for Go's net/http. For that developer has to write a FieldMap function which is used by this library to map the incoming JSON from the request to the struct fields. 6 | 7 | ## What autobinding does ? 8 | So it automatically creates FieldMap function for your struct. 9 | 10 | ## How to use ? 11 | Just add this line to all of your files which has struct and for which you want to create a FieldMap function 12 | 13 | ``` 14 | //go:generate autobindings -file 15 | ``` 16 | 17 | Or 18 | 19 | From command line just run 20 | 21 | ``` 22 | autobindings -file 23 | ``` 24 | 25 | Or 26 | 27 | If you don't want to create the automatic files, then use -print flag to just print the function associated with your struct on the console 28 | 29 | ``` 30 | autobindings -file -print 31 | ``` 32 | 33 | Example 34 | 35 | ```go 36 | $ ./generator -file testfile.go -print 37 | package main 38 | 39 | /* 40 | This is an autogenerated file by autobindings 41 | */ 42 | 43 | import ( 44 | "github.com/mholt/binding" 45 | ) 46 | 47 | func (i Item) FieldMap() binding.FieldMap { 48 | return binding.FieldMap{ 49 | &i.A: "A", 50 | &i.B: "B", 51 | } 52 | } 53 | ``` 54 | 55 | ## How to install ? 56 | ``` 57 | go install github.com/rainingclouds/autobindings 58 | ``` 59 | 60 | ## How does it happens ? 61 | Using the power of go generate :) 62 | It creates _bindings.go for every struct in the given file. This file contains FieldMap function for that struct. This function is used by [Binding](https://github.com/mholt/binding) library to perform the mapping. 63 | 64 | ## Okay so what is missing ? 65 | * It doesn't support Embedded fields yet. 66 | 67 | ## Happy to help 68 | akshay@rainingclouds.com 69 | [@akshay_deo](https://twitter.com/akshay_deo) 70 | -------------------------------------------------------------------------------- /generator.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "go/ast" 8 | "go/format" 9 | "go/parser" 10 | "go/token" 11 | "os" 12 | "strings" 13 | "text/template" 14 | ) 15 | 16 | var bindingsFile = `package {{.packageName}} 17 | /* 18 | This is an autogenerated file by autobindings 19 | */ 20 | 21 | import( 22 | "github.com/mholt/binding" 23 | ) 24 | 25 | func ({{.variableName}} {{.structName}}) FieldMap() binding.FieldMap { 26 | binding_fmap := binding.FieldMap{ {{$vname := .variableName}}{{range $field, $mapping := .mappings}} 27 | &{{$vname}}.{{$field}}: "{{$mapping}}",{{end}} 28 | } 29 | 30 | 31 | {{ if .hasEmbeds }} 32 | type FieldMap interface { 33 | FieldMap() binding.FieldMap 34 | } 35 | var iface interface{} 36 | {{$vname := .variableName}} 37 | {{range $field, $type := .embeds}} 38 | iface = {{$vname}}.{{$type}} 39 | if m, ok := iface.(FieldMap); ok { 40 | for k, v := range m.FieldMap() { 41 | binding_fmap[k] = v 42 | } 43 | } 44 | {{end}} 45 | {{end}} 46 | return binding_fmap 47 | } 48 | ` 49 | 50 | func main() { 51 | 52 | prnt := flag.Bool("print", false, "Output In Console") 53 | filename := flag.String("file", "", "Input file") 54 | 55 | flag.Parse() 56 | 57 | if *filename == "" { 58 | fmt.Println("Usage : bindings {file_name}\nExample: bindings file.go") 59 | return 60 | } 61 | 62 | generateFieldMap(*filename, *prnt) 63 | } 64 | 65 | func generateFieldMap(fileName string, printOnConsole bool) { 66 | fset := token.NewFileSet() // positions are relative to fset 67 | // Parse the file given in arguments 68 | f, err := parser.ParseFile(fset, fileName, nil, parser.ParseComments) 69 | if err != nil { 70 | panic(err) 71 | } 72 | structMap := map[string]*ast.FieldList{} 73 | // range over the structs and fill struct map 74 | for _, d := range f.Scope.Objects { 75 | ts, ok := d.Decl.(*ast.TypeSpec) 76 | if !ok { 77 | continue 78 | } 79 | switch ts.Type.(type) { 80 | case *ast.StructType: 81 | x, _ := ts.Type.(*ast.StructType) 82 | structMap[ts.Name.String()] = x.Fields 83 | } 84 | } 85 | // looping through each struct and creating a bindings file for it 86 | packageName := f.Name 87 | for structName, fields := range structMap { 88 | variableName := strings.ToLower(string(structName[0])) 89 | mappings := map[string]string{} 90 | embeds := []ast.Expr{} 91 | hasEmbeds := false 92 | for _, field := range fields.List { 93 | if len(field.Names) == 0 { 94 | hasEmbeds = true 95 | embeds = append(embeds, field.Type) 96 | continue 97 | } 98 | name := field.Names[0].String() 99 | // if tag for field doesn't exists, create one 100 | if field.Tag == nil { 101 | mappings[name] = name 102 | } else if strings.Contains(field.Tag.Value, "json") { 103 | tags := strings.Replace(field.Tag.Value, "`", "", -1) 104 | for _, tag := range strings.Split(tags, " ") { 105 | if strings.Contains(tag, "json") { 106 | mapping := strings.Replace(tag, "json:\"", "", -1) 107 | mapping = strings.Replace(mapping, "\"", "", -1) 108 | if mapping == "-" { 109 | continue 110 | } 111 | mappings[name] = mapping 112 | } 113 | } 114 | } else { 115 | // I will handle other cases later 116 | mappings[name] = name 117 | } 118 | } 119 | content := new(bytes.Buffer) 120 | t := template.Must(template.New("bindings").Parse(bindingsFile)) 121 | err = t.Execute(content, map[string]interface{}{ 122 | "packageName": packageName, 123 | "variableName": variableName, 124 | "structName": structName, 125 | "mappings": mappings, 126 | "embeds": embeds, 127 | "hasEmbeds": hasEmbeds}) 128 | if err != nil { 129 | panic(err) 130 | } 131 | finalContent, err := format.Source(content.Bytes()) 132 | if err != nil { 133 | panic(err) 134 | } 135 | if printOnConsole { 136 | fmt.Println(string(finalContent)) 137 | return 138 | } 139 | // opening file for writing content 140 | writer, err := os.Create(fmt.Sprintf("%s_bindings.go", strings.ToLower(structName))) 141 | if err != nil { 142 | fmt.Printf("Error opening file %v", err) 143 | panic(err) 144 | } 145 | writer.WriteString(string(finalContent)) 146 | writer.Close() 147 | } 148 | } 149 | --------------------------------------------------------------------------------