2015-04-17 12:16:33 +00:00
/ *
2015-05-01 16:19:44 +00:00
Copyright 2015 The Kubernetes Authors All rights reserved .
2015-04-17 12:16:33 +00:00
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 .
* /
2015-05-07 11:46:54 +00:00
package runtime
2015-04-17 12:16:33 +00:00
import (
"fmt"
"io"
2015-12-23 06:53:03 +00:00
"log"
2015-07-30 19:15:14 +00:00
"path"
2015-04-17 12:16:33 +00:00
"reflect"
2015-12-23 06:53:03 +00:00
goruntime "runtime"
2015-04-23 14:05:14 +00:00
"sort"
2015-04-17 12:16:33 +00:00
"strings"
2015-05-07 11:46:54 +00:00
2015-11-19 14:24:16 +00:00
"k8s.io/kubernetes/pkg/api/unversioned"
2015-09-09 17:45:01 +00:00
"k8s.io/kubernetes/pkg/util/sets"
2015-04-17 12:16:33 +00:00
)
2015-05-15 11:01:47 +00:00
type ConversionGenerator interface {
2015-11-18 15:34:16 +00:00
GenerateConversionsForType ( groupVersion unversioned . GroupVersion , reflection reflect . Type ) error
2015-04-17 12:16:33 +00:00
WriteConversionFunctions ( w io . Writer ) error
2015-07-30 19:15:14 +00:00
RegisterConversionFunctions ( w io . Writer , pkg string ) error
AddImport ( pkg string ) string
2015-09-09 17:45:01 +00:00
RepackImports ( exclude sets . String )
2015-07-30 19:15:14 +00:00
WriteImports ( w io . Writer ) error
2015-04-17 12:16:33 +00:00
OverwritePackage ( pkg , overwrite string )
2015-07-30 19:15:14 +00:00
AssumePrivateConversions ( )
2015-04-17 12:16:33 +00:00
}
2016-01-25 23:47:26 +00:00
func NewConversionGenerator ( scheme * Scheme , targetPkg string ) ConversionGenerator {
2015-07-30 19:15:14 +00:00
g := & conversionGenerator {
2015-12-23 06:53:03 +00:00
scheme : scheme ,
nameFormat : "Convert_%s_%s_To_%s_%s" ,
generatedNamePrefix : "auto" ,
targetPkg : targetPkg ,
2016-02-08 22:19:57 +00:00
publicFuncs : make ( map [ typePair ] functionName ) ,
2015-04-17 12:16:33 +00:00
convertibles : make ( map [ reflect . Type ] reflect . Type ) ,
2015-08-31 05:47:41 +00:00
overridden : make ( map [ reflect . Type ] bool ) ,
2015-04-17 12:16:33 +00:00
pkgOverwrites : make ( map [ string ] string ) ,
2015-07-30 19:15:14 +00:00
imports : make ( map [ string ] string ) ,
shortImports : make ( map [ string ] string ) ,
2015-04-17 12:16:33 +00:00
}
2015-07-30 19:15:14 +00:00
g . targetPackage ( targetPkg )
g . AddImport ( "reflect" )
2015-08-05 22:03:47 +00:00
g . AddImport ( "k8s.io/kubernetes/pkg/conversion" )
2015-07-30 19:15:14 +00:00
return g
2015-04-17 12:16:33 +00:00
}
var complexTypes [ ] reflect . Kind = [ ] reflect . Kind { reflect . Map , reflect . Ptr , reflect . Slice , reflect . Interface , reflect . Struct }
2016-02-08 22:19:57 +00:00
type functionName struct {
name string
packageName string
}
2015-05-15 11:01:47 +00:00
type conversionGenerator struct {
2016-01-25 23:47:26 +00:00
scheme * Scheme
2015-12-23 06:53:03 +00:00
nameFormat string
generatedNamePrefix string
targetPkg string
2016-02-08 22:19:57 +00:00
publicFuncs map [ typePair ] functionName
2015-04-17 12:16:33 +00:00
convertibles map [ reflect . Type ] reflect . Type
2015-08-31 05:47:41 +00:00
overridden map [ reflect . Type ] bool
2015-04-17 12:16:33 +00:00
// If pkgOverwrites is set for a given package name, that package name
// will be replaced while writing conversion function. If empty, package
// name will be omitted.
pkgOverwrites map [ string ] string
2015-07-30 19:15:14 +00:00
// map of package names to shortname
imports map [ string ] string
// map of short names to package names
shortImports map [ string ] string
2015-05-15 11:01:47 +00:00
// A buffer that is used for storing lines that needs to be written.
linesToPrint [ ] string
2015-07-30 19:15:14 +00:00
// if true, we assume conversions on the scheme are not available to us in the current package
assumePrivateConversions bool
}
func ( g * conversionGenerator ) AssumePrivateConversions ( ) {
g . assumePrivateConversions = true
}
func ( g * conversionGenerator ) AddImport ( pkg string ) string {
return g . addImportByPath ( pkg )
2015-04-17 12:16:33 +00:00
}
2015-11-18 15:34:16 +00:00
func ( g * conversionGenerator ) GenerateConversionsForType ( gv unversioned . GroupVersion , reflection reflect . Type ) error {
2015-04-17 12:16:33 +00:00
kind := reflection . Name ( )
2015-11-17 18:21:32 +00:00
// TODO this is equivalent to what it did before, but it needs to be fixed for the proper group
2015-12-21 05:08:33 +00:00
internalVersion := gv
internalVersion . Version = APIVersionInternal
2015-11-19 14:24:16 +00:00
2016-01-25 23:47:26 +00:00
internalObj , err := g . scheme . New ( internalVersion . WithKind ( kind ) )
2015-04-17 12:16:33 +00:00
if err != nil {
return fmt . Errorf ( "cannot create an object of type %v in internal version" , kind )
}
internalObjType := reflect . TypeOf ( internalObj )
if internalObjType . Kind ( ) != reflect . Ptr {
2015-08-08 01:52:23 +00:00
return fmt . Errorf ( "created object should be of type Ptr: %v" , internalObjType . Kind ( ) )
2015-04-17 12:16:33 +00:00
}
2015-05-20 17:59:48 +00:00
inErr := g . generateConversionsBetween ( reflection , internalObjType . Elem ( ) )
outErr := g . generateConversionsBetween ( internalObjType . Elem ( ) , reflection )
if inErr != nil || outErr != nil {
return fmt . Errorf ( "errors: %v, %v" , inErr , outErr )
}
return nil
2015-04-17 12:16:33 +00:00
}
2015-11-17 02:44:10 +00:00
// primitiveConversion returns true if the two types can be converted via a cast.
func primitiveConversion ( inType , outType reflect . Type ) ( string , bool ) {
switch inType . Kind ( ) {
case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 ,
reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 ,
reflect . Float32 , reflect . Float64 :
switch outType . Kind ( ) {
case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 ,
reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 ,
reflect . Float32 , reflect . Float64 :
return outType . Name ( ) , true
}
}
return "" , false
}
2015-05-15 11:01:47 +00:00
func ( g * conversionGenerator ) generateConversionsBetween ( inType , outType reflect . Type ) error {
2015-05-06 07:05:58 +00:00
existingConversion := g . scheme . Converter ( ) . HasConversionFunc ( inType , outType ) && g . scheme . Converter ( ) . HasConversionFunc ( outType , inType )
2015-04-17 12:16:33 +00:00
// Avoid processing the same type multiple times.
if value , found := g . convertibles [ inType ] ; found {
if value != outType {
return fmt . Errorf ( "multiple possible convertibles for %v" , inType )
}
return nil
}
if inType == outType {
2015-12-23 06:53:03 +00:00
switch inType . Kind ( ) {
case reflect . Ptr :
return g . generateConversionsBetween ( inType . Elem ( ) , inType . Elem ( ) )
case reflect . Struct :
// pointers to structs invoke new(inType)
g . addImportByPath ( inType . PkgPath ( ) )
}
g . rememberConversionFunction ( inType , inType , false )
2015-04-17 12:16:33 +00:00
// Don't generate conversion methods for the same type.
return nil
}
2015-11-17 02:44:10 +00:00
if _ , ok := primitiveConversion ( inType , outType ) ; ok {
return nil
}
2015-04-17 12:16:33 +00:00
if inType . Kind ( ) != outType . Kind ( ) {
2015-05-14 10:57:02 +00:00
if existingConversion {
2015-12-23 06:53:03 +00:00
g . rememberConversionFunction ( inType , outType , false )
g . rememberConversionFunction ( outType , inType , false )
2015-05-14 10:57:02 +00:00
return nil
}
2015-04-17 12:16:33 +00:00
return fmt . Errorf ( "cannot convert types of different kinds: %v %v" , inType , outType )
}
2015-07-30 19:15:14 +00:00
g . addImportByPath ( inType . PkgPath ( ) )
g . addImportByPath ( outType . PkgPath ( ) )
2015-05-06 07:05:58 +00:00
// We should be able to generate conversions both sides.
2015-04-17 12:16:33 +00:00
switch inType . Kind ( ) {
case reflect . Map :
2015-05-06 07:05:58 +00:00
inErr := g . generateConversionsForMap ( inType , outType )
outErr := g . generateConversionsForMap ( outType , inType )
if ! existingConversion && ( inErr != nil || outErr != nil ) {
return inErr
}
// We don't add it to g.convertibles - maps should be handled correctly
// inside appropriate conversion functions.
return nil
2015-04-17 12:16:33 +00:00
case reflect . Ptr :
2015-05-06 07:05:58 +00:00
inErr := g . generateConversionsBetween ( inType . Elem ( ) , outType . Elem ( ) )
outErr := g . generateConversionsBetween ( outType . Elem ( ) , inType . Elem ( ) )
if ! existingConversion && ( inErr != nil || outErr != nil ) {
return inErr
}
// We don't add it to g.convertibles - maps should be handled correctly
// inside appropriate conversion functions.
return nil
2015-04-17 12:16:33 +00:00
case reflect . Slice :
2015-05-06 07:05:58 +00:00
inErr := g . generateConversionsForSlice ( inType , outType )
outErr := g . generateConversionsForSlice ( outType , inType )
if ! existingConversion && ( inErr != nil || outErr != nil ) {
return inErr
}
// We don't add it to g.convertibles - slices should be handled correctly
// inside appropriate conversion functions.
2015-04-17 12:16:33 +00:00
return nil
2015-05-06 07:05:58 +00:00
case reflect . Interface :
// TODO(wojtek-t): Currently we don't support converting interfaces.
return fmt . Errorf ( "interfaces are not supported" )
2015-04-17 12:16:33 +00:00
case reflect . Struct :
2015-05-06 07:05:58 +00:00
inErr := g . generateConversionsForStruct ( inType , outType )
outErr := g . generateConversionsForStruct ( outType , inType )
if ! existingConversion && ( inErr != nil || outErr != nil ) {
return inErr
}
2015-12-23 06:53:03 +00:00
g . rememberConversionFunction ( inType , outType , true )
2015-08-31 05:47:41 +00:00
if existingConversion {
g . overridden [ inType ] = true
2015-05-06 07:05:58 +00:00
}
2015-08-31 05:47:41 +00:00
g . convertibles [ inType ] = outType
2015-05-06 07:05:58 +00:00
return nil
2015-04-17 12:16:33 +00:00
default :
// All simple types should be handled correctly with default conversion.
return nil
}
}
func isComplexType ( reflection reflect . Type ) bool {
for _ , complexType := range complexTypes {
if complexType == reflection . Kind ( ) {
return true
}
}
return false
}
2015-12-23 06:53:03 +00:00
func ( g * conversionGenerator ) rememberConversionFunction ( inType , outType reflect . Type , willGenerate bool ) {
if _ , ok := g . publicFuncs [ typePair { inType , outType } ] ; ok {
return
}
if v , ok := g . scheme . Converter ( ) . ConversionFuncValue ( inType , outType ) ; ok {
if fn := goruntime . FuncForPC ( v . Pointer ( ) ) ; fn != nil {
name := fn . Name ( )
var p , n string
if last := strings . LastIndex ( name , "." ) ; last != - 1 {
p = name [ : last ]
n = name [ last + 1 : ]
} else {
n = name
}
if isPublic ( n ) {
2016-02-08 22:19:57 +00:00
g . AddImport ( p )
g . publicFuncs [ typePair { inType , outType } ] = functionName { name : n , packageName : p }
2015-12-23 06:53:03 +00:00
} else {
log . Printf ( "WARNING: Cannot generate conversion %v -> %v, method %q is private" , inType , outType , fn . Name ( ) )
}
} else {
log . Printf ( "WARNING: Cannot generate conversion %v -> %v, method is not accessible" , inType , outType )
}
} else if willGenerate {
2016-02-08 22:19:57 +00:00
g . publicFuncs [ typePair { inType , outType } ] = functionName { name : g . conversionFunctionName ( inType , outType ) }
2015-12-23 06:53:03 +00:00
}
}
func isPublic ( name string ) bool {
return strings . ToUpper ( name [ : 1 ] ) == name [ : 1 ]
}
2015-05-15 11:01:47 +00:00
func ( g * conversionGenerator ) generateConversionsForMap ( inType , outType reflect . Type ) error {
2015-04-17 12:16:33 +00:00
inKey := inType . Key ( )
outKey := outType . Key ( )
2015-07-30 19:15:14 +00:00
g . addImportByPath ( inKey . PkgPath ( ) )
g . addImportByPath ( outKey . PkgPath ( ) )
2015-04-17 12:16:33 +00:00
if err := g . generateConversionsBetween ( inKey , outKey ) ; err != nil {
return err
}
inValue := inType . Elem ( )
outValue := outType . Elem ( )
2015-08-10 18:00:20 +00:00
g . addImportByPath ( inValue . PkgPath ( ) )
g . addImportByPath ( outValue . PkgPath ( ) )
2015-04-17 12:16:33 +00:00
if err := g . generateConversionsBetween ( inValue , outValue ) ; err != nil {
return err
}
return nil
}
2015-05-15 11:01:47 +00:00
func ( g * conversionGenerator ) generateConversionsForSlice ( inType , outType reflect . Type ) error {
2015-04-17 12:16:33 +00:00
inElem := inType . Elem ( )
outElem := outType . Elem ( )
2015-12-19 00:04:31 +00:00
// slice conversion requires the package for the destination type in order to instantiate the map
g . addImportByPath ( outElem . PkgPath ( ) )
2015-04-17 12:16:33 +00:00
if err := g . generateConversionsBetween ( inElem , outElem ) ; err != nil {
return err
}
return nil
}
2015-05-15 11:01:47 +00:00
func ( g * conversionGenerator ) generateConversionsForStruct ( inType , outType reflect . Type ) error {
2015-04-17 12:16:33 +00:00
for i := 0 ; i < inType . NumField ( ) ; i ++ {
inField := inType . Field ( i )
outField , found := outType . FieldByName ( inField . Name )
if ! found {
return fmt . Errorf ( "couldn't find a corresponding field %v in %v" , inField . Name , outType )
}
if isComplexType ( inField . Type ) {
if err := g . generateConversionsBetween ( inField . Type , outField . Type ) ; err != nil {
return err
}
}
}
return nil
}
2015-05-15 11:01:47 +00:00
// A buffer of lines that will be written.
type bufferedLine struct {
line string
indentation int
}
type buffer struct {
lines [ ] bufferedLine
}
func newBuffer ( ) * buffer {
return & buffer {
lines : make ( [ ] bufferedLine , 0 ) ,
}
}
func ( b * buffer ) addLine ( line string , indent int ) {
b . lines = append ( b . lines , bufferedLine { line , indent } )
}
func ( b * buffer ) flushLines ( w io . Writer ) error {
for _ , line := range b . lines {
indentation := strings . Repeat ( "\t" , line . indentation )
fullLine := fmt . Sprintf ( "%s%s" , indentation , line . line )
if _ , err := io . WriteString ( w , fullLine ) ; err != nil {
return err
}
}
return nil
}
2015-04-23 14:05:14 +00:00
type byName [ ] reflect . Type
func ( s byName ) Len ( ) int {
return len ( s )
}
func ( s byName ) Less ( i , j int ) bool {
2015-05-20 17:59:48 +00:00
fullNameI := s [ i ] . PkgPath ( ) + "/" + s [ i ] . Name ( )
fullNameJ := s [ j ] . PkgPath ( ) + "/" + s [ j ] . Name ( )
return fullNameI < fullNameJ
2015-04-23 14:05:14 +00:00
}
func ( s byName ) Swap ( i , j int ) {
s [ i ] , s [ j ] = s [ j ] , s [ i ]
}
2015-07-30 19:15:14 +00:00
func ( g * conversionGenerator ) targetPackage ( pkg string ) {
g . imports [ pkg ] = ""
g . shortImports [ "" ] = pkg
}
2015-09-09 17:45:01 +00:00
func ( g * conversionGenerator ) RepackImports ( exclude sets . String ) {
2015-07-30 19:15:14 +00:00
var packages [ ] string
for key := range g . imports {
packages = append ( packages , key )
}
sort . Strings ( packages )
g . imports = make ( map [ string ] string )
g . shortImports = make ( map [ string ] string )
g . targetPackage ( g . targetPkg )
for _ , pkg := range packages {
if ! exclude . Has ( pkg ) {
g . addImportByPath ( pkg )
}
}
}
func ( g * conversionGenerator ) WriteImports ( w io . Writer ) error {
var packages [ ] string
for key := range g . imports {
packages = append ( packages , key )
}
sort . Strings ( packages )
buffer := newBuffer ( )
indent := 0
buffer . addLine ( "import (\n" , indent )
for _ , importPkg := range packages {
if len ( importPkg ) == 0 {
continue
}
if len ( g . imports [ importPkg ] ) == 0 {
continue
}
buffer . addLine ( fmt . Sprintf ( "%s \"%s\"\n" , g . imports [ importPkg ] , importPkg ) , indent + 1 )
}
buffer . addLine ( ")\n" , indent )
buffer . addLine ( "\n" , indent )
if err := buffer . flushLines ( w ) ; err != nil {
return err
}
return nil
}
2015-05-15 11:01:47 +00:00
func ( g * conversionGenerator ) WriteConversionFunctions ( w io . Writer ) error {
2015-04-23 14:05:14 +00:00
// It's desired to print conversion functions always in the same order
// (e.g. for better tracking of what has really been added).
var keys [ ] reflect . Type
for key := range g . convertibles {
keys = append ( keys , key )
}
sort . Sort ( byName ( keys ) )
2015-05-15 11:01:47 +00:00
buffer := newBuffer ( )
2015-04-17 12:16:33 +00:00
indent := 0
2015-04-23 14:05:14 +00:00
for _ , inType := range keys {
outType := g . convertibles [ inType ]
2015-04-17 12:16:33 +00:00
// All types in g.convertibles are structs.
if inType . Kind ( ) != reflect . Struct {
return fmt . Errorf ( "non-struct conversions are not-supported" )
}
2015-05-15 11:01:47 +00:00
if err := g . writeConversionForType ( buffer , inType , outType , indent ) ; err != nil {
2015-04-17 12:16:33 +00:00
return err
}
}
2015-05-15 11:01:47 +00:00
if err := buffer . flushLines ( w ) ; err != nil {
return err
}
2015-04-17 12:16:33 +00:00
return nil
}
2015-07-30 19:15:14 +00:00
func ( g * conversionGenerator ) writeRegisterHeader ( b * buffer , pkg string , indent int ) {
2015-05-18 20:40:10 +00:00
b . addLine ( "func init() {\n" , indent )
2015-07-30 19:15:14 +00:00
b . addLine ( fmt . Sprintf ( "err := %s.AddGeneratedConversionFuncs(\n" , pkg ) , indent + 1 )
2015-05-18 20:40:10 +00:00
}
func ( g * conversionGenerator ) writeRegisterFooter ( b * buffer , indent int ) {
b . addLine ( ")\n" , indent + 1 )
b . addLine ( "if err != nil {\n" , indent + 1 )
b . addLine ( "// If one of the conversion functions is malformed, detect it immediately.\n" , indent + 2 )
b . addLine ( "panic(err)\n" , indent + 2 )
b . addLine ( "}\n" , indent + 1 )
b . addLine ( "}\n" , indent )
b . addLine ( "\n" , indent )
}
2015-07-30 19:15:14 +00:00
func ( g * conversionGenerator ) RegisterConversionFunctions ( w io . Writer , pkg string ) error {
2015-04-17 12:16:33 +00:00
// Write conversion function names alphabetically ordered.
var names [ ] string
for inType , outType := range g . convertibles {
2015-08-31 05:47:41 +00:00
names = append ( names , g . generatedFunctionName ( inType , outType ) )
2015-04-17 12:16:33 +00:00
}
sort . Strings ( names )
2015-05-15 11:01:47 +00:00
buffer := newBuffer ( )
2015-05-18 20:40:10 +00:00
indent := 0
2015-07-30 19:15:14 +00:00
g . writeRegisterHeader ( buffer , pkg , indent )
2015-04-17 12:16:33 +00:00
for _ , name := range names {
2015-05-18 20:40:10 +00:00
buffer . addLine ( fmt . Sprintf ( "%s,\n" , name ) , indent + 2 )
2015-05-15 11:01:47 +00:00
}
2015-05-18 20:40:10 +00:00
g . writeRegisterFooter ( buffer , indent )
2015-05-15 11:01:47 +00:00
if err := buffer . flushLines ( w ) ; err != nil {
return err
2015-04-17 12:16:33 +00:00
}
return nil
}
2015-07-30 19:15:14 +00:00
func ( g * conversionGenerator ) addImportByPath ( pkg string ) string {
if name , ok := g . imports [ pkg ] ; ok {
return name
}
name := path . Base ( pkg )
if _ , ok := g . shortImports [ name ] ; ! ok {
g . imports [ pkg ] = name
g . shortImports [ name ] = pkg
return name
}
if dirname := path . Base ( path . Dir ( pkg ) ) ; len ( dirname ) > 0 {
name = dirname + name
if _ , ok := g . shortImports [ name ] ; ! ok {
g . imports [ pkg ] = name
g . shortImports [ name ] = pkg
return name
}
if subdirname := path . Base ( path . Dir ( path . Dir ( pkg ) ) ) ; len ( subdirname ) > 0 {
name = subdirname + name
if _ , ok := g . shortImports [ name ] ; ! ok {
g . imports [ pkg ] = name
g . shortImports [ name ] = pkg
return name
}
}
}
for i := 2 ; i < 100 ; i ++ {
generatedName := fmt . Sprintf ( "%s%d" , name , i )
if _ , ok := g . shortImports [ generatedName ] ; ! ok {
g . imports [ pkg ] = generatedName
g . shortImports [ generatedName ] = pkg
return generatedName
}
}
panic ( fmt . Sprintf ( "unable to find a unique name for the package path %q: %v" , pkg , g . shortImports ) )
}
2015-05-15 11:01:47 +00:00
func ( g * conversionGenerator ) typeName ( inType reflect . Type ) string {
2015-04-17 12:16:33 +00:00
switch inType . Kind ( ) {
case reflect . Slice :
return fmt . Sprintf ( "[]%s" , g . typeName ( inType . Elem ( ) ) )
2015-04-17 12:16:33 +00:00
case reflect . Ptr :
return fmt . Sprintf ( "*%s" , g . typeName ( inType . Elem ( ) ) )
2015-07-30 19:15:14 +00:00
case reflect . Map :
if len ( inType . Name ( ) ) == 0 {
return fmt . Sprintf ( "map[%s]%s" , g . typeName ( inType . Key ( ) ) , g . typeName ( inType . Elem ( ) ) )
}
fallthrough
2015-04-17 12:16:33 +00:00
default :
2015-07-30 19:15:14 +00:00
pkg , name := inType . PkgPath ( ) , inType . Name ( )
if len ( name ) == 0 && inType . Kind ( ) == reflect . Struct {
return "struct{}"
}
if len ( pkg ) == 0 {
2015-04-17 12:16:33 +00:00
// Default package.
2015-07-30 19:15:14 +00:00
return name
2015-04-17 12:16:33 +00:00
}
2015-07-30 19:15:14 +00:00
if val , found := g . pkgOverwrites [ pkg ] ; found {
pkg = val
}
if len ( pkg ) == 0 {
return name
2015-06-17 19:48:27 +00:00
}
2015-07-30 19:15:14 +00:00
short := g . addImportByPath ( pkg )
if len ( short ) > 0 {
return fmt . Sprintf ( "%s.%s" , short , name )
}
return name
2015-04-17 12:16:33 +00:00
}
}
2015-05-15 11:01:47 +00:00
func ( g * conversionGenerator ) writeDefaultingFunc ( b * buffer , inType reflect . Type , indent int ) error {
2015-04-28 14:50:55 +00:00
getStmt := "if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {\n"
2015-05-15 11:01:47 +00:00
b . addLine ( getStmt , indent )
2015-04-28 14:50:55 +00:00
callFormat := "defaulting.(func(*%s))(in)\n"
callStmt := fmt . Sprintf ( callFormat , g . typeName ( inType ) )
2015-05-15 11:01:47 +00:00
b . addLine ( callStmt , indent + 1 )
b . addLine ( "}\n" , indent )
2015-04-28 14:50:55 +00:00
return nil
}
2015-04-17 12:16:33 +00:00
func packageForName ( inType reflect . Type ) string {
if inType . PkgPath ( ) == "" {
return ""
}
slices := strings . Split ( inType . PkgPath ( ) , "/" )
return slices [ len ( slices ) - 1 ]
}
2015-05-15 11:01:47 +00:00
func ( g * conversionGenerator ) conversionFunctionName ( inType , outType reflect . Type ) string {
2015-12-23 06:53:03 +00:00
funcNameFormat := g . nameFormat
2015-04-17 12:16:33 +00:00
inPkg := packageForName ( inType )
outPkg := packageForName ( outType )
funcName := fmt . Sprintf ( funcNameFormat , inPkg , inType . Name ( ) , outPkg , outType . Name ( ) )
return funcName
}
2015-12-23 06:53:03 +00:00
func ( g * conversionGenerator ) conversionFunctionCall ( inType , outType reflect . Type , scopeName string , args ... string ) string {
if named , ok := g . publicFuncs [ typePair { inType , outType } ] ; ok {
args [ len ( args ) - 1 ] = scopeName
2016-02-08 22:19:57 +00:00
name := named . name
localPackageName , ok := g . imports [ named . packageName ]
if ! ok {
panic ( fmt . Sprintf ( "have not defined an import for %s" , named . packageName ) )
}
if len ( named . packageName ) > 0 && len ( localPackageName ) > 0 {
name = localPackageName + "." + name
}
return fmt . Sprintf ( "%s(%s)" , name , strings . Join ( args , ", " ) )
2015-12-23 06:53:03 +00:00
}
log . Printf ( "WARNING: Using reflection to convert %v -> %v (no public conversion)" , inType , outType )
return fmt . Sprintf ( "%s.Convert(%s)" , scopeName , strings . Join ( args , ", " ) )
}
2015-08-31 05:47:41 +00:00
func ( g * conversionGenerator ) generatedFunctionName ( inType , outType reflect . Type ) string {
2015-12-23 06:53:03 +00:00
return g . generatedNamePrefix + g . conversionFunctionName ( inType , outType )
2015-08-31 05:47:41 +00:00
}
2015-05-15 11:01:47 +00:00
func ( g * conversionGenerator ) writeHeader ( b * buffer , name , inType , outType string , indent int ) {
2015-04-17 12:16:33 +00:00
format := "func %s(in *%s, out *%s, s conversion.Scope) error {\n"
stmt := fmt . Sprintf ( format , name , inType , outType )
2015-05-15 11:01:47 +00:00
b . addLine ( stmt , indent )
2015-04-17 12:16:33 +00:00
}
2015-05-15 11:01:47 +00:00
func ( g * conversionGenerator ) writeFooter ( b * buffer , indent int ) {
b . addLine ( "return nil\n" , indent + 1 )
b . addLine ( "}\n" , indent )
2015-04-17 12:16:33 +00:00
}
2015-05-15 11:01:47 +00:00
func ( g * conversionGenerator ) writeConversionForMap ( b * buffer , inField , outField reflect . StructField , indent int ) error {
2015-04-17 12:16:33 +00:00
ifFormat := "if in.%s != nil {\n"
ifStmt := fmt . Sprintf ( ifFormat , inField . Name )
2015-05-15 11:01:47 +00:00
b . addLine ( ifStmt , indent )
2015-04-17 12:16:33 +00:00
makeFormat := "out.%s = make(%s)\n"
makeStmt := fmt . Sprintf ( makeFormat , outField . Name , g . typeName ( outField . Type ) )
2015-05-15 11:01:47 +00:00
b . addLine ( makeStmt , indent + 1 )
2015-04-17 12:16:33 +00:00
forFormat := "for key, val := range in.%s {\n"
forStmt := fmt . Sprintf ( forFormat , inField . Name )
2015-05-15 11:01:47 +00:00
b . addLine ( forStmt , indent + 1 )
2015-04-17 12:16:33 +00:00
// Whether we need to explicitly create a new value.
newValue := false
if isComplexType ( inField . Type . Elem ( ) ) || ! inField . Type . Elem ( ) . ConvertibleTo ( outField . Type . Elem ( ) ) {
newValue = true
newFormat := "newVal := %s{}\n"
newStmt := fmt . Sprintf ( newFormat , g . typeName ( outField . Type . Elem ( ) ) )
2015-05-15 11:01:47 +00:00
b . addLine ( newStmt , indent + 2 )
2015-12-23 06:53:03 +00:00
call := g . conversionFunctionCall ( inField . Type . Elem ( ) , outField . Type . Elem ( ) , "s" , "&val" , "&newVal" , "0" )
convertStmt := fmt . Sprintf ( "if err := %s; err != nil {\n" , call )
2015-05-15 11:01:47 +00:00
b . addLine ( convertStmt , indent + 2 )
b . addLine ( "return err\n" , indent + 3 )
b . addLine ( "}\n" , indent + 2 )
2015-04-17 12:16:33 +00:00
}
if inField . Type . Key ( ) . ConvertibleTo ( outField . Type . Key ( ) ) {
value := "val"
if newValue {
value = "newVal"
}
assignStmt := ""
if inField . Type . Key ( ) . AssignableTo ( outField . Type . Key ( ) ) {
assignStmt = fmt . Sprintf ( "out.%s[key] = %s\n" , outField . Name , value )
} else {
assignStmt = fmt . Sprintf ( "out.%s[%s(key)] = %s\n" , outField . Name , g . typeName ( outField . Type . Key ( ) ) , value )
}
2015-05-15 11:01:47 +00:00
b . addLine ( assignStmt , indent + 2 )
2015-04-17 12:16:33 +00:00
} else {
// TODO(wojtek-t): Support maps with keys that are non-convertible to each other.
return fmt . Errorf ( "conversions between unconvertible keys in map are not supported." )
}
2015-05-15 11:01:47 +00:00
b . addLine ( "}\n" , indent + 1 )
b . addLine ( "} else {\n" , indent )
2015-04-28 14:50:55 +00:00
nilFormat := "out.%s = nil\n"
nilStmt := fmt . Sprintf ( nilFormat , outField . Name )
2015-05-15 11:01:47 +00:00
b . addLine ( nilStmt , indent + 1 )
b . addLine ( "}\n" , indent )
2015-04-17 12:16:33 +00:00
return nil
}
2015-05-15 11:01:47 +00:00
func ( g * conversionGenerator ) writeConversionForSlice ( b * buffer , inField , outField reflect . StructField , indent int ) error {
2015-04-17 12:16:33 +00:00
ifFormat := "if in.%s != nil {\n"
ifStmt := fmt . Sprintf ( ifFormat , inField . Name )
2015-05-15 11:01:47 +00:00
b . addLine ( ifStmt , indent )
2015-04-17 12:16:33 +00:00
makeFormat := "out.%s = make(%s, len(in.%s))\n"
makeStmt := fmt . Sprintf ( makeFormat , outField . Name , g . typeName ( outField . Type ) , inField . Name )
2015-05-15 11:01:47 +00:00
b . addLine ( makeStmt , indent + 1 )
2015-04-17 12:16:33 +00:00
forFormat := "for i := range in.%s {\n"
forStmt := fmt . Sprintf ( forFormat , inField . Name )
2015-05-15 11:01:47 +00:00
b . addLine ( forStmt , indent + 1 )
2015-04-17 12:16:33 +00:00
assigned := false
switch inField . Type . Elem ( ) . Kind ( ) {
case reflect . Map , reflect . Ptr , reflect . Slice , reflect . Interface , reflect . Struct :
// Don't copy these via assignment/conversion!
default :
// This should handle all simple types.
if inField . Type . Elem ( ) . AssignableTo ( outField . Type . Elem ( ) ) {
assignFormat := "out.%s[i] = in.%s[i]\n"
assignStmt := fmt . Sprintf ( assignFormat , outField . Name , inField . Name )
2015-05-15 11:01:47 +00:00
b . addLine ( assignStmt , indent + 2 )
2015-04-17 12:16:33 +00:00
assigned = true
} else if inField . Type . Elem ( ) . ConvertibleTo ( outField . Type . Elem ( ) ) {
assignFormat := "out.%s[i] = %s(in.%s[i])\n"
assignStmt := fmt . Sprintf ( assignFormat , outField . Name , g . typeName ( outField . Type . Elem ( ) ) , inField . Name )
2015-05-15 11:01:47 +00:00
b . addLine ( assignStmt , indent + 2 )
2015-04-17 12:16:33 +00:00
assigned = true
2015-04-17 12:16:33 +00:00
}
2015-04-17 12:16:33 +00:00
}
if ! assigned {
2015-12-23 06:53:03 +00:00
call := g . conversionFunctionCall ( inField . Type . Elem ( ) , outField . Type . Elem ( ) , "s" , "&in." + inField . Name + "[i]" , "&out." + outField . Name + "[i]" , "0" )
assignStmt := fmt . Sprintf ( "if err := %s; err != nil {\n" , call )
2015-05-15 11:01:47 +00:00
b . addLine ( assignStmt , indent + 2 )
b . addLine ( "return err\n" , indent + 3 )
b . addLine ( "}\n" , indent + 2 )
2015-04-28 14:50:55 +00:00
}
2015-05-15 11:01:47 +00:00
b . addLine ( "}\n" , indent + 1 )
b . addLine ( "} else {\n" , indent )
2015-04-28 14:50:55 +00:00
nilFormat := "out.%s = nil\n"
nilStmt := fmt . Sprintf ( nilFormat , outField . Name )
2015-05-15 11:01:47 +00:00
b . addLine ( nilStmt , indent + 1 )
b . addLine ( "}\n" , indent )
2015-04-17 12:16:33 +00:00
return nil
}
2015-05-15 11:01:47 +00:00
func ( g * conversionGenerator ) writeConversionForPtr ( b * buffer , inField , outField reflect . StructField , indent int ) error {
2015-04-23 14:05:14 +00:00
switch inField . Type . Elem ( ) . Kind ( ) {
case reflect . Map , reflect . Ptr , reflect . Slice , reflect . Interface , reflect . Struct :
// Don't copy these via assignment/conversion!
default :
// This should handle pointers to all simple types.
assignable := inField . Type . Elem ( ) . AssignableTo ( outField . Type . Elem ( ) )
convertible := inField . Type . Elem ( ) . ConvertibleTo ( outField . Type . Elem ( ) )
if assignable || convertible {
ifFormat := "if in.%s != nil {\n"
ifStmt := fmt . Sprintf ( ifFormat , inField . Name )
2015-05-15 11:01:47 +00:00
b . addLine ( ifStmt , indent )
2015-04-23 14:05:14 +00:00
newFormat := "out.%s = new(%s)\n"
newStmt := fmt . Sprintf ( newFormat , outField . Name , g . typeName ( outField . Type . Elem ( ) ) )
2015-05-15 11:01:47 +00:00
b . addLine ( newStmt , indent + 1 )
2015-04-23 14:05:14 +00:00
}
if assignable {
assignFormat := "*out.%s = *in.%s\n"
assignStmt := fmt . Sprintf ( assignFormat , outField . Name , inField . Name )
2015-05-15 11:01:47 +00:00
b . addLine ( assignStmt , indent + 1 )
2015-04-23 14:05:14 +00:00
} else if convertible {
assignFormat := "*out.%s = %s(*in.%s)\n"
assignStmt := fmt . Sprintf ( assignFormat , outField . Name , g . typeName ( outField . Type . Elem ( ) ) , inField . Name )
2015-05-15 11:01:47 +00:00
b . addLine ( assignStmt , indent + 1 )
2015-04-23 14:05:14 +00:00
}
if assignable || convertible {
2015-05-15 11:01:47 +00:00
b . addLine ( "} else {\n" , indent )
2015-04-28 14:50:55 +00:00
nilFormat := "out.%s = nil\n"
nilStmt := fmt . Sprintf ( nilFormat , outField . Name )
2015-05-15 11:01:47 +00:00
b . addLine ( nilStmt , indent + 1 )
b . addLine ( "}\n" , indent )
2015-04-23 14:05:14 +00:00
return nil
}
}
2015-12-23 06:53:03 +00:00
b . addLine ( fmt . Sprintf ( "// unable to generate simple pointer conversion for %v -> %v\n" , inField . Type . Elem ( ) , outField . Type . Elem ( ) ) , indent )
2015-04-28 14:50:55 +00:00
ifFormat := "if in.%s != nil {\n"
ifStmt := fmt . Sprintf ( ifFormat , inField . Name )
2015-05-15 11:01:47 +00:00
b . addLine ( ifStmt , indent )
2015-04-28 14:50:55 +00:00
assignStmt := ""
2015-12-23 06:53:03 +00:00
if _ , ok := g . publicFuncs [ typePair { inField . Type . Elem ( ) , outField . Type . Elem ( ) } ] ; ok {
2015-04-28 14:50:55 +00:00
newFormat := "out.%s = new(%s)\n"
newStmt := fmt . Sprintf ( newFormat , outField . Name , g . typeName ( outField . Type . Elem ( ) ) )
2015-05-15 11:01:47 +00:00
b . addLine ( newStmt , indent + 1 )
2015-12-23 06:53:03 +00:00
call := g . conversionFunctionCall ( inField . Type . Elem ( ) , outField . Type . Elem ( ) , "s" , "in." + inField . Name , "out." + outField . Name , "0" )
assignStmt = fmt . Sprintf ( "if err := %s; err != nil {\n" , call )
2015-04-28 14:50:55 +00:00
} else {
2015-12-23 06:53:03 +00:00
call := g . conversionFunctionCall ( inField . Type . Elem ( ) , outField . Type . Elem ( ) , "s" , "&in." + inField . Name , "&out." + outField . Name , "0" )
assignStmt = fmt . Sprintf ( "if err := %s; err != nil {\n" , call )
2015-04-28 14:50:55 +00:00
}
2015-05-15 11:01:47 +00:00
b . addLine ( assignStmt , indent + 1 )
b . addLine ( "return err\n" , indent + 2 )
b . addLine ( "}\n" , indent + 1 )
b . addLine ( "} else {\n" , indent )
2015-04-28 14:50:55 +00:00
nilFormat := "out.%s = nil\n"
nilStmt := fmt . Sprintf ( nilFormat , outField . Name )
2015-05-15 11:01:47 +00:00
b . addLine ( nilStmt , indent + 1 )
b . addLine ( "}\n" , indent )
2015-04-23 14:05:14 +00:00
return nil
}
2015-08-31 05:47:41 +00:00
func ( g * conversionGenerator ) canTryConversion ( b * buffer , inType reflect . Type , inField , outField reflect . StructField , indent int ) ( bool , error ) {
if inField . Type . Kind ( ) != outField . Type . Kind ( ) {
if ! g . overridden [ inType ] {
return false , fmt . Errorf ( "input %s.%s (%s) does not match output (%s) and conversion is not overridden" , inType , inField . Name , inField . Type . Kind ( ) , outField . Type . Kind ( ) )
}
b . addLine ( fmt . Sprintf ( "// in.%s has no peer in out\n" , inField . Name ) , indent )
return false , nil
}
return true , nil
}
2015-05-15 11:01:47 +00:00
func ( g * conversionGenerator ) writeConversionForStruct ( b * buffer , inType , outType reflect . Type , indent int ) error {
2015-04-17 12:16:33 +00:00
for i := 0 ; i < inType . NumField ( ) ; i ++ {
inField := inType . Field ( i )
2015-08-31 05:47:41 +00:00
outField , found := outType . FieldByName ( inField . Name )
if ! found {
if ! g . overridden [ inType ] {
return fmt . Errorf ( "input %s.%s has no peer in output %s and conversion is not overridden" , inType , inField . Name , outType )
}
b . addLine ( fmt . Sprintf ( "// in.%s has no peer in out\n" , inField . Name ) , indent )
continue
}
2015-04-17 12:16:33 +00:00
2015-12-21 05:08:33 +00:00
if g . scheme . Converter ( ) . IsConversionIgnored ( inField . Type , outField . Type ) {
continue
}
2015-05-07 11:46:54 +00:00
existsConversion := g . scheme . Converter ( ) . HasConversionFunc ( inField . Type , outField . Type )
2015-12-23 06:53:03 +00:00
_ , hasPublicConversion := g . publicFuncs [ typePair { inField . Type , outField . Type } ]
// TODO: This allows a private conversion for a slice to take precedence over a public
// conversion for the field, even though that is technically slower. We should report when
// we generate an inefficient conversion.
if existsConversion || hasPublicConversion {
2015-05-06 07:05:58 +00:00
// Use the conversion method that is already defined.
2015-12-23 06:53:03 +00:00
call := g . conversionFunctionCall ( inField . Type , outField . Type , "s" , "&in." + inField . Name , "&out." + outField . Name , "0" )
assignStmt := fmt . Sprintf ( "if err := %s; err != nil {\n" , call )
2015-05-15 11:01:47 +00:00
b . addLine ( assignStmt , indent )
b . addLine ( "return err\n" , indent + 1 )
b . addLine ( "}\n" , indent )
continue
}
switch inField . Type . Kind ( ) {
case reflect . Map :
2015-08-31 05:47:41 +00:00
if try , err := g . canTryConversion ( b , inType , inField , outField , indent ) ; err != nil {
return err
} else if ! try {
continue
}
2015-05-15 11:01:47 +00:00
if err := g . writeConversionForMap ( b , inField , outField , indent ) ; err != nil {
2015-05-06 07:05:58 +00:00
return err
}
2015-05-15 11:01:47 +00:00
continue
case reflect . Ptr :
2015-08-31 05:47:41 +00:00
if try , err := g . canTryConversion ( b , inType , inField , outField , indent ) ; err != nil {
return err
} else if ! try {
continue
}
2015-05-15 11:01:47 +00:00
if err := g . writeConversionForPtr ( b , inField , outField , indent ) ; err != nil {
2015-05-06 07:05:58 +00:00
return err
}
2015-05-15 11:01:47 +00:00
continue
case reflect . Slice :
2015-08-31 05:47:41 +00:00
if try , err := g . canTryConversion ( b , inType , inField , outField , indent ) ; err != nil {
return err
} else if ! try {
continue
}
2015-05-15 11:01:47 +00:00
if err := g . writeConversionForSlice ( b , inField , outField , indent ) ; err != nil {
2015-05-06 07:05:58 +00:00
return err
}
continue
2015-05-15 11:01:47 +00:00
case reflect . Interface , reflect . Struct :
2015-04-17 12:16:33 +00:00
// Don't copy these via assignment/conversion!
default :
// This should handle all simple types.
if inField . Type . AssignableTo ( outField . Type ) {
assignFormat := "out.%s = in.%s\n"
assignStmt := fmt . Sprintf ( assignFormat , outField . Name , inField . Name )
2015-05-15 11:01:47 +00:00
b . addLine ( assignStmt , indent )
2015-04-17 12:16:33 +00:00
continue
}
if inField . Type . ConvertibleTo ( outField . Type ) {
assignFormat := "out.%s = %s(in.%s)\n"
assignStmt := fmt . Sprintf ( assignFormat , outField . Name , g . typeName ( outField . Type ) , inField . Name )
2015-05-15 11:01:47 +00:00
b . addLine ( assignStmt , indent )
2015-04-17 12:16:33 +00:00
continue
}
}
2015-12-23 06:53:03 +00:00
call := g . conversionFunctionCall ( inField . Type , outField . Type , "s" , "&in." + inField . Name , "&out." + outField . Name , "0" )
assignStmt := fmt . Sprintf ( "if err := %s; err != nil {\n" , call )
2015-05-15 11:01:47 +00:00
b . addLine ( assignStmt , indent )
b . addLine ( "return err\n" , indent + 1 )
b . addLine ( "}\n" , indent )
2015-04-17 12:16:33 +00:00
}
2015-04-23 14:05:14 +00:00
return nil
}
2015-05-15 11:01:47 +00:00
func ( g * conversionGenerator ) writeConversionForType ( b * buffer , inType , outType reflect . Type , indent int ) error {
2015-08-31 05:47:41 +00:00
// Always emit the auto-generated name.
autoFuncName := g . generatedFunctionName ( inType , outType )
g . writeHeader ( b , autoFuncName , g . typeName ( inType ) , g . typeName ( outType ) , indent )
2015-05-15 11:01:47 +00:00
if err := g . writeDefaultingFunc ( b , inType , indent + 1 ) ; err != nil {
2015-04-28 14:50:55 +00:00
return err
}
2015-04-23 14:05:14 +00:00
switch inType . Kind ( ) {
case reflect . Struct :
2015-05-15 11:01:47 +00:00
if err := g . writeConversionForStruct ( b , inType , outType , indent + 1 ) ; err != nil {
2015-04-23 14:05:14 +00:00
return err
}
default :
2015-05-15 11:01:47 +00:00
return fmt . Errorf ( "type not supported: %v" , inType )
2015-04-17 12:16:33 +00:00
}
2015-05-15 11:01:47 +00:00
g . writeFooter ( b , indent )
b . addLine ( "\n" , 0 )
2015-08-31 05:47:41 +00:00
if ! g . overridden [ inType ] {
// Also emit the "user-facing" name.
userFuncName := g . conversionFunctionName ( inType , outType )
g . writeHeader ( b , userFuncName , g . typeName ( inType ) , g . typeName ( outType ) , indent )
b . addLine ( fmt . Sprintf ( "return %s(in, out, s)\n" , autoFuncName ) , indent + 1 )
b . addLine ( "}\n\n" , 0 )
}
2015-04-17 12:16:33 +00:00
return nil
}
2015-05-15 11:01:47 +00:00
func ( g * conversionGenerator ) existsConversionFunction ( inType , outType reflect . Type ) bool {
2015-04-28 14:50:55 +00:00
if val , found := g . convertibles [ inType ] ; found && val == outType {
return true
}
if val , found := g . convertibles [ outType ] ; found && val == inType {
return true
}
return false
}
2015-05-07 11:46:54 +00:00
// TODO(wojtek-t): We should somehow change the conversion methods registered under:
// pkg/runtime/scheme.go to implement the naming convention for conversion functions
// and get rid of this hack.
type typePair struct {
inType reflect . Type
outType reflect . Type
}
2015-12-21 05:08:33 +00:00
var defaultConversions [ ] typePair = [ ] typePair { }
2015-05-07 11:46:54 +00:00
2015-05-15 11:01:47 +00:00
func ( g * conversionGenerator ) OverwritePackage ( pkg , overwrite string ) {
2015-04-17 12:16:33 +00:00
g . pkgOverwrites [ pkg ] = overwrite
}