629 lines
19 KiB
Go
629 lines
19 KiB
Go
// Copyright 2019 DeepMap, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package codegen
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"runtime/debug"
|
|
"sort"
|
|
"strings"
|
|
"text/template"
|
|
|
|
"github.com/getkin/kin-openapi/openapi3"
|
|
"github.com/pkg/errors"
|
|
"golang.org/x/tools/imports"
|
|
|
|
"github.com/deepmap/oapi-codegen/pkg/codegen/templates"
|
|
)
|
|
|
|
// Options defines the optional code to generate.
|
|
type Options struct {
|
|
GenerateChiServer bool // GenerateChiServer specifies whether to generate chi server boilerplate
|
|
GenerateEchoServer bool // GenerateEchoServer specifies whether to generate echo server boilerplate
|
|
GenerateClient bool // GenerateClient specifies whether to generate client boilerplate
|
|
GenerateTypes bool // GenerateTypes specifies whether to generate type definitions
|
|
EmbedSpec bool // Whether to embed the swagger spec in the generated code
|
|
SkipFmt bool // Whether to skip go imports on the generated code
|
|
SkipPrune bool // Whether to skip pruning unused components on the generated code
|
|
AliasTypes bool // Whether to alias types if possible
|
|
IncludeTags []string // Only include operations that have one of these tags. Ignored when empty.
|
|
ExcludeTags []string // Exclude operations that have one of these tags. Ignored when empty.
|
|
UserTemplates map[string]string // Override built-in templates from user-provided files
|
|
ImportMapping map[string]string // ImportMapping specifies the golang package path for each external reference
|
|
ExcludeSchemas []string // Exclude from generation schemas with given names. Ignored when empty.
|
|
}
|
|
|
|
// goImport represents a go package to be imported in the generated code
|
|
type goImport struct {
|
|
Name string // package name
|
|
Path string // package path
|
|
}
|
|
|
|
// String returns a go import statement
|
|
func (gi goImport) String() string {
|
|
if gi.Name != "" {
|
|
return fmt.Sprintf("%s %q", gi.Name, gi.Path)
|
|
}
|
|
return fmt.Sprintf("%q", gi.Path)
|
|
}
|
|
|
|
// importMap maps external OpenAPI specifications files/urls to external go packages
|
|
type importMap map[string]goImport
|
|
|
|
// GoImports returns a slice of go import statements
|
|
func (im importMap) GoImports() []string {
|
|
goImports := make([]string, 0, len(im))
|
|
for _, v := range im {
|
|
goImports = append(goImports, v.String())
|
|
}
|
|
return goImports
|
|
}
|
|
|
|
var importMapping importMap
|
|
|
|
func constructImportMapping(input map[string]string) importMap {
|
|
var (
|
|
pathToName = map[string]string{}
|
|
result = importMap{}
|
|
)
|
|
|
|
{
|
|
var packagePaths []string
|
|
for _, packageName := range input {
|
|
packagePaths = append(packagePaths, packageName)
|
|
}
|
|
sort.Strings(packagePaths)
|
|
|
|
for _, packagePath := range packagePaths {
|
|
if _, ok := pathToName[packagePath]; !ok {
|
|
pathToName[packagePath] = fmt.Sprintf("externalRef%d", len(pathToName))
|
|
}
|
|
}
|
|
}
|
|
for specPath, packagePath := range input {
|
|
result[specPath] = goImport{Name: pathToName[packagePath], Path: packagePath}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Uses the Go templating engine to generate all of our server wrappers from
|
|
// the descriptions we've built up above from the schema objects.
|
|
// opts defines
|
|
func Generate(swagger *openapi3.T, packageName string, opts Options) (string, error) {
|
|
importMapping = constructImportMapping(opts.ImportMapping)
|
|
|
|
filterOperationsByTag(swagger, opts)
|
|
if !opts.SkipPrune {
|
|
pruneUnusedComponents(swagger)
|
|
}
|
|
|
|
// This creates the golang templates text package
|
|
TemplateFunctions["opts"] = func() Options { return opts }
|
|
t := template.New("oapi-codegen").Funcs(TemplateFunctions)
|
|
// This parses all of our own template files into the template object
|
|
// above
|
|
t, err := templates.Parse(t)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error parsing oapi-codegen templates")
|
|
}
|
|
|
|
// Override built-in templates with user-provided versions
|
|
for _, tpl := range t.Templates() {
|
|
if _, ok := opts.UserTemplates[tpl.Name()]; ok {
|
|
utpl := t.New(tpl.Name())
|
|
if _, err := utpl.Parse(opts.UserTemplates[tpl.Name()]); err != nil {
|
|
return "", errors.Wrapf(err, "error parsing user-provided template %q", tpl.Name())
|
|
}
|
|
}
|
|
}
|
|
|
|
ops, err := OperationDefinitions(swagger)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error creating operation definitions")
|
|
}
|
|
|
|
var typeDefinitions, constantDefinitions string
|
|
if opts.GenerateTypes {
|
|
typeDefinitions, err = GenerateTypeDefinitions(t, swagger, ops, opts.ExcludeSchemas)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating type definitions")
|
|
}
|
|
|
|
constantDefinitions, err = GenerateConstants(t, ops)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating constants")
|
|
}
|
|
|
|
}
|
|
|
|
var echoServerOut string
|
|
if opts.GenerateEchoServer {
|
|
echoServerOut, err = GenerateEchoServer(t, ops)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating Go handlers for Paths")
|
|
}
|
|
}
|
|
|
|
var chiServerOut string
|
|
if opts.GenerateChiServer {
|
|
chiServerOut, err = GenerateChiServer(t, ops)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating Go handlers for Paths")
|
|
}
|
|
}
|
|
|
|
var clientOut string
|
|
if opts.GenerateClient {
|
|
clientOut, err = GenerateClient(t, ops)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating client")
|
|
}
|
|
}
|
|
|
|
var clientWithResponsesOut string
|
|
if opts.GenerateClient {
|
|
clientWithResponsesOut, err = GenerateClientWithResponses(t, ops)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating client with responses")
|
|
}
|
|
}
|
|
|
|
var inlinedSpec string
|
|
if opts.EmbedSpec {
|
|
inlinedSpec, err = GenerateInlinedSpec(t, importMapping, swagger)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating Go handlers for Paths")
|
|
}
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
w := bufio.NewWriter(&buf)
|
|
|
|
externalImports := importMapping.GoImports()
|
|
importsOut, err := GenerateImports(t, externalImports, packageName)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating imports")
|
|
}
|
|
|
|
_, err = w.WriteString(importsOut)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error writing imports")
|
|
}
|
|
|
|
_, err = w.WriteString(constantDefinitions)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error writing constants")
|
|
}
|
|
|
|
_, err = w.WriteString(typeDefinitions)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error writing type definitions")
|
|
|
|
}
|
|
|
|
if opts.GenerateClient {
|
|
_, err = w.WriteString(clientOut)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error writing client")
|
|
}
|
|
_, err = w.WriteString(clientWithResponsesOut)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error writing client")
|
|
}
|
|
}
|
|
|
|
if opts.GenerateEchoServer {
|
|
_, err = w.WriteString(echoServerOut)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error writing server path handlers")
|
|
}
|
|
}
|
|
|
|
if opts.GenerateChiServer {
|
|
_, err = w.WriteString(chiServerOut)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error writing server path handlers")
|
|
}
|
|
}
|
|
|
|
if opts.EmbedSpec {
|
|
_, err = w.WriteString(inlinedSpec)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error writing inlined spec")
|
|
}
|
|
}
|
|
|
|
err = w.Flush()
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error flushing output buffer")
|
|
}
|
|
|
|
// remove any byte-order-marks which break Go-Code
|
|
goCode := SanitizeCode(buf.String())
|
|
|
|
// The generation code produces unindented horrors. Use the Go Imports
|
|
// to make it all pretty.
|
|
if opts.SkipFmt {
|
|
return goCode, nil
|
|
}
|
|
|
|
outBytes, err := imports.Process(packageName+".go", []byte(goCode), nil)
|
|
if err != nil {
|
|
fmt.Println(goCode)
|
|
return "", errors.Wrap(err, "error formatting Go code")
|
|
}
|
|
return string(outBytes), nil
|
|
}
|
|
|
|
func GenerateTypeDefinitions(t *template.Template, swagger *openapi3.T, ops []OperationDefinition, excludeSchemas []string) (string, error) {
|
|
schemaTypes, err := GenerateTypesForSchemas(t, swagger.Components.Schemas, excludeSchemas)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating Go types for component schemas")
|
|
}
|
|
|
|
paramTypes, err := GenerateTypesForParameters(t, swagger.Components.Parameters)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating Go types for component parameters")
|
|
}
|
|
allTypes := append(schemaTypes, paramTypes...)
|
|
|
|
responseTypes, err := GenerateTypesForResponses(t, swagger.Components.Responses)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating Go types for component responses")
|
|
}
|
|
allTypes = append(allTypes, responseTypes...)
|
|
|
|
bodyTypes, err := GenerateTypesForRequestBodies(t, swagger.Components.RequestBodies)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating Go types for component request bodies")
|
|
}
|
|
allTypes = append(allTypes, bodyTypes...)
|
|
|
|
paramTypesOut, err := GenerateTypesForOperations(t, ops)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating Go types for operation parameters")
|
|
}
|
|
|
|
enumsOut, err := GenerateEnums(t, allTypes)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating code for type enums")
|
|
}
|
|
|
|
typesOut, err := GenerateTypes(t, allTypes)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating code for type definitions")
|
|
}
|
|
|
|
allOfBoilerplate, err := GenerateAdditionalPropertyBoilerplate(t, allTypes)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating allOf boilerplate")
|
|
}
|
|
|
|
typeDefinitions := strings.Join([]string{enumsOut, typesOut, paramTypesOut, allOfBoilerplate}, "")
|
|
return typeDefinitions, nil
|
|
}
|
|
|
|
// Generates operation ids, context keys, paths, etc. to be exported as constants
|
|
func GenerateConstants(t *template.Template, ops []OperationDefinition) (string, error) {
|
|
var buf bytes.Buffer
|
|
w := bufio.NewWriter(&buf)
|
|
|
|
constants := Constants{
|
|
SecuritySchemeProviderNames: []string{},
|
|
}
|
|
|
|
providerNameMap := map[string]struct{}{}
|
|
for _, op := range ops {
|
|
for _, def := range op.SecurityDefinitions {
|
|
providerName := SanitizeGoIdentity(def.ProviderName)
|
|
providerNameMap[providerName] = struct{}{}
|
|
}
|
|
}
|
|
|
|
var providerNames []string
|
|
for providerName := range providerNameMap {
|
|
providerNames = append(providerNames, providerName)
|
|
}
|
|
|
|
sort.Strings(providerNames)
|
|
|
|
for _, providerName := range providerNames {
|
|
constants.SecuritySchemeProviderNames = append(constants.SecuritySchemeProviderNames, providerName)
|
|
}
|
|
|
|
err := t.ExecuteTemplate(w, "constants.tmpl", constants)
|
|
|
|
if err != nil {
|
|
return "", fmt.Errorf("error generating server interface: %s", err)
|
|
}
|
|
err = w.Flush()
|
|
if err != nil {
|
|
return "", fmt.Errorf("error flushing output buffer for server interface: %s", err)
|
|
}
|
|
return buf.String(), nil
|
|
}
|
|
|
|
// Generates type definitions for any custom types defined in the
|
|
// components/schemas section of the Swagger spec.
|
|
func GenerateTypesForSchemas(t *template.Template, schemas map[string]*openapi3.SchemaRef, excludeSchemas []string) ([]TypeDefinition, error) {
|
|
var excludeSchemasMap = make(map[string]bool)
|
|
for _, schema := range excludeSchemas {
|
|
excludeSchemasMap[schema] = true
|
|
}
|
|
types := make([]TypeDefinition, 0)
|
|
// We're going to define Go types for every object under components/schemas
|
|
for _, schemaName := range SortedSchemaKeys(schemas) {
|
|
if _, ok := excludeSchemasMap[schemaName]; ok {
|
|
continue
|
|
}
|
|
schemaRef := schemas[schemaName]
|
|
|
|
goSchema, err := GenerateGoSchema(schemaRef, []string{schemaName})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, fmt.Sprintf("error converting Schema %s to Go type", schemaName))
|
|
}
|
|
|
|
types = append(types, TypeDefinition{
|
|
JsonName: schemaName,
|
|
TypeName: SchemaNameToTypeName(schemaName),
|
|
Schema: goSchema,
|
|
})
|
|
|
|
types = append(types, goSchema.GetAdditionalTypeDefs()...)
|
|
}
|
|
return types, nil
|
|
}
|
|
|
|
// Generates type definitions for any custom types defined in the
|
|
// components/parameters section of the Swagger spec.
|
|
func GenerateTypesForParameters(t *template.Template, params map[string]*openapi3.ParameterRef) ([]TypeDefinition, error) {
|
|
var types []TypeDefinition
|
|
for _, paramName := range SortedParameterKeys(params) {
|
|
paramOrRef := params[paramName]
|
|
|
|
goType, err := paramToGoType(paramOrRef.Value, nil)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, fmt.Sprintf("error generating Go type for schema in parameter %s", paramName))
|
|
}
|
|
|
|
typeDef := TypeDefinition{
|
|
JsonName: paramName,
|
|
Schema: goType,
|
|
TypeName: SchemaNameToTypeName(paramName),
|
|
}
|
|
|
|
if paramOrRef.Ref != "" {
|
|
// Generate a reference type for referenced parameters
|
|
refType, err := RefPathToGoType(paramOrRef.Ref)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, fmt.Sprintf("error generating Go type for (%s) in parameter %s", paramOrRef.Ref, paramName))
|
|
}
|
|
typeDef.TypeName = SchemaNameToTypeName(refType)
|
|
}
|
|
|
|
types = append(types, typeDef)
|
|
}
|
|
return types, nil
|
|
}
|
|
|
|
// Generates type definitions for any custom types defined in the
|
|
// components/responses section of the Swagger spec.
|
|
func GenerateTypesForResponses(t *template.Template, responses openapi3.Responses) ([]TypeDefinition, error) {
|
|
var types []TypeDefinition
|
|
|
|
for _, responseName := range SortedResponsesKeys(responses) {
|
|
responseOrRef := responses[responseName]
|
|
|
|
// We have to generate the response object. We're only going to
|
|
// handle application/json media types here. Other responses should
|
|
// simply be specified as strings or byte arrays.
|
|
response := responseOrRef.Value
|
|
jsonResponse, found := response.Content["application/json"]
|
|
if found {
|
|
goType, err := GenerateGoSchema(jsonResponse.Schema, []string{responseName})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, fmt.Sprintf("error generating Go type for schema in response %s", responseName))
|
|
}
|
|
|
|
typeDef := TypeDefinition{
|
|
JsonName: responseName,
|
|
Schema: goType,
|
|
TypeName: SchemaNameToTypeName(responseName),
|
|
}
|
|
|
|
if responseOrRef.Ref != "" {
|
|
// Generate a reference type for referenced parameters
|
|
refType, err := RefPathToGoType(responseOrRef.Ref)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, fmt.Sprintf("error generating Go type for (%s) in parameter %s", responseOrRef.Ref, responseName))
|
|
}
|
|
typeDef.TypeName = SchemaNameToTypeName(refType)
|
|
}
|
|
types = append(types, typeDef)
|
|
}
|
|
}
|
|
return types, nil
|
|
}
|
|
|
|
// Generates type definitions for any custom types defined in the
|
|
// components/requestBodies section of the Swagger spec.
|
|
func GenerateTypesForRequestBodies(t *template.Template, bodies map[string]*openapi3.RequestBodyRef) ([]TypeDefinition, error) {
|
|
var types []TypeDefinition
|
|
|
|
for _, bodyName := range SortedRequestBodyKeys(bodies) {
|
|
bodyOrRef := bodies[bodyName]
|
|
|
|
// As for responses, we will only generate Go code for JSON bodies,
|
|
// the other body formats are up to the user.
|
|
response := bodyOrRef.Value
|
|
jsonBody, found := response.Content["application/json"]
|
|
if found {
|
|
goType, err := GenerateGoSchema(jsonBody.Schema, []string{bodyName})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, fmt.Sprintf("error generating Go type for schema in body %s", bodyName))
|
|
}
|
|
|
|
typeDef := TypeDefinition{
|
|
JsonName: bodyName,
|
|
Schema: goType,
|
|
TypeName: SchemaNameToTypeName(bodyName),
|
|
}
|
|
|
|
if bodyOrRef.Ref != "" {
|
|
// Generate a reference type for referenced bodies
|
|
refType, err := RefPathToGoType(bodyOrRef.Ref)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, fmt.Sprintf("error generating Go type for (%s) in body %s", bodyOrRef.Ref, bodyName))
|
|
}
|
|
typeDef.TypeName = SchemaNameToTypeName(refType)
|
|
}
|
|
types = append(types, typeDef)
|
|
}
|
|
}
|
|
return types, nil
|
|
}
|
|
|
|
// Helper function to pass a bunch of types to the template engine, and buffer
|
|
// its output into a string.
|
|
func GenerateTypes(t *template.Template, types []TypeDefinition) (string, error) {
|
|
var buf bytes.Buffer
|
|
w := bufio.NewWriter(&buf)
|
|
|
|
context := struct {
|
|
Types []TypeDefinition
|
|
}{
|
|
Types: types,
|
|
}
|
|
|
|
err := t.ExecuteTemplate(w, "typedef.tmpl", context)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating types")
|
|
}
|
|
err = w.Flush()
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error flushing output buffer for types")
|
|
}
|
|
return buf.String(), nil
|
|
}
|
|
|
|
func GenerateEnums(t *template.Template, types []TypeDefinition) (string, error) {
|
|
var buf bytes.Buffer
|
|
w := bufio.NewWriter(&buf)
|
|
c := Constants{
|
|
EnumDefinitions: []EnumDefinition{},
|
|
}
|
|
for _, tp := range types {
|
|
if len(tp.Schema.EnumValues) > 0 {
|
|
wrapper := ""
|
|
if tp.Schema.GoType == "string" {
|
|
wrapper = `"`
|
|
}
|
|
c.EnumDefinitions = append(c.EnumDefinitions, EnumDefinition{
|
|
Schema: tp.Schema,
|
|
TypeName: tp.TypeName,
|
|
ValueWrapper: wrapper,
|
|
})
|
|
}
|
|
}
|
|
err := t.ExecuteTemplate(w, "constants.tmpl", c)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating enums")
|
|
}
|
|
err = w.Flush()
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error flushing output buffer for enums")
|
|
}
|
|
return buf.String(), nil
|
|
}
|
|
|
|
// Generate our import statements and package definition.
|
|
func GenerateImports(t *template.Template, externalImports []string, packageName string) (string, error) {
|
|
var buf bytes.Buffer
|
|
w := bufio.NewWriter(&buf)
|
|
|
|
// Read build version for incorporating into generated files
|
|
var modulePath string
|
|
var moduleVersion string
|
|
if bi, ok := debug.ReadBuildInfo(); ok {
|
|
modulePath = bi.Main.Path
|
|
moduleVersion = bi.Main.Version
|
|
} else {
|
|
// Unit tests have ok=false, so we'll just use "unknown" for the
|
|
// version if we can't read this.
|
|
modulePath = "unknown module path"
|
|
moduleVersion = "unknown version"
|
|
}
|
|
|
|
context := struct {
|
|
ExternalImports []string
|
|
PackageName string
|
|
ModuleName string
|
|
Version string
|
|
}{
|
|
ExternalImports: externalImports,
|
|
PackageName: packageName,
|
|
ModuleName: modulePath,
|
|
Version: moduleVersion,
|
|
}
|
|
err := t.ExecuteTemplate(w, "imports.tmpl", context)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating imports")
|
|
}
|
|
err = w.Flush()
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error flushing output buffer for imports")
|
|
}
|
|
return buf.String(), nil
|
|
}
|
|
|
|
// Generate all the glue code which provides the API for interacting with
|
|
// additional properties and JSON-ification
|
|
func GenerateAdditionalPropertyBoilerplate(t *template.Template, typeDefs []TypeDefinition) (string, error) {
|
|
var buf bytes.Buffer
|
|
w := bufio.NewWriter(&buf)
|
|
|
|
var filteredTypes []TypeDefinition
|
|
for _, t := range typeDefs {
|
|
if t.Schema.HasAdditionalProperties {
|
|
filteredTypes = append(filteredTypes, t)
|
|
}
|
|
}
|
|
|
|
context := struct {
|
|
Types []TypeDefinition
|
|
}{
|
|
Types: filteredTypes,
|
|
}
|
|
|
|
err := t.ExecuteTemplate(w, "additional-properties.tmpl", context)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error generating additional properties code")
|
|
}
|
|
err = w.Flush()
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "error flushing output buffer for additional properties")
|
|
}
|
|
return buf.String(), nil
|
|
}
|
|
|
|
// SanitizeCode runs sanitizers across the generated Go code to ensure the
|
|
// generated code will be able to compile.
|
|
func SanitizeCode(goCode string) string {
|
|
// remove any byte-order-marks which break Go-Code
|
|
// See: https://groups.google.com/forum/#!topic/golang-nuts/OToNIPdfkks
|
|
return strings.Replace(goCode, "\uFEFF", "", -1)
|
|
}
|