github.com/shogo82148/goa-v1@v1.6.2/goagen/codegen/types.go (about)

     1  package codegen
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sort"
     7  	"strings"
     8  	"text/template"
     9  	"unicode"
    10  
    11  	"github.com/shogo82148/goa-v1/design"
    12  	"github.com/shogo82148/goa-v1/dslengine"
    13  )
    14  
    15  // TransformMapKey is the name of the metadata used to specify the key for mapping fields when
    16  // generating the code that transforms one data structure into another.
    17  const TransformMapKey = "transform:key"
    18  
    19  var (
    20  	// TempCount holds the value appended to variable names to make them unique.
    21  	TempCount int
    22  
    23  	// Templates used by GoTypeTransform
    24  	transformT       *template.Template
    25  	transformArrayT  *template.Template
    26  	transformHashT   *template.Template
    27  	transformObjectT *template.Template
    28  )
    29  
    30  // Initialize all templates
    31  func init() {
    32  	fn := template.FuncMap{
    33  		"tabs":               Tabs,
    34  		"add":                func(a, b int) int { return a + b },
    35  		"goify":              Goify,
    36  		"gotyperef":          GoTypeRef,
    37  		"gotypename":         GoTypeName,
    38  		"transformAttribute": transformAttribute,
    39  		"transformArray":     transformArray,
    40  		"transformHash":      transformHash,
    41  		"transformObject":    transformObject,
    42  		"typeName":           typeName,
    43  	}
    44  	transformT = template.Must(template.New("transform").Funcs(fn).Parse(transformTmpl))
    45  	transformArrayT = template.Must(template.New("transformArray").Funcs(fn).Parse(transformArrayTmpl))
    46  	transformHashT = template.Must(template.New("transformHash").Funcs(fn).Parse(transformHashTmpl))
    47  	transformObjectT = template.Must(template.New("transformObject").Funcs(fn).Parse(transformObjectTmpl))
    48  }
    49  
    50  // GoTypeDef returns the Go code that defines a Go type which matches the data structure
    51  // definition (the part that comes after `type foo`).
    52  // tabs is the number of tab character(s) used to tabulate the definition however the first
    53  // line is never indented.
    54  // jsonTags controls whether to produce json tags.
    55  // private controls whether the field is a pointer or not. All fields in the struct are
    56  //   pointers for a private struct.
    57  func GoTypeDef(ds design.DataStructure, tabs int, jsonTags, private bool) string {
    58  	def := ds.Definition()
    59  	if tname, ok := def.Metadata["struct:field:type"]; ok {
    60  		if len(tname) > 0 {
    61  			return tname[0]
    62  		}
    63  	}
    64  	t := def.Type
    65  	switch actual := t.(type) {
    66  	case design.Primitive:
    67  		return GoTypeName(t, nil, tabs, private)
    68  	case *design.Array:
    69  		d := GoTypeDef(actual.ElemType, tabs, jsonTags, private)
    70  		if actual.ElemType.Type.IsObject() {
    71  			d = "*" + d
    72  		}
    73  		return "[]" + d
    74  	case *design.Hash:
    75  		keyDef := GoTypeDef(actual.KeyType, tabs, jsonTags, private)
    76  		if actual.KeyType.Type.IsObject() {
    77  			keyDef = "*" + keyDef
    78  		}
    79  		elemDef := GoTypeDef(actual.ElemType, tabs, jsonTags, private)
    80  		if actual.ElemType.Type.IsObject() {
    81  			elemDef = "*" + elemDef
    82  		}
    83  		return fmt.Sprintf("map[%s]%s", keyDef, elemDef)
    84  	case design.Object:
    85  		return goTypeDefObject(actual, def, tabs, jsonTags, private)
    86  	case *design.UserTypeDefinition:
    87  		return GoTypeName(actual, actual.AllRequired(), tabs, private)
    88  	case *design.MediaTypeDefinition:
    89  		return GoTypeName(actual, actual.AllRequired(), tabs, private)
    90  	default:
    91  		panic("goa bug: unknown data structure type")
    92  	}
    93  }
    94  
    95  // goTypeDefObject returns the Go code that defines a Go struct.
    96  func goTypeDefObject(obj design.Object, def *design.AttributeDefinition, tabs int, jsonTags, private bool) string {
    97  	var buffer bytes.Buffer
    98  	buffer.WriteString("struct {\n")
    99  	keys := make([]string, len(obj))
   100  	i := 0
   101  	for n := range obj {
   102  		keys[i] = n
   103  		i++
   104  	}
   105  	sort.Strings(keys)
   106  	for _, name := range keys {
   107  		WriteTabs(&buffer, tabs+1)
   108  		field := obj[name]
   109  		typedef := GoTypeDef(field, tabs+1, jsonTags, private)
   110  		if (private && field.Type.IsPrimitive() && !def.IsInterface(name)) || field.Type.IsObject() || def.IsPrimitivePointer(name) {
   111  			typedef = "*" + typedef
   112  		}
   113  		fname := GoifyAtt(field, name, true)
   114  		var tags string
   115  		if jsonTags {
   116  			tags = attributeTags(def, field, name, private)
   117  		}
   118  		desc := obj[name].Description
   119  		if desc != "" {
   120  			desc = strings.Replace(desc, "\n", "\n\t// ", -1)
   121  			desc = fmt.Sprintf("// %s\n\t", desc)
   122  		}
   123  		buffer.WriteString(fmt.Sprintf("%s%s %s%s\n", desc, fname, typedef, tags))
   124  	}
   125  	WriteTabs(&buffer, tabs)
   126  	buffer.WriteString("}")
   127  	return buffer.String()
   128  }
   129  
   130  // attributeTags computes the struct field tags.
   131  func attributeTags(parent, att *design.AttributeDefinition, name string, private bool) string {
   132  	var elems []string
   133  	keys := make([]string, len(att.Metadata))
   134  	i := 0
   135  	for k := range att.Metadata {
   136  		keys[i] = k
   137  		i++
   138  	}
   139  	sort.Strings(keys)
   140  	for _, key := range keys {
   141  		val := att.Metadata[key]
   142  		if strings.HasPrefix(key, "struct:tag:") {
   143  			name := key[11:]
   144  			value := strings.Join(val, ",")
   145  			elems = append(elems, fmt.Sprintf("%s:\"%s\"", name, value))
   146  		}
   147  	}
   148  	if len(elems) > 0 {
   149  		return " `" + strings.Join(elems, " ") + "`"
   150  	}
   151  	// Default algorithm
   152  	var omit string
   153  	if private || (!parent.IsRequired(name) && !parent.HasDefaultValue(name)) {
   154  		omit = ",omitempty"
   155  	}
   156  	return fmt.Sprintf(" `form:\"%s%s\" json:\"%s%s\" yaml:\"%s%s\" xml:\"%s%s\"`",
   157  		name, omit, name, omit, name, omit, name, omit)
   158  }
   159  
   160  // GoTypeRef returns the Go code that refers to the Go type which matches the given data type
   161  // (the part that comes after `var foo`)
   162  // required only applies when referring to a user type that is an object defined inline. In this
   163  // case the type (Object) does not carry the required field information defined in the parent
   164  // (anonymous) attribute.
   165  // tabs is used to properly tabulate the object struct fields and only applies to this case.
   166  // This function assumes the type is in the same package as the code accessing it.
   167  func GoTypeRef(t design.DataType, required []string, tabs int, private bool) string {
   168  	tname := GoTypeName(t, required, tabs, private)
   169  	if mt, ok := t.(*design.MediaTypeDefinition); ok {
   170  		if mt.IsError() {
   171  			return "error"
   172  		}
   173  	}
   174  	if t.IsObject() {
   175  		return "*" + tname
   176  	}
   177  	return tname
   178  }
   179  
   180  // GoTypeName returns the Go type name for a data type.
   181  // tabs is used to properly tabulate the object struct fields and only applies to this case.
   182  // This function assumes the type is in the same package as the code accessing it.
   183  // required only applies when referring to a user type that is an object defined inline. In this
   184  // case the type (Object) does not carry the required field information defined in the parent
   185  // (anonymous) attribute.
   186  func GoTypeName(t design.DataType, required []string, tabs int, private bool) string {
   187  	switch actual := t.(type) {
   188  	case design.Primitive:
   189  		return GoNativeType(t)
   190  	case *design.Array:
   191  		return "[]" + GoTypeRef(actual.ElemType.Type, actual.ElemType.AllRequired(), tabs+1, private)
   192  	case design.Object:
   193  		att := &design.AttributeDefinition{Type: actual}
   194  		if len(required) > 0 {
   195  			requiredVal := &dslengine.ValidationDefinition{Required: required}
   196  			att.Validation.Merge(requiredVal)
   197  		}
   198  		return GoTypeDef(att, tabs, false, private)
   199  	case *design.Hash:
   200  		return fmt.Sprintf(
   201  			"map[%s]%s",
   202  			GoTypeRef(actual.KeyType.Type, actual.KeyType.AllRequired(), tabs+1, private),
   203  			GoTypeRef(actual.ElemType.Type, actual.ElemType.AllRequired(), tabs+1, private),
   204  		)
   205  	case *design.UserTypeDefinition:
   206  		return Goify(actual.TypeName, !private)
   207  	case *design.MediaTypeDefinition:
   208  		if actual.IsError() {
   209  			return "error"
   210  		}
   211  		return Goify(actual.TypeName, !private)
   212  	default:
   213  		panic(fmt.Sprintf("goa bug: unknown type %#v", actual))
   214  	}
   215  }
   216  
   217  // GoNativeType returns the Go built-in type from which instances of t can be initialized.
   218  func GoNativeType(t design.DataType) string {
   219  	switch actual := t.(type) {
   220  	case design.Primitive:
   221  		switch actual.Kind() {
   222  		case design.BooleanKind:
   223  			return "bool"
   224  		case design.IntegerKind:
   225  			return "int"
   226  		case design.NumberKind:
   227  			return "float64"
   228  		case design.StringKind:
   229  			return "string"
   230  		case design.DateTimeKind:
   231  			return "time.Time"
   232  		case design.UUIDKind:
   233  			return "uuid.UUID"
   234  		case design.AnyKind:
   235  			return "interface{}"
   236  		case design.FileKind:
   237  			return "multipart.FileHeader"
   238  		default:
   239  			panic(fmt.Sprintf("goa bug: unknown primitive type %#v", actual))
   240  		}
   241  	case *design.Array:
   242  		return "[]" + GoNativeType(actual.ElemType.Type)
   243  	case design.Object:
   244  		return "map[string]interface{}"
   245  	case *design.Hash:
   246  		return fmt.Sprintf("map[%s]%s", GoNativeType(actual.KeyType.Type), GoNativeType(actual.ElemType.Type))
   247  	case *design.MediaTypeDefinition:
   248  		return GoNativeType(actual.Type)
   249  	case *design.UserTypeDefinition:
   250  		return GoNativeType(actual.Type)
   251  	default:
   252  		panic(fmt.Sprintf("goa bug: unknown type %#v", actual))
   253  	}
   254  }
   255  
   256  // GoTypeDesc returns the description of a type.  If no description is defined
   257  // for the type, one will be generated.
   258  func GoTypeDesc(t design.DataType, upper bool) string {
   259  	switch actual := t.(type) {
   260  	case *design.UserTypeDefinition:
   261  		if actual.Description != "" {
   262  			return strings.Replace(actual.Description, "\n", "\n// ", -1)
   263  		}
   264  
   265  		return Goify(actual.TypeName, upper) + " user type."
   266  	case *design.MediaTypeDefinition:
   267  		if actual.Description != "" {
   268  			return strings.Replace(actual.Description, "\n", "\n// ", -1)
   269  		}
   270  		name := Goify(actual.TypeName, upper)
   271  		if actual.View != "default" {
   272  			name += Goify(actual.View, true)
   273  		}
   274  
   275  		switch elem := actual.UserTypeDefinition.AttributeDefinition.Type.(type) {
   276  		case *design.Array:
   277  			elemName := GoTypeName(elem.ElemType.Type, nil, 0, !upper)
   278  			if actual.View != "default" {
   279  				elemName += Goify(actual.View, true)
   280  			}
   281  			return fmt.Sprintf("%s media type is a collection of %s.", name, elemName)
   282  		default:
   283  			return name + " media type."
   284  		}
   285  	default:
   286  		return ""
   287  	}
   288  }
   289  
   290  var commonInitialisms = map[string]bool{
   291  	"API":   true,
   292  	"ASCII": true,
   293  	"CPU":   true,
   294  	"CSS":   true,
   295  	"DNS":   true,
   296  	"EOF":   true,
   297  	"GUID":  true,
   298  	"HTML":  true,
   299  	"HTTP":  true,
   300  	"HTTPS": true,
   301  	"ID":    true,
   302  	"IP":    true,
   303  	"JMES":  true,
   304  	"JSON":  true,
   305  	"JWT":   true,
   306  	"LHS":   true,
   307  	"OK":    true,
   308  	"QPS":   true,
   309  	"RAM":   true,
   310  	"RHS":   true,
   311  	"RPC":   true,
   312  	"SLA":   true,
   313  	"SMTP":  true,
   314  	"SQL":   true,
   315  	"SSH":   true,
   316  	"TCP":   true,
   317  	"TLS":   true,
   318  	"TTL":   true,
   319  	"UDP":   true,
   320  	"UI":    true,
   321  	"UID":   true,
   322  	"UUID":  true,
   323  	"URI":   true,
   324  	"URL":   true,
   325  	"UTF8":  true,
   326  	"VM":    true,
   327  	"XML":   true,
   328  	"XSRF":  true,
   329  	"XSS":   true,
   330  }
   331  
   332  // removeTrailingInvalid removes trailing invalid identifiers from runes.
   333  func removeTrailingInvalid(runes []rune) []rune {
   334  	valid := len(runes) - 1
   335  	for ; valid >= 0 && !validIdentifier(runes[valid]); valid-- {
   336  	}
   337  
   338  	return runes[0 : valid+1]
   339  }
   340  
   341  // removeInvalidAtIndex removes consecutive invalid identifiers from runes starting at index i.
   342  func removeInvalidAtIndex(i int, runes []rune) []rune {
   343  	valid := i
   344  	for ; valid < len(runes) && !validIdentifier(runes[valid]); valid++ {
   345  	}
   346  
   347  	return append(runes[:i], runes[valid:]...)
   348  }
   349  
   350  // GoifyAtt honors any struct:field:name metadata set on the attribute and calls Goify with the tag
   351  // value if present or the given name otherwise.
   352  func GoifyAtt(att *design.AttributeDefinition, name string, firstUpper bool) string {
   353  	if tname, ok := att.Metadata["struct:field:name"]; ok {
   354  		if len(tname) > 0 {
   355  			name = tname[0]
   356  		}
   357  	}
   358  	return Goify(name, firstUpper)
   359  }
   360  
   361  // Goify makes a valid Go identifier out of any string.
   362  // It does that by removing any non letter and non digit character and by making sure the first
   363  // character is a letter or "_".
   364  // Goify produces a "CamelCase" version of the string, if firstUpper is true the first character
   365  // of the identifier is uppercase otherwise it's lowercase.
   366  func Goify(str string, firstUpper bool) string {
   367  	runes := []rune(str)
   368  
   369  	// remove trailing invalid identifiers (makes code below simpler)
   370  	runes = removeTrailingInvalid(runes)
   371  
   372  	w, i := 0, 0 // index of start of word, scan
   373  	for i+1 <= len(runes) {
   374  		eow := false // whether we hit the end of a word
   375  
   376  		// remove leading invalid identifiers
   377  		runes = removeInvalidAtIndex(i, runes)
   378  
   379  		if i+1 == len(runes) {
   380  			eow = true
   381  		} else if !validIdentifier(runes[i]) {
   382  			// get rid of it
   383  			runes = append(runes[:i], runes[i+1:]...)
   384  		} else if runes[i+1] == '_' {
   385  			// underscore; shift the remainder forward over any run of underscores
   386  			eow = true
   387  			n := 1
   388  			for i+n+1 < len(runes) && runes[i+n+1] == '_' {
   389  				n++
   390  			}
   391  			copy(runes[i+1:], runes[i+n+1:])
   392  			runes = runes[:len(runes)-n]
   393  		} else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) {
   394  			// lower->non-lower
   395  			eow = true
   396  		}
   397  		i++
   398  		if !eow {
   399  			continue
   400  		}
   401  
   402  		// [w,i] is a word.
   403  		word := string(runes[w:i])
   404  		// is it one of our initialisms?
   405  		if u := strings.ToUpper(word); commonInitialisms[u] {
   406  			if firstUpper {
   407  				u = strings.ToUpper(u)
   408  			} else if w == 0 {
   409  				u = strings.ToLower(u)
   410  			}
   411  
   412  			// All the common initialisms are ASCII,
   413  			// so we can replace the bytes exactly.
   414  			copy(runes[w:], []rune(u))
   415  		} else if w > 0 && strings.ToLower(word) == word {
   416  			// already all lowercase, and not the first word, so uppercase the first character.
   417  			runes[w] = unicode.ToUpper(runes[w])
   418  		} else if w == 0 && strings.ToLower(word) == word && firstUpper {
   419  			runes[w] = unicode.ToUpper(runes[w])
   420  		}
   421  		if w == 0 && !firstUpper {
   422  			runes[w] = unicode.ToLower(runes[w])
   423  		}
   424  		//advance to next word
   425  		w = i
   426  	}
   427  
   428  	return fixReserved(string(runes))
   429  }
   430  
   431  // Reserved golang keywords and package names
   432  var Reserved = map[string]bool{
   433  	"byte":       true,
   434  	"complex128": true,
   435  	"complex64":  true,
   436  	"float32":    true,
   437  	"float64":    true,
   438  	"int":        true,
   439  	"int16":      true,
   440  	"int32":      true,
   441  	"int64":      true,
   442  	"int8":       true,
   443  	"rune":       true,
   444  	"string":     true,
   445  	"uint16":     true,
   446  	"uint32":     true,
   447  	"uint64":     true,
   448  	"uint8":      true,
   449  
   450  	"break":       true,
   451  	"case":        true,
   452  	"chan":        true,
   453  	"const":       true,
   454  	"continue":    true,
   455  	"default":     true,
   456  	"defer":       true,
   457  	"else":        true,
   458  	"fallthrough": true,
   459  	"for":         true,
   460  	"func":        true,
   461  	"go":          true,
   462  	"goto":        true,
   463  	"if":          true,
   464  	"import":      true,
   465  	"interface":   true,
   466  	"map":         true,
   467  	"package":     true,
   468  	"range":       true,
   469  	"return":      true,
   470  	"select":      true,
   471  	"struct":      true,
   472  	"switch":      true,
   473  	"type":        true,
   474  	"var":         true,
   475  
   476  	// stdlib and goa packages used by generated code
   477  	"fmt":  true,
   478  	"http": true,
   479  	"json": true,
   480  	"os":   true,
   481  	"url":  true,
   482  	"time": true,
   483  }
   484  
   485  // validIdentifier returns true if the rune is a letter or number
   486  func validIdentifier(r rune) bool {
   487  	return unicode.IsLetter(r) || unicode.IsDigit(r)
   488  }
   489  
   490  // fixReserved appends an underscore on to Go reserved keywords.
   491  func fixReserved(w string) string {
   492  	if Reserved[w] {
   493  		w += "_"
   494  	}
   495  	return w
   496  }
   497  
   498  // GoTypeTransform produces Go code that initializes the data structure defined by target from an
   499  // instance of the data structure described by source. The algorithm matches object fields by name
   500  // or using the value of the "transform:key" attribute metadata when present.
   501  // The function returns an error if target is not compatible with source (different type, fields of
   502  // different type etc). It ignores fields in target that don't have a match in source.
   503  func GoTypeTransform(source, target *design.UserTypeDefinition, targetPkg, funcName string) (string, error) {
   504  	var impl string
   505  	var err error
   506  	switch {
   507  	case source.IsObject():
   508  		if !target.IsObject() {
   509  			return "", fmt.Errorf("source is an object but target type is %s", target.Type.Name())
   510  		}
   511  		impl, err = transformObject(source.ToObject(), target.ToObject(), targetPkg, target.TypeName, "source", "target", 1)
   512  	case source.IsArray():
   513  		if !target.IsArray() {
   514  			return "", fmt.Errorf("source is an array but target type is %s", target.Type.Name())
   515  		}
   516  		impl, err = transformArray(source.ToArray(), target.ToArray(), targetPkg, "source", "target", 1)
   517  	case source.IsHash():
   518  		if !target.IsHash() {
   519  			return "", fmt.Errorf("source is a hash but target type is %s", target.Type.Name())
   520  		}
   521  		impl, err = transformHash(source.ToHash(), target.ToHash(), targetPkg, "source", "target", 1)
   522  	default:
   523  		panic("cannot transform primitive types") // bug
   524  	}
   525  
   526  	if err != nil {
   527  		return "", err
   528  	}
   529  	t := GoTypeRef(target, nil, 0, false)
   530  	if strings.HasPrefix(t, "*") && len(targetPkg) > 0 {
   531  		t = fmt.Sprintf("*%s.%s", targetPkg, t[1:])
   532  	}
   533  	data := map[string]interface{}{
   534  		"Name":      funcName,
   535  		"Source":    source,
   536  		"Target":    target,
   537  		"TargetRef": t,
   538  		"TargetPkg": targetPkg,
   539  		"Impl":      impl,
   540  	}
   541  	return RunTemplate(transformT, data), nil
   542  }
   543  
   544  // GoTypeTransformName generates a valid Go identifer that is adequate for naming the type
   545  // transform function that creates an instance of the data structure described by target from an
   546  // instance of the data strucuture described by source.
   547  func GoTypeTransformName(source, target *design.UserTypeDefinition, suffix string) string {
   548  	return fmt.Sprintf("%sTo%s%s", Goify(source.TypeName, true), Goify(target.TypeName, true), Goify(suffix, true))
   549  }
   550  
   551  // WriteTabs is a helper function that writes count tabulation characters to buf.
   552  func WriteTabs(buf *bytes.Buffer, count int) {
   553  	for i := 0; i < count; i++ {
   554  		buf.WriteByte('\t')
   555  	}
   556  }
   557  
   558  // Tempvar generates a unique variable name.
   559  func Tempvar() string {
   560  	TempCount++
   561  	return fmt.Sprintf("tmp%d", TempCount)
   562  }
   563  
   564  // RunTemplate executs the given template with the given input and returns
   565  // the rendered string.
   566  func RunTemplate(tmpl *template.Template, data interface{}) string {
   567  	var b bytes.Buffer
   568  	err := tmpl.Execute(&b, data)
   569  	if err != nil {
   570  		panic(err) // should never happen, bug if it does.
   571  	}
   572  	return b.String()
   573  }
   574  
   575  func transformAttribute(source, target *design.AttributeDefinition, targetPkg, sctx, tctx string, depth int) (string, error) {
   576  	if source.Type.Kind() != target.Type.Kind() {
   577  		return "", fmt.Errorf("incompatible attribute types: %s is of type %s but %s is of type %s",
   578  			sctx, source.Type.Name(), tctx, target.Type.Name())
   579  	}
   580  	switch {
   581  	case source.Type.IsArray():
   582  		return transformArray(source.Type.ToArray(), target.Type.ToArray(), targetPkg, sctx, tctx, depth)
   583  	case source.Type.IsHash():
   584  		return transformHash(source.Type.ToHash(), target.Type.ToHash(), targetPkg, sctx, tctx, depth)
   585  	case source.Type.IsObject():
   586  		return transformObject(source.Type.ToObject(), target.Type.ToObject(), targetPkg, typeName(target), sctx, tctx, depth)
   587  	default:
   588  		return fmt.Sprintf("%s%s = %s\n", Tabs(depth), tctx, sctx), nil
   589  	}
   590  }
   591  
   592  func transformObject(source, target design.Object, targetPkg, targetType, sctx, tctx string, depth int) (string, error) {
   593  	attributeMap, err := computeMapping(source, target, sctx, tctx)
   594  	if err != nil {
   595  		return "", err
   596  	}
   597  
   598  	// First validate that all attributes are compatible - doing that in a template doesn't make
   599  	// sense.
   600  	for s, t := range attributeMap {
   601  		sourceAtt := source[s]
   602  		targetAtt := target[t]
   603  		if sourceAtt.Type.Kind() != targetAtt.Type.Kind() {
   604  			return "", fmt.Errorf("incompatible attribute types: %s.%s is of type %s but %s.%s is of type %s",
   605  				sctx, source.Name(), sourceAtt.Type.Name(), tctx, target.Name(), targetAtt.Type.Name())
   606  		}
   607  	}
   608  
   609  	// We're good - generate
   610  	data := map[string]interface{}{
   611  		"AttributeMap": attributeMap,
   612  		"Source":       source,
   613  		"Target":       target,
   614  		"TargetPkg":    targetPkg,
   615  		"TargetType":   targetType,
   616  		"SourceCtx":    sctx,
   617  		"TargetCtx":    tctx,
   618  		"Depth":        depth,
   619  	}
   620  	return RunTemplate(transformObjectT, data), nil
   621  }
   622  
   623  func transformArray(source, target *design.Array, targetPkg, sctx, tctx string, depth int) (string, error) {
   624  	if source.ElemType.Type.Kind() != target.ElemType.Type.Kind() {
   625  		return "", fmt.Errorf("incompatible attribute types: %s is an array with elements of type %s but %s is an array with elements of type %s",
   626  			sctx, source.ElemType.Type.Name(), tctx, target.ElemType.Type.Name())
   627  	}
   628  	data := map[string]interface{}{
   629  		"Source":    source,
   630  		"Target":    target,
   631  		"TargetPkg": targetPkg,
   632  		"SourceCtx": sctx,
   633  		"TargetCtx": tctx,
   634  		"Depth":     depth,
   635  	}
   636  	return RunTemplate(transformArrayT, data), nil
   637  }
   638  
   639  func transformHash(source, target *design.Hash, targetPkg, sctx, tctx string, depth int) (string, error) {
   640  	if source.ElemType.Type.Kind() != target.ElemType.Type.Kind() {
   641  		return "", fmt.Errorf("incompatible attribute types: %s is a hash with elements of type %s but %s is a hash with elements of type %s",
   642  			sctx, source.ElemType.Type.Name(), tctx, target.ElemType.Type.Name())
   643  	}
   644  	if source.KeyType.Type.Kind() != target.KeyType.Type.Kind() {
   645  		return "", fmt.Errorf("incompatible attribute types: %s is a hash with keys of type %s but %s is a hash with keys of type %s",
   646  			sctx, source.KeyType.Type.Name(), tctx, target.KeyType.Type.Name())
   647  	}
   648  	data := map[string]interface{}{
   649  		"Source":    source,
   650  		"Target":    target,
   651  		"TargetPkg": targetPkg,
   652  		"SourceCtx": sctx,
   653  		"TargetCtx": tctx,
   654  		"Depth":     depth,
   655  	}
   656  	return RunTemplate(transformHashT, data), nil
   657  }
   658  
   659  // computeMapping returns a map that indexes the target type definition object attributes with the
   660  // corresponding source type definition object attributes. An attribute is associated with another
   661  // attribute if their map key match. The map key of an attribute is the value of the TransformMapKey
   662  // metadata if present, the attribute name otherwise.
   663  // The function returns an error if the TransformMapKey metadata is malformed (has no value).
   664  func computeMapping(source, target design.Object, sctx, tctx string) (map[string]string, error) {
   665  	attributeMap := make(map[string]string)
   666  	sourceMap := make(map[string]string)
   667  	targetMap := make(map[string]string)
   668  	for name, att := range source {
   669  		key := name
   670  		if keys, ok := att.Metadata[TransformMapKey]; ok {
   671  			if len(keys) == 0 {
   672  				return nil, fmt.Errorf("invalid metadata transform key: missing value on attribute %s of %s", name, sctx)
   673  			}
   674  			key = keys[0]
   675  		}
   676  		sourceMap[key] = name
   677  	}
   678  	for name, att := range target {
   679  		key := name
   680  		if keys, ok := att.Metadata[TransformMapKey]; ok {
   681  			if len(keys) == 0 {
   682  				return nil, fmt.Errorf("invalid metadata transform key: missing value on attribute %s of %s", name, tctx)
   683  			}
   684  			key = keys[0]
   685  		}
   686  		targetMap[key] = name
   687  	}
   688  	for key, attName := range sourceMap {
   689  		if targetAtt, ok := targetMap[key]; ok {
   690  			attributeMap[attName] = targetAtt
   691  		}
   692  	}
   693  	return attributeMap, nil
   694  }
   695  
   696  // toSlice returns Go code that represents the given slice.
   697  func toSlice(val []interface{}) string {
   698  	elems := make([]string, len(val))
   699  	for i, v := range val {
   700  		elems[i] = fmt.Sprintf("%#v", v)
   701  	}
   702  	return fmt.Sprintf("[]interface{}{%s}", strings.Join(elems, ", "))
   703  }
   704  
   705  // typeName returns the type name of the given attribute if it is a named type, empty string otherwise.
   706  func typeName(att *design.AttributeDefinition) (name string) {
   707  	if ut, ok := att.Type.(*design.UserTypeDefinition); ok {
   708  		name = Goify(ut.TypeName, true)
   709  	} else if mt, ok := att.Type.(*design.MediaTypeDefinition); ok {
   710  		name = Goify(mt.TypeName, true)
   711  	}
   712  	return
   713  }
   714  
   715  const transformTmpl = `func {{ .Name }}(source {{ gotyperef .Source nil 0 false }}) (target {{ .TargetRef }}) {
   716  {{ .Impl }}	return
   717  }
   718  `
   719  
   720  const transformObjectTmpl = `{{ tabs .Depth }}{{ .TargetCtx }} = new({{ if .TargetPkg }}{{ .TargetPkg }}.{{ end }}{{ if .TargetType }}{{ .TargetType }}{{ else }}{{ gotyperef .Target.Type .Target.AllRequired 1 false }}{{ end }})
   721  {{ range $source, $target := .AttributeMap }}{{/*
   722  */}}{{ $sourceAtt := index $.Source $source }}{{ $targetAtt := index $.Target $target }}{{/*
   723  */}}{{ $source := goify $source true }}{{ $target := goify $target true }}{{/*
   724  */}}{{     if $sourceAtt.Type.IsArray }}{{ transformArray  $sourceAtt.Type.ToArray  $targetAtt.Type.ToArray  $.TargetPkg (printf "%s.%s" $.SourceCtx $source) (printf "%s.%s" $.TargetCtx $target) $.Depth }}{{/*
   725  */}}{{ else if $sourceAtt.Type.IsHash }}{{  transformHash   $sourceAtt.Type.ToHash   $targetAtt.Type.ToHash   $.TargetPkg (printf "%s.%s" $.SourceCtx $source) (printf "%s.%s" $.TargetCtx $target) $.Depth }}{{/*
   726  */}}{{ else if $sourceAtt.Type.IsObject }}{{ transformObject $sourceAtt.Type.ToObject $targetAtt.Type.ToObject $.TargetPkg (typeName $targetAtt) (printf "%s.%s" $.SourceCtx $source) (printf "%s.%s" $.TargetCtx $target) $.Depth }}{{/*
   727  */}}{{ else }}{{ tabs $.Depth }}{{ $.TargetCtx }}.{{ $target }} = {{ $.SourceCtx }}.{{ $source }}
   728  {{ end }}{{ end }}`
   729  
   730  const transformArrayTmpl = `{{ tabs .Depth }}{{ .TargetCtx}} = make([]{{ gotyperef .Target.ElemType.Type nil 0 false }}, len({{ .SourceCtx }}))
   731  {{ tabs .Depth }}for i, v := range {{ .SourceCtx }} {
   732  {{ transformAttribute .Source.ElemType .Target.ElemType .TargetPkg (printf "%s[i]" .SourceCtx) (printf "%s[i]" .TargetCtx) (add .Depth 1) }}{{/*
   733  */}}{{ tabs .Depth }}}
   734  `
   735  
   736  const transformHashTmpl = `{{ tabs .Depth }}{{ .TargetCtx }} = make(map[{{ gotyperef .Target.KeyType.Type nil 0 false }}]{{ gotyperef .Target.ElemType.Type nil 0 false }}, len({{ .SourceCtx }}))
   737  {{ tabs .Depth }}for k, v := range {{ .SourceCtx }} {
   738  {{ tabs .Depth }}	var tk {{ gotyperef .Target.KeyType.Type nil 0 false }}
   739  {{ transformAttribute .Source.KeyType .Target.KeyType .TargetPkg "k" "tk" (add .Depth 1) }}{{/*
   740  */}}{{ tabs .Depth }}	var tv {{ gotyperef .Target.ElemType.Type nil 0 false }}
   741  {{ transformAttribute .Source.ElemType .Target.ElemType .TargetPkg "v" "tv" (add .Depth 1) }}{{/*
   742  */}}{{ tabs .Depth }}	{{ .TargetCtx }}[tk] = tv
   743  {{ tabs .Depth }}}
   744  `