github.com/thetreep/go-swagger@v0.0.0-20240223100711-35af64f14f01/generator/media.go (about) 1 package generator 2 3 import ( 4 "regexp" 5 "sort" 6 "strings" 7 8 "github.com/go-openapi/runtime" 9 "github.com/go-openapi/swag" 10 ) 11 12 const jsonSerializer = "json" 13 14 var mediaTypeNames = map[*regexp.Regexp]string{ 15 regexp.MustCompile("application/.*json"): jsonSerializer, 16 regexp.MustCompile("application/.*yaml"): "yaml", 17 regexp.MustCompile("application/.*protobuf"): "protobuf", 18 regexp.MustCompile("application/.*capnproto"): "capnproto", 19 regexp.MustCompile("application/.*thrift"): "thrift", 20 regexp.MustCompile("(?:application|text)/.*xml"): "xml", 21 regexp.MustCompile("text/.*markdown"): "markdown", 22 regexp.MustCompile("text/.*html"): "html", 23 regexp.MustCompile("text/.*csv"): "csv", 24 regexp.MustCompile("text/.*tsv"): "tsv", 25 regexp.MustCompile("text/.*javascript"): "js", 26 regexp.MustCompile("text/.*css"): "css", 27 regexp.MustCompile("text/.*plain"): "txt", 28 regexp.MustCompile("application/.*octet-stream"): "bin", 29 regexp.MustCompile("application/.*tar"): "tar", 30 regexp.MustCompile("application/.*gzip"): "gzip", 31 regexp.MustCompile("application/.*gz"): "gzip", 32 regexp.MustCompile("application/.*raw-stream"): "bin", 33 regexp.MustCompile("application/x-www-form-urlencoded"): "urlform", 34 regexp.MustCompile("application/javascript"): "txt", 35 regexp.MustCompile("multipart/form-data"): "multipartform", 36 regexp.MustCompile("image/.*"): "bin", 37 regexp.MustCompile("audio/.*"): "bin", 38 regexp.MustCompile("application/pdf"): "bin", 39 } 40 41 var knownProducers = map[string]string{ 42 jsonSerializer: "runtime.JSONProducer()", 43 "yaml": "yamlpc.YAMLProducer()", 44 "xml": "runtime.XMLProducer()", 45 "txt": "runtime.TextProducer()", 46 "bin": "runtime.ByteStreamProducer()", 47 "urlform": "runtime.DiscardProducer", 48 "multipartform": "runtime.DiscardProducer", 49 } 50 51 var knownConsumers = map[string]string{ 52 jsonSerializer: "runtime.JSONConsumer()", 53 "yaml": "yamlpc.YAMLConsumer()", 54 "xml": "runtime.XMLConsumer()", 55 "txt": "runtime.TextConsumer()", 56 "bin": "runtime.ByteStreamConsumer()", 57 "urlform": "runtime.DiscardConsumer", 58 "multipartform": "runtime.DiscardConsumer", 59 } 60 61 func wellKnownMime(tn string) (string, bool) { 62 for k, v := range mediaTypeNames { 63 if k.MatchString(tn) { 64 return v, true 65 } 66 } 67 return "", false 68 } 69 70 func mediaMime(orig string) string { 71 return strings.SplitN(orig, ";", 2)[0] 72 } 73 74 func mediaGoName(media string) string { 75 return pascalize(strings.ReplaceAll(media, "*", "Star")) 76 } 77 78 func mediaParameters(orig string) string { 79 parts := strings.SplitN(orig, ";", 2) 80 if len(parts) < 2 { 81 return "" 82 } 83 return parts[1] 84 } 85 86 func (a *appGenerator) makeSerializers(mediaTypes []string, known func(string) (string, bool)) (GenSerGroups, bool) { 87 supportsJSON := false 88 uniqueSerializers := make(map[string]*GenSerializer, len(mediaTypes)) 89 uniqueSerializerGroups := make(map[string]*GenSerGroup, len(mediaTypes)) 90 91 // build all required serializers 92 for _, media := range mediaTypes { 93 key := mediaMime(media) 94 nm, ok := wellKnownMime(key) 95 if !ok { 96 // keep this serializer named, even though its implementation is empty (cf. #1557) 97 nm = key 98 } 99 name := swag.ToJSONName(nm) 100 impl, _ := known(name) 101 102 ser, ok := uniqueSerializers[key] 103 if !ok { 104 ser = &GenSerializer{ 105 AppName: a.Name, 106 ReceiverName: a.Receiver, 107 Name: name, 108 MediaType: key, 109 Implementation: impl, 110 Parameters: []string{}, 111 } 112 uniqueSerializers[key] = ser 113 } 114 // provide all known parameters (currently unused by codegen templates) 115 if params := strings.TrimSpace(mediaParameters(media)); params != "" { 116 found := false 117 for _, p := range ser.Parameters { 118 if params == p { 119 found = true 120 break 121 } 122 } 123 if !found { 124 ser.Parameters = append(ser.Parameters, params) 125 } 126 } 127 128 uniqueSerializerGroups[name] = &GenSerGroup{ 129 GenSerializer: GenSerializer{ 130 AppName: a.Name, 131 ReceiverName: a.Receiver, 132 Name: name, 133 Implementation: impl, 134 }, 135 } 136 } 137 138 if len(uniqueSerializers) == 0 { 139 impl, _ := known(jsonSerializer) 140 uniqueSerializers[runtime.JSONMime] = &GenSerializer{ 141 AppName: a.Name, 142 ReceiverName: a.Receiver, 143 Name: jsonSerializer, 144 MediaType: runtime.JSONMime, 145 Implementation: impl, 146 Parameters: []string{}, 147 } 148 uniqueSerializerGroups[jsonSerializer] = &GenSerGroup{ 149 GenSerializer: GenSerializer{ 150 AppName: a.Name, 151 ReceiverName: a.Receiver, 152 Name: jsonSerializer, 153 Implementation: impl, 154 }, 155 } 156 supportsJSON = true 157 } 158 159 // group serializers by consumer/producer to serve several mime media types 160 serializerGroups := make(GenSerGroups, 0, len(uniqueSerializers)) 161 162 for _, group := range uniqueSerializerGroups { 163 if group.Name == jsonSerializer { 164 supportsJSON = true 165 } 166 serializers := make(GenSerializers, 0, len(uniqueSerializers)) 167 for _, ser := range uniqueSerializers { 168 if group.Name == ser.Name { 169 sort.Strings(ser.Parameters) 170 serializers = append(serializers, *ser) 171 } 172 } 173 sort.Sort(serializers) 174 group.AllSerializers = serializers // provides the full list of mime media types for this serializer group 175 serializerGroups = append(serializerGroups, *group) 176 } 177 sort.Sort(serializerGroups) 178 return serializerGroups, supportsJSON 179 } 180 181 func (a *appGenerator) makeConsumes() (GenSerGroups, bool) { 182 // builds a codegen struct from all consumes in the spec 183 return a.makeSerializers(a.Analyzed.RequiredConsumes(), func(media string) (string, bool) { 184 c, ok := knownConsumers[media] 185 return c, ok 186 }) 187 } 188 189 func (a *appGenerator) makeProduces() (GenSerGroups, bool) { 190 // builds a codegen struct from all produces in the spec 191 return a.makeSerializers(a.Analyzed.RequiredProduces(), func(media string) (string, bool) { 192 p, ok := knownProducers[media] 193 return p, ok 194 }) 195 }