github.com/brycereitano/goa@v0.0.0-20170315073847-8ffa6c85e265/goagen/gen_app/encoding.go (about) 1 package genapp 2 3 import ( 4 "fmt" 5 "sort" 6 7 "github.com/goadesign/goa/design" 8 "github.com/goadesign/goa/goagen/codegen" 9 ) 10 11 // BuildEncoders builds the template data needed to render the given encoding definitions. 12 // This extra map is needed to handle the case where a single encoding definition maps to multiple 13 // encoding packages. The data is indexed by mime type. 14 func BuildEncoders(info []*design.EncodingDefinition, encoder bool) ([]*EncoderTemplateData, error) { 15 if len(info) == 0 { 16 return nil, nil 17 } 18 // knownStdPackages lists the stdlib packages known by BuildEncoders 19 var knownStdPackages = map[string]string{ 20 "encoding/json": "json", 21 "encoding/xml": "xml", 22 "encoding/gob": "gob", 23 } 24 encs := normalizeEncodingDefinitions(info) 25 data := make([]*EncoderTemplateData, len(encs)) 26 defaultMediaType := info[0].MIMETypes[0] 27 for i, enc := range encs { 28 var pkgName string 29 if name, ok := knownStdPackages[enc.PackagePath]; ok { 30 pkgName = name 31 } else { 32 srcPath, err := codegen.PackageSourcePath(enc.PackagePath) 33 if err != nil { 34 return nil, fmt.Errorf("failed to locate package source of %s (%s)", 35 enc.PackagePath, err) 36 } 37 pkgName, err = codegen.PackageName(srcPath) 38 if err != nil { 39 return nil, fmt.Errorf("failed to load package %s (%s)", 40 enc.PackagePath, err) 41 } 42 } 43 isDefault := false 44 for _, m := range enc.MIMETypes { 45 if m == defaultMediaType { 46 isDefault = true 47 } 48 } 49 d := &EncoderTemplateData{ 50 PackagePath: enc.PackagePath, 51 PackageName: pkgName, 52 Function: enc.Function, 53 MIMETypes: enc.MIMETypes, 54 Default: isDefault, 55 } 56 data[i] = d 57 } 58 return data, nil 59 } 60 61 // normalizeEncodingDefinitions figures out the package path and function of all encoding 62 // definitions and groups them by package and function name. 63 // We're going for simple rather than efficient (this is codegen after all) 64 // Also we assume that the encoding definitions have been validated: they have at least 65 // one mime type and definitions with no package path use known encoders. 66 func normalizeEncodingDefinitions(defs []*design.EncodingDefinition) []*design.EncodingDefinition { 67 // First splat all definitions so each only have one mime type 68 var encs []*design.EncodingDefinition 69 for _, enc := range defs { 70 if len(enc.MIMETypes) == 1 { 71 encs = append(encs, enc) 72 continue 73 } 74 for _, m := range enc.MIMETypes { 75 encs = append(encs, &design.EncodingDefinition{ 76 MIMETypes: []string{m}, 77 PackagePath: enc.PackagePath, 78 Function: enc.Function, 79 Encoder: enc.Encoder, 80 }) 81 } 82 } 83 84 // Next make sure all definitions have a package path 85 for _, enc := range encs { 86 if enc.PackagePath == "" { 87 mt := enc.MIMETypes[0] 88 enc.PackagePath = design.KnownEncoders[mt] 89 idx := 0 90 if !enc.Encoder { 91 idx = 1 92 } 93 enc.Function = design.KnownEncoderFunctions[mt][idx] 94 } else if enc.Function == "" { 95 if enc.Encoder { 96 enc.Function = "NewEncoder" 97 } else { 98 enc.Function = "NewDecoder" 99 } 100 } 101 } 102 103 // Regroup by package and function name 104 byfn := make(map[string][]*design.EncodingDefinition) 105 var first string 106 for _, enc := range encs { 107 key := enc.PackagePath + "#" + enc.Function 108 if first == "" { 109 first = key 110 } 111 if _, ok := byfn[key]; ok { 112 byfn[key] = append(byfn[key], enc) 113 } else { 114 byfn[key] = []*design.EncodingDefinition{enc} 115 } 116 } 117 118 // Reserialize into array keeping the first element identical since it's the default 119 // encoder. 120 return serialize(byfn, first) 121 } 122 123 func serialize(byfn map[string][]*design.EncodingDefinition, first string) []*design.EncodingDefinition { 124 res := make([]*design.EncodingDefinition, len(byfn)) 125 i := 0 126 keys := make([]string, len(byfn)) 127 for k := range byfn { 128 keys[i] = k 129 i++ 130 } 131 sort.Strings(keys) 132 var idx int 133 for j, k := range keys { 134 if k == first { 135 idx = j 136 break 137 } 138 } 139 keys[0], keys[idx] = keys[idx], keys[0] 140 i = 0 141 for _, key := range keys { 142 encs := byfn[key] 143 res[i] = &design.EncodingDefinition{ 144 MIMETypes: encs[0].MIMETypes, 145 PackagePath: encs[0].PackagePath, 146 Function: encs[0].Function, 147 } 148 if len(encs) > 0 { 149 encs = encs[1:] 150 for _, enc := range encs { 151 for _, m := range enc.MIMETypes { 152 found := false 153 for _, rm := range res[i].MIMETypes { 154 if m == rm { 155 found = true 156 break 157 } 158 } 159 if !found { 160 res[i].MIMETypes = append(res[i].MIMETypes, m) 161 } 162 } 163 } 164 } 165 i++ 166 } 167 return res 168 }