Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
ff1da767
Commit
ff1da767
authored
Apr 15, 2016
by
k8s-merge-robot
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #24268 from wojtek-t/remove_old_conversions_generator
Automatic merge from submit-queue Remove old conversion generator
parents
d56ec66e
89e860e6
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
31 additions
and
1308 deletions
+31
-1308
conversion.go
cmd/genconversion/conversion.go
+0
-132
update-generated-conversions.sh
hack/after-build/update-generated-conversions.sh
+0
-52
verify-generated-conversions.sh
hack/after-build/verify-generated-conversions.sh
+0
-53
golang.sh
hack/lib/golang.sh
+0
-1
update-all.sh
hack/update-all.sh
+0
-1
update-generated-conversions.sh
hack/update-generated-conversions.sh
+0
-30
verify-generated-conversions.sh
hack/verify-generated-conversions.sh
+0
-30
pre-commit
hooks/pre-commit
+0
-12
conversion_generator.go
pkg/runtime/conversion_generator.go
+0
-921
conversion_generator_test.go
pkg/runtime/conversion_generator_test.go
+0
-76
swagger_doc_generator.go
pkg/runtime/swagger_doc_generator.go
+31
-0
No files found.
cmd/genconversion/conversion.go
deleted
100644 → 0
View file @
d56ec66e
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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
main
import
(
"bytes"
"fmt"
"io"
"os"
"path"
"runtime"
"k8s.io/kubernetes/pkg/api"
_
"k8s.io/kubernetes/pkg/api/install"
"k8s.io/kubernetes/pkg/api/unversioned"
_
"k8s.io/kubernetes/pkg/apis/autoscaling/install"
_
"k8s.io/kubernetes/pkg/apis/batch/install"
_
"k8s.io/kubernetes/pkg/apis/componentconfig/install"
_
"k8s.io/kubernetes/pkg/apis/extensions/install"
_
"k8s.io/kubernetes/pkg/apis/metrics/install"
kruntime
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/sets"
"github.com/golang/glog"
flag
"github.com/spf13/pflag"
"golang.org/x/tools/imports"
)
const
pkgBase
=
"k8s.io/kubernetes/pkg"
var
(
functionDest
=
flag
.
StringP
(
"funcDest"
,
"f"
,
"-"
,
"Output for conversion functions; '-' means stdout"
)
groupVersion
=
flag
.
StringP
(
"version"
,
"v"
,
"api/v1"
,
"groupPath/version for conversion."
)
)
// We're moving to pkg/apis/group/version. This handles new and legacy packages.
func
pkgPath
(
group
,
version
string
)
string
{
if
group
==
""
{
group
=
"api"
}
gv
:=
group
if
version
!=
""
{
gv
=
path
.
Join
(
group
,
version
)
}
switch
{
case
group
==
"api"
:
// TODO(lavalamp): remove this special case when we move api to apis/api
return
path
.
Join
(
pkgBase
,
gv
)
default
:
return
path
.
Join
(
pkgBase
,
"apis"
,
gv
)
}
}
func
main
()
{
runtime
.
GOMAXPROCS
(
runtime
.
NumCPU
())
flag
.
Parse
()
var
funcOut
io
.
Writer
if
*
functionDest
==
"-"
{
funcOut
=
os
.
Stdout
}
else
{
file
,
err
:=
os
.
Create
(
*
functionDest
)
if
err
!=
nil
{
glog
.
Fatalf
(
"Couldn't open %v: %v"
,
*
functionDest
,
err
)
}
defer
file
.
Close
()
funcOut
=
file
}
data
:=
new
(
bytes
.
Buffer
)
gv
,
err
:=
unversioned
.
ParseGroupVersion
(
*
groupVersion
)
if
err
!=
nil
{
glog
.
Fatalf
(
"Error parsing groupversion %v: %v"
,
*
groupVersion
,
err
)
}
_
,
err
=
data
.
WriteString
(
fmt
.
Sprintf
(
"package %v
\n
"
,
gv
.
Version
))
if
err
!=
nil
{
glog
.
Fatalf
(
"Error while writing package line: %v"
,
err
)
}
versionPath
:=
pkgPath
(
gv
.
Group
,
gv
.
Version
)
generator
:=
kruntime
.
NewConversionGenerator
(
api
.
Scheme
,
versionPath
)
apiShort
:=
generator
.
AddImport
(
path
.
Join
(
pkgBase
,
"api"
))
generator
.
AddImport
(
path
.
Join
(
pkgBase
,
"api/resource"
))
generator
.
AddImport
(
path
.
Join
(
pkgBase
,
"types"
))
// TODO(wojtek-t): Change the overwrites to a flag.
generator
.
OverwritePackage
(
gv
.
Version
,
""
)
for
_
,
knownType
:=
range
api
.
Scheme
.
KnownTypes
(
gv
)
{
if
knownType
.
PkgPath
()
!=
versionPath
{
continue
}
if
err
:=
generator
.
GenerateConversionsForType
(
gv
,
knownType
);
err
!=
nil
{
glog
.
Errorf
(
"Error while generating conversion functions for %v: %v"
,
knownType
,
err
)
}
}
generator
.
RepackImports
(
sets
.
NewString
())
if
err
:=
generator
.
WriteImports
(
data
);
err
!=
nil
{
glog
.
Fatalf
(
"Error while writing imports: %v"
,
err
)
}
if
err
:=
generator
.
WriteConversionFunctions
(
data
);
err
!=
nil
{
glog
.
Fatalf
(
"Error while writing conversion functions: %v"
,
err
)
}
if
err
:=
generator
.
RegisterConversionFunctions
(
data
,
fmt
.
Sprintf
(
"%s.Scheme"
,
apiShort
));
err
!=
nil
{
glog
.
Fatalf
(
"Error while writing conversion functions: %v"
,
err
)
}
b
,
err
:=
imports
.
Process
(
""
,
data
.
Bytes
(),
nil
)
if
err
!=
nil
{
for
i
,
s
:=
range
bytes
.
Split
(
data
.
Bytes
(),
[]
byte
(
"
\n
"
))
{
glog
.
Infof
(
"%d:
\t
%s"
,
i
,
s
)
}
glog
.
Fatalf
(
"Error while update imports: %v
\n
"
,
err
)
}
if
_
,
err
:=
funcOut
.
Write
(
b
);
err
!=
nil
{
glog
.
Fatalf
(
"Error while writing out the resulting file: %v"
,
err
)
}
}
hack/after-build/update-generated-conversions.sh
deleted
100755 → 0
View file @
d56ec66e
#!/bin/bash
# Copyright 2015 The Kubernetes Authors All rights reserved.
#
# 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.
set
-o
errexit
set
-o
nounset
set
-o
pipefail
KUBE_ROOT
=
$(
dirname
"
${
BASH_SOURCE
}
"
)
/../..
source
"
${
KUBE_ROOT
}
/hack/lib/init.sh"
kube::golang::setup_env
genconversion
=
$(
kube::util::find-binary
"genconversion"
)
function
generate_version
()
{
local
group_version
=
$1
local
TMPFILE
=
"/tmp/conversion_generated.
$(
date
+%s
)
.go"
echo
"Generating for
${
group_version
}
"
sed
's/YEAR/2015/'
hack/boilerplate/boilerplate.go.txt
>
"
$TMPFILE
"
cat
>>
"
$TMPFILE
"
<<
EOF
// DO NOT EDIT. THIS FILE IS AUTO-GENERATED BY \
$KUBEROOT
/hack/update-generated-conversions.sh
EOF
"
${
genconversion
}
"
-v
"
${
group_version
}
"
-f
-
>>
"
$TMPFILE
"
mv
"
$TMPFILE
"
"pkg/
$(
kube::util::group-version-to-pkg-path
"
${
group_version
}
"
)
/conversion_generated.go"
}
# TODO(lavalamp): get this list by listing the pkg/apis/ directory?
DEFAULT_GROUP_VERSIONS
=
""
VERSIONS
=
${
VERSIONS
:-
$DEFAULT_GROUP_VERSIONS
}
for
ver
in
$VERSIONS
;
do
# Ensure that the version being processed is registered by setting
# KUBE_API_VERSIONS.
KUBE_API_VERSIONS
=
"
${
ver
}
"
generate_version
"
${
ver
}
"
done
hack/after-build/verify-generated-conversions.sh
deleted
100755 → 0
View file @
d56ec66e
#!/bin/bash
# Copyright 2015 The Kubernetes Authors All rights reserved.
#
# 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.
set
-o
errexit
set
-o
nounset
set
-o
pipefail
KUBE_ROOT
=
$(
dirname
"
${
BASH_SOURCE
}
"
)
/../..
source
"
${
KUBE_ROOT
}
/hack/lib/init.sh"
kube::golang::setup_env
APIROOTS
=
${
APIROOTS
:-
pkg
/apis/authorization pkg/apis/autoscaling pkg/apis/batch pkg/apis/extensions pkg/apis/metrics
}
_tmp
=
"
${
KUBE_ROOT
}
/_tmp"
cleanup
()
{
rm
-rf
"
${
_tmp
}
"
}
trap
"cleanup"
EXIT SIGINT
for
APIROOT
in
${
APIROOTS
}
;
do
mkdir
-p
"
${
_tmp
}
/
${
APIROOT
%/*
}
"
cp
-a
"
${
KUBE_ROOT
}
/
${
APIROOT
}
"
"
${
_tmp
}
/
${
APIROOT
}
"
done
"
${
KUBE_ROOT
}
/hack/after-build/update-generated-conversions.sh"
for
APIROOT
in
${
APIROOTS
}
;
do
TMP_APIROOT
=
"
${
_tmp
}
/
${
APIROOT
}
"
echo
"diffing
${
APIROOT
}
against freshly generated conversions"
ret
=
0
diff
-Naupr
-I
'Auto generated by'
"
${
KUBE_ROOT
}
/
${
APIROOT
}
"
"
${
TMP_APIROOT
}
"
||
ret
=
$?
cp
-a
"
${
TMP_APIROOT
}
"
"
${
KUBE_ROOT
}
/
${
APIROOT
%/*
}
"
if
[[
$ret
-eq
0
]]
;
then
echo
"
${
APIROOT
}
up to date."
else
echo
"
${
APIROOT
}
is out of date. Please run hack/update-generated-conversions.sh"
exit
1
fi
done
hack/lib/golang.sh
View file @
ff1da767
...
@@ -108,7 +108,6 @@ kube::golang::test_targets() {
...
@@ -108,7 +108,6 @@ kube::golang::test_targets() {
cmd/genyaml
cmd/genyaml
cmd/mungedocs
cmd/mungedocs
cmd/genbashcomp
cmd/genbashcomp
cmd/genconversion
cmd/genswaggertypedocs
cmd/genswaggertypedocs
examples/k8petstore/web-server/src
examples/k8petstore/web-server/src
github.com/onsi/ginkgo/ginkgo
github.com/onsi/ginkgo/ginkgo
...
...
hack/update-all.sh
View file @
ff1da767
...
@@ -51,7 +51,6 @@ if ! $ALL ; then
...
@@ -51,7 +51,6 @@ if ! $ALL ; then
fi
fi
BASH_TARGETS
=
"codecgen
BASH_TARGETS
=
"codecgen
generated-conversions
generated-docs
generated-docs
generated-swagger-docs
generated-swagger-docs
swagger-spec
swagger-spec
...
...
hack/update-generated-conversions.sh
deleted
100755 → 0
View file @
d56ec66e
#!/bin/bash
# Copyright 2015 The Kubernetes Authors All rights reserved.
#
# 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.
set
-o
errexit
set
-o
nounset
set
-o
pipefail
KUBE_ROOT
=
$(
dirname
"
${
BASH_SOURCE
}
"
)
/..
source
"
${
KUBE_ROOT
}
/hack/lib/init.sh"
kube::golang::setup_env
"
${
KUBE_ROOT
}
/hack/build-go.sh"
cmd/genconversion
"
${
KUBE_ROOT
}
/hack/after-build/update-generated-conversions.sh"
"
$@
"
# ex: ts=2 sw=2 et filetype=sh
hack/verify-generated-conversions.sh
deleted
100755 → 0
View file @
d56ec66e
#!/bin/bash
# Copyright 2015 The Kubernetes Authors All rights reserved.
#
# 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.
set
-o
errexit
set
-o
nounset
set
-o
pipefail
KUBE_ROOT
=
$(
dirname
"
${
BASH_SOURCE
}
"
)
/..
source
"
${
KUBE_ROOT
}
/hack/lib/init.sh"
kube::golang::setup_env
"
${
KUBE_ROOT
}
/hack/build-go.sh"
cmd/genconversion
"
${
KUBE_ROOT
}
/hack/after-build/verify-generated-conversions.sh"
"
$@
"
# ex: ts=2 sw=2 et filetype=sh
hooks/pre-commit
View file @
ff1da767
...
@@ -134,18 +134,6 @@ else
...
@@ -134,18 +134,6 @@ else
fi
fi
echo
"
${
reset
}
"
echo
"
${
reset
}
"
echo
-ne
"Checking for conversions that need updating... "
if
!
hack/after-build/verify-generated-conversions.sh
>
/dev/null
;
then
echo
"
${
red
}
ERROR!"
echo
"Some conversions functions need regeneration."
echo
"To regenerate conversions, run:"
echo
" hack/update-generated-conversions.sh"
exit_code
=
1
else
echo
"
${
green
}
OK"
fi
echo
"
${
reset
}
"
echo
-ne
"Checking for swagger type documentation that need updating... "
echo
-ne
"Checking for swagger type documentation that need updating... "
if
!
hack/after-build/verify-generated-swagger-docs.sh
>
/dev/null
;
then
if
!
hack/after-build/verify-generated-swagger-docs.sh
>
/dev/null
;
then
echo
"
${
red
}
ERROR!"
echo
"
${
red
}
ERROR!"
...
...
pkg/runtime/conversion_generator.go
deleted
100644 → 0
View file @
d56ec66e
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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
runtime
import
(
"fmt"
"io"
"log"
"path"
"reflect"
goruntime
"runtime"
"sort"
"strings"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/util/sets"
)
type
ConversionGenerator
interface
{
GenerateConversionsForType
(
groupVersion
unversioned
.
GroupVersion
,
reflection
reflect
.
Type
)
error
WriteConversionFunctions
(
w
io
.
Writer
)
error
RegisterConversionFunctions
(
w
io
.
Writer
,
pkg
string
)
error
AddImport
(
pkg
string
)
string
RepackImports
(
exclude
sets
.
String
)
WriteImports
(
w
io
.
Writer
)
error
OverwritePackage
(
pkg
,
overwrite
string
)
AssumePrivateConversions
()
}
func
NewConversionGenerator
(
scheme
*
Scheme
,
targetPkg
string
)
ConversionGenerator
{
g
:=
&
conversionGenerator
{
scheme
:
scheme
,
nameFormat
:
"Convert_%s_%s_To_%s_%s"
,
generatedNamePrefix
:
"auto"
,
targetPkg
:
targetPkg
,
publicFuncs
:
make
(
map
[
typePair
]
functionName
),
convertibles
:
make
(
map
[
reflect
.
Type
]
reflect
.
Type
),
overridden
:
make
(
map
[
reflect
.
Type
]
bool
),
pkgOverwrites
:
make
(
map
[
string
]
string
),
imports
:
make
(
map
[
string
]
string
),
shortImports
:
make
(
map
[
string
]
string
),
}
g
.
targetPackage
(
targetPkg
)
g
.
AddImport
(
"reflect"
)
g
.
AddImport
(
"k8s.io/kubernetes/pkg/conversion"
)
return
g
}
var
complexTypes
[]
reflect
.
Kind
=
[]
reflect
.
Kind
{
reflect
.
Map
,
reflect
.
Ptr
,
reflect
.
Slice
,
reflect
.
Interface
,
reflect
.
Struct
}
type
functionName
struct
{
name
string
packageName
string
}
type
conversionGenerator
struct
{
scheme
*
Scheme
nameFormat
string
generatedNamePrefix
string
targetPkg
string
publicFuncs
map
[
typePair
]
functionName
convertibles
map
[
reflect
.
Type
]
reflect
.
Type
overridden
map
[
reflect
.
Type
]
bool
// 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
// map of package names to shortname
imports
map
[
string
]
string
// map of short names to package names
shortImports
map
[
string
]
string
// A buffer that is used for storing lines that needs to be written.
linesToPrint
[]
string
// 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
)
}
func
(
g
*
conversionGenerator
)
GenerateConversionsForType
(
gv
unversioned
.
GroupVersion
,
reflection
reflect
.
Type
)
error
{
kind
:=
reflection
.
Name
()
// TODO this is equivalent to what it did before, but it needs to be fixed for the proper group
internalVersion
:=
gv
internalVersion
.
Version
=
APIVersionInternal
internalObj
,
err
:=
g
.
scheme
.
New
(
internalVersion
.
WithKind
(
kind
))
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
{
return
fmt
.
Errorf
(
"created object should be of type Ptr: %v"
,
internalObjType
.
Kind
())
}
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
}
// 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
}
func
(
g
*
conversionGenerator
)
generateConversionsBetween
(
inType
,
outType
reflect
.
Type
)
error
{
existingConversion
:=
g
.
scheme
.
Converter
()
.
HasConversionFunc
(
inType
,
outType
)
&&
g
.
scheme
.
Converter
()
.
HasConversionFunc
(
outType
,
inType
)
// 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
{
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
)
// Don't generate conversion methods for the same type.
return
nil
}
if
_
,
ok
:=
primitiveConversion
(
inType
,
outType
);
ok
{
return
nil
}
if
inType
.
Kind
()
!=
outType
.
Kind
()
{
if
existingConversion
{
g
.
rememberConversionFunction
(
inType
,
outType
,
false
)
g
.
rememberConversionFunction
(
outType
,
inType
,
false
)
return
nil
}
return
fmt
.
Errorf
(
"cannot convert types of different kinds: %v %v"
,
inType
,
outType
)
}
g
.
addImportByPath
(
inType
.
PkgPath
())
g
.
addImportByPath
(
outType
.
PkgPath
())
// We should be able to generate conversions both sides.
switch
inType
.
Kind
()
{
case
reflect
.
Map
:
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
case
reflect
.
Ptr
:
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
case
reflect
.
Slice
:
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.
return
nil
case
reflect
.
Interface
:
// TODO(wojtek-t): Currently we don't support converting interfaces.
return
fmt
.
Errorf
(
"interfaces are not supported"
)
case
reflect
.
Struct
:
inErr
:=
g
.
generateConversionsForStruct
(
inType
,
outType
)
outErr
:=
g
.
generateConversionsForStruct
(
outType
,
inType
)
if
!
existingConversion
&&
(
inErr
!=
nil
||
outErr
!=
nil
)
{
return
inErr
}
g
.
rememberConversionFunction
(
inType
,
outType
,
true
)
if
existingConversion
{
g
.
overridden
[
inType
]
=
true
}
g
.
convertibles
[
inType
]
=
outType
return
nil
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
}
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
)
{
g
.
AddImport
(
p
)
g
.
publicFuncs
[
typePair
{
inType
,
outType
}]
=
functionName
{
name
:
n
,
packageName
:
p
}
}
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
{
g
.
publicFuncs
[
typePair
{
inType
,
outType
}]
=
functionName
{
name
:
g
.
conversionFunctionName
(
inType
,
outType
)}
}
}
func
isPublic
(
name
string
)
bool
{
return
strings
.
ToUpper
(
name
[
:
1
])
==
name
[
:
1
]
}
func
(
g
*
conversionGenerator
)
generateConversionsForMap
(
inType
,
outType
reflect
.
Type
)
error
{
inKey
:=
inType
.
Key
()
outKey
:=
outType
.
Key
()
g
.
addImportByPath
(
inKey
.
PkgPath
())
g
.
addImportByPath
(
outKey
.
PkgPath
())
if
err
:=
g
.
generateConversionsBetween
(
inKey
,
outKey
);
err
!=
nil
{
return
err
}
inValue
:=
inType
.
Elem
()
outValue
:=
outType
.
Elem
()
g
.
addImportByPath
(
inValue
.
PkgPath
())
g
.
addImportByPath
(
outValue
.
PkgPath
())
if
err
:=
g
.
generateConversionsBetween
(
inValue
,
outValue
);
err
!=
nil
{
return
err
}
return
nil
}
func
(
g
*
conversionGenerator
)
generateConversionsForSlice
(
inType
,
outType
reflect
.
Type
)
error
{
inElem
:=
inType
.
Elem
()
outElem
:=
outType
.
Elem
()
// slice conversion requires the package for the destination type in order to instantiate the map
g
.
addImportByPath
(
outElem
.
PkgPath
())
if
err
:=
g
.
generateConversionsBetween
(
inElem
,
outElem
);
err
!=
nil
{
return
err
}
return
nil
}
func
(
g
*
conversionGenerator
)
generateConversionsForStruct
(
inType
,
outType
reflect
.
Type
)
error
{
errs
:=
[]
string
{}
for
i
:=
0
;
i
<
inType
.
NumField
();
i
++
{
inField
:=
inType
.
Field
(
i
)
outField
,
found
:=
outType
.
FieldByName
(
inField
.
Name
)
if
!
found
{
// aggregate the errors so we can return them at the end but still provide
// best effort for generation for other fields in this type
errs
=
append
(
errs
,
fmt
.
Sprintf
(
"couldn't find a corresponding field %v in %v"
,
inField
.
Name
,
outType
))
continue
}
if
isComplexType
(
inField
.
Type
)
{
if
err
:=
g
.
generateConversionsBetween
(
inField
.
Type
,
outField
.
Type
);
err
!=
nil
{
return
err
}
}
}
if
len
(
errs
)
==
0
{
return
nil
}
return
fmt
.
Errorf
(
strings
.
Join
(
errs
,
","
))
}
// 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
}
type
byName
[]
reflect
.
Type
func
(
s
byName
)
Len
()
int
{
return
len
(
s
)
}
func
(
s
byName
)
Less
(
i
,
j
int
)
bool
{
fullNameI
:=
s
[
i
]
.
PkgPath
()
+
"/"
+
s
[
i
]
.
Name
()
fullNameJ
:=
s
[
j
]
.
PkgPath
()
+
"/"
+
s
[
j
]
.
Name
()
return
fullNameI
<
fullNameJ
}
func
(
s
byName
)
Swap
(
i
,
j
int
)
{
s
[
i
],
s
[
j
]
=
s
[
j
],
s
[
i
]
}
func
(
g
*
conversionGenerator
)
targetPackage
(
pkg
string
)
{
g
.
imports
[
pkg
]
=
""
g
.
shortImports
[
""
]
=
pkg
}
func
(
g
*
conversionGenerator
)
RepackImports
(
exclude
sets
.
String
)
{
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
}
func
(
g
*
conversionGenerator
)
WriteConversionFunctions
(
w
io
.
Writer
)
error
{
// 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
))
buffer
:=
newBuffer
()
indent
:=
0
for
_
,
inType
:=
range
keys
{
outType
:=
g
.
convertibles
[
inType
]
// All types in g.convertibles are structs.
if
inType
.
Kind
()
!=
reflect
.
Struct
{
return
fmt
.
Errorf
(
"non-struct conversions are not-supported"
)
}
if
err
:=
g
.
writeConversionForType
(
buffer
,
inType
,
outType
,
indent
);
err
!=
nil
{
return
err
}
}
if
err
:=
buffer
.
flushLines
(
w
);
err
!=
nil
{
return
err
}
return
nil
}
func
(
g
*
conversionGenerator
)
writeRegisterHeader
(
b
*
buffer
,
pkg
string
,
indent
int
)
{
b
.
addLine
(
"func init() {
\n
"
,
indent
)
b
.
addLine
(
fmt
.
Sprintf
(
"err := %s.AddGeneratedConversionFuncs(
\n
"
,
pkg
),
indent
+
1
)
}
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
)
}
func
(
g
*
conversionGenerator
)
RegisterConversionFunctions
(
w
io
.
Writer
,
pkg
string
)
error
{
// Write conversion function names alphabetically ordered.
var
names
[]
string
for
inType
,
outType
:=
range
g
.
convertibles
{
names
=
append
(
names
,
g
.
generatedFunctionName
(
inType
,
outType
))
}
sort
.
Strings
(
names
)
buffer
:=
newBuffer
()
indent
:=
0
g
.
writeRegisterHeader
(
buffer
,
pkg
,
indent
)
for
_
,
name
:=
range
names
{
buffer
.
addLine
(
fmt
.
Sprintf
(
"%s,
\n
"
,
name
),
indent
+
2
)
}
g
.
writeRegisterFooter
(
buffer
,
indent
)
if
err
:=
buffer
.
flushLines
(
w
);
err
!=
nil
{
return
err
}
return
nil
}
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
))
}
func
(
g
*
conversionGenerator
)
typeName
(
inType
reflect
.
Type
)
string
{
switch
inType
.
Kind
()
{
case
reflect
.
Slice
:
return
fmt
.
Sprintf
(
"[]%s"
,
g
.
typeName
(
inType
.
Elem
()))
case
reflect
.
Ptr
:
return
fmt
.
Sprintf
(
"*%s"
,
g
.
typeName
(
inType
.
Elem
()))
case
reflect
.
Map
:
if
len
(
inType
.
Name
())
==
0
{
return
fmt
.
Sprintf
(
"map[%s]%s"
,
g
.
typeName
(
inType
.
Key
()),
g
.
typeName
(
inType
.
Elem
()))
}
fallthrough
default
:
pkg
,
name
:=
inType
.
PkgPath
(),
inType
.
Name
()
if
len
(
name
)
==
0
&&
inType
.
Kind
()
==
reflect
.
Struct
{
return
"struct{}"
}
if
len
(
pkg
)
==
0
{
// Default package.
return
name
}
if
val
,
found
:=
g
.
pkgOverwrites
[
pkg
];
found
{
pkg
=
val
}
if
len
(
pkg
)
==
0
{
return
name
}
short
:=
g
.
addImportByPath
(
pkg
)
if
len
(
short
)
>
0
{
return
fmt
.
Sprintf
(
"%s.%s"
,
short
,
name
)
}
return
name
}
}
func
(
g
*
conversionGenerator
)
writeDefaultingFunc
(
b
*
buffer
,
inType
reflect
.
Type
,
indent
int
)
error
{
getStmt
:=
"if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
\n
"
b
.
addLine
(
getStmt
,
indent
)
callFormat
:=
"defaulting.(func(*%s))(in)
\n
"
callStmt
:=
fmt
.
Sprintf
(
callFormat
,
g
.
typeName
(
inType
))
b
.
addLine
(
callStmt
,
indent
+
1
)
b
.
addLine
(
"}
\n
"
,
indent
)
return
nil
}
func
packageForName
(
inType
reflect
.
Type
)
string
{
if
inType
.
PkgPath
()
==
""
{
return
""
}
slices
:=
strings
.
Split
(
inType
.
PkgPath
(),
"/"
)
return
slices
[
len
(
slices
)
-
1
]
}
func
(
g
*
conversionGenerator
)
conversionFunctionName
(
inType
,
outType
reflect
.
Type
)
string
{
funcNameFormat
:=
g
.
nameFormat
inPkg
:=
packageForName
(
inType
)
outPkg
:=
packageForName
(
outType
)
funcName
:=
fmt
.
Sprintf
(
funcNameFormat
,
inPkg
,
inType
.
Name
(),
outPkg
,
outType
.
Name
())
return
funcName
}
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
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
,
", "
))
}
log
.
Printf
(
"WARNING: Using reflection to convert %v -> %v (no public conversion)"
,
inType
,
outType
)
return
fmt
.
Sprintf
(
"%s.Convert(%s)"
,
scopeName
,
strings
.
Join
(
args
,
", "
))
}
func
(
g
*
conversionGenerator
)
generatedFunctionName
(
inType
,
outType
reflect
.
Type
)
string
{
return
g
.
generatedNamePrefix
+
g
.
conversionFunctionName
(
inType
,
outType
)
}
func
(
g
*
conversionGenerator
)
writeHeader
(
b
*
buffer
,
name
,
inType
,
outType
string
,
indent
int
)
{
format
:=
"func %s(in *%s, out *%s, s conversion.Scope) error {
\n
"
stmt
:=
fmt
.
Sprintf
(
format
,
name
,
inType
,
outType
)
b
.
addLine
(
stmt
,
indent
)
}
func
(
g
*
conversionGenerator
)
writeFooter
(
b
*
buffer
,
indent
int
)
{
b
.
addLine
(
"return nil
\n
"
,
indent
+
1
)
b
.
addLine
(
"}
\n
"
,
indent
)
}
func
(
g
*
conversionGenerator
)
writeConversionForMap
(
b
*
buffer
,
inField
,
outField
reflect
.
StructField
,
indent
int
)
error
{
ifFormat
:=
"if in.%s != nil {
\n
"
ifStmt
:=
fmt
.
Sprintf
(
ifFormat
,
inField
.
Name
)
b
.
addLine
(
ifStmt
,
indent
)
makeFormat
:=
"out.%s = make(%s)
\n
"
makeStmt
:=
fmt
.
Sprintf
(
makeFormat
,
outField
.
Name
,
g
.
typeName
(
outField
.
Type
))
b
.
addLine
(
makeStmt
,
indent
+
1
)
forFormat
:=
"for key, val := range in.%s {
\n
"
forStmt
:=
fmt
.
Sprintf
(
forFormat
,
inField
.
Name
)
b
.
addLine
(
forStmt
,
indent
+
1
)
// 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
()))
b
.
addLine
(
newStmt
,
indent
+
2
)
call
:=
g
.
conversionFunctionCall
(
inField
.
Type
.
Elem
(),
outField
.
Type
.
Elem
(),
"s"
,
"&val"
,
"&newVal"
,
"0"
)
convertStmt
:=
fmt
.
Sprintf
(
"if err := %s; err != nil {
\n
"
,
call
)
b
.
addLine
(
convertStmt
,
indent
+
2
)
b
.
addLine
(
"return err
\n
"
,
indent
+
3
)
b
.
addLine
(
"}
\n
"
,
indent
+
2
)
}
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
)
}
b
.
addLine
(
assignStmt
,
indent
+
2
)
}
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."
)
}
b
.
addLine
(
"}
\n
"
,
indent
+
1
)
b
.
addLine
(
"} else {
\n
"
,
indent
)
nilFormat
:=
"out.%s = nil
\n
"
nilStmt
:=
fmt
.
Sprintf
(
nilFormat
,
outField
.
Name
)
b
.
addLine
(
nilStmt
,
indent
+
1
)
b
.
addLine
(
"}
\n
"
,
indent
)
return
nil
}
func
(
g
*
conversionGenerator
)
writeConversionForSlice
(
b
*
buffer
,
inField
,
outField
reflect
.
StructField
,
indent
int
)
error
{
ifFormat
:=
"if in.%s != nil {
\n
"
ifStmt
:=
fmt
.
Sprintf
(
ifFormat
,
inField
.
Name
)
b
.
addLine
(
ifStmt
,
indent
)
makeFormat
:=
"out.%s = make(%s, len(in.%s))
\n
"
makeStmt
:=
fmt
.
Sprintf
(
makeFormat
,
outField
.
Name
,
g
.
typeName
(
outField
.
Type
),
inField
.
Name
)
b
.
addLine
(
makeStmt
,
indent
+
1
)
forFormat
:=
"for i := range in.%s {
\n
"
forStmt
:=
fmt
.
Sprintf
(
forFormat
,
inField
.
Name
)
b
.
addLine
(
forStmt
,
indent
+
1
)
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
)
b
.
addLine
(
assignStmt
,
indent
+
2
)
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
)
b
.
addLine
(
assignStmt
,
indent
+
2
)
assigned
=
true
}
}
if
!
assigned
{
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
)
b
.
addLine
(
assignStmt
,
indent
+
2
)
b
.
addLine
(
"return err
\n
"
,
indent
+
3
)
b
.
addLine
(
"}
\n
"
,
indent
+
2
)
}
b
.
addLine
(
"}
\n
"
,
indent
+
1
)
b
.
addLine
(
"} else {
\n
"
,
indent
)
nilFormat
:=
"out.%s = nil
\n
"
nilStmt
:=
fmt
.
Sprintf
(
nilFormat
,
outField
.
Name
)
b
.
addLine
(
nilStmt
,
indent
+
1
)
b
.
addLine
(
"}
\n
"
,
indent
)
return
nil
}
func
(
g
*
conversionGenerator
)
writeConversionForPtr
(
b
*
buffer
,
inField
,
outField
reflect
.
StructField
,
indent
int
)
error
{
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
)
b
.
addLine
(
ifStmt
,
indent
)
newFormat
:=
"out.%s = new(%s)
\n
"
newStmt
:=
fmt
.
Sprintf
(
newFormat
,
outField
.
Name
,
g
.
typeName
(
outField
.
Type
.
Elem
()))
b
.
addLine
(
newStmt
,
indent
+
1
)
}
if
assignable
{
assignFormat
:=
"*out.%s = *in.%s
\n
"
assignStmt
:=
fmt
.
Sprintf
(
assignFormat
,
outField
.
Name
,
inField
.
Name
)
b
.
addLine
(
assignStmt
,
indent
+
1
)
}
else
if
convertible
{
assignFormat
:=
"*out.%s = %s(*in.%s)
\n
"
assignStmt
:=
fmt
.
Sprintf
(
assignFormat
,
outField
.
Name
,
g
.
typeName
(
outField
.
Type
.
Elem
()),
inField
.
Name
)
b
.
addLine
(
assignStmt
,
indent
+
1
)
}
if
assignable
||
convertible
{
b
.
addLine
(
"} else {
\n
"
,
indent
)
nilFormat
:=
"out.%s = nil
\n
"
nilStmt
:=
fmt
.
Sprintf
(
nilFormat
,
outField
.
Name
)
b
.
addLine
(
nilStmt
,
indent
+
1
)
b
.
addLine
(
"}
\n
"
,
indent
)
return
nil
}
}
b
.
addLine
(
fmt
.
Sprintf
(
"// unable to generate simple pointer conversion for %v -> %v
\n
"
,
inField
.
Type
.
Elem
(),
outField
.
Type
.
Elem
()),
indent
)
ifFormat
:=
"if in.%s != nil {
\n
"
ifStmt
:=
fmt
.
Sprintf
(
ifFormat
,
inField
.
Name
)
b
.
addLine
(
ifStmt
,
indent
)
assignStmt
:=
""
if
_
,
ok
:=
g
.
publicFuncs
[
typePair
{
inField
.
Type
.
Elem
(),
outField
.
Type
.
Elem
()}];
ok
{
newFormat
:=
"out.%s = new(%s)
\n
"
newStmt
:=
fmt
.
Sprintf
(
newFormat
,
outField
.
Name
,
g
.
typeName
(
outField
.
Type
.
Elem
()))
b
.
addLine
(
newStmt
,
indent
+
1
)
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
)
}
else
{
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
)
}
b
.
addLine
(
assignStmt
,
indent
+
1
)
b
.
addLine
(
"return err
\n
"
,
indent
+
2
)
b
.
addLine
(
"}
\n
"
,
indent
+
1
)
b
.
addLine
(
"} else {
\n
"
,
indent
)
nilFormat
:=
"out.%s = nil
\n
"
nilStmt
:=
fmt
.
Sprintf
(
nilFormat
,
outField
.
Name
)
b
.
addLine
(
nilStmt
,
indent
+
1
)
b
.
addLine
(
"}
\n
"
,
indent
)
return
nil
}
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
}
func
(
g
*
conversionGenerator
)
writeConversionForStruct
(
b
*
buffer
,
inType
,
outType
reflect
.
Type
,
indent
int
)
error
{
for
i
:=
0
;
i
<
inType
.
NumField
();
i
++
{
inField
:=
inType
.
Field
(
i
)
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
}
if
g
.
scheme
.
Converter
()
.
IsConversionIgnored
(
inField
.
Type
,
outField
.
Type
)
{
continue
}
existsConversion
:=
g
.
scheme
.
Converter
()
.
HasConversionFunc
(
inField
.
Type
,
outField
.
Type
)
_
,
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
{
// Use the conversion method that is already defined.
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
)
b
.
addLine
(
assignStmt
,
indent
)
b
.
addLine
(
"return err
\n
"
,
indent
+
1
)
b
.
addLine
(
"}
\n
"
,
indent
)
continue
}
switch
inField
.
Type
.
Kind
()
{
case
reflect
.
Map
:
if
try
,
err
:=
g
.
canTryConversion
(
b
,
inType
,
inField
,
outField
,
indent
);
err
!=
nil
{
return
err
}
else
if
!
try
{
continue
}
if
err
:=
g
.
writeConversionForMap
(
b
,
inField
,
outField
,
indent
);
err
!=
nil
{
return
err
}
continue
case
reflect
.
Ptr
:
if
try
,
err
:=
g
.
canTryConversion
(
b
,
inType
,
inField
,
outField
,
indent
);
err
!=
nil
{
return
err
}
else
if
!
try
{
continue
}
if
err
:=
g
.
writeConversionForPtr
(
b
,
inField
,
outField
,
indent
);
err
!=
nil
{
return
err
}
continue
case
reflect
.
Slice
:
if
try
,
err
:=
g
.
canTryConversion
(
b
,
inType
,
inField
,
outField
,
indent
);
err
!=
nil
{
return
err
}
else
if
!
try
{
continue
}
if
err
:=
g
.
writeConversionForSlice
(
b
,
inField
,
outField
,
indent
);
err
!=
nil
{
return
err
}
continue
case
reflect
.
Interface
,
reflect
.
Struct
:
// 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
)
b
.
addLine
(
assignStmt
,
indent
)
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
)
b
.
addLine
(
assignStmt
,
indent
)
continue
}
}
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
)
b
.
addLine
(
assignStmt
,
indent
)
b
.
addLine
(
"return err
\n
"
,
indent
+
1
)
b
.
addLine
(
"}
\n
"
,
indent
)
}
return
nil
}
func
(
g
*
conversionGenerator
)
writeConversionForType
(
b
*
buffer
,
inType
,
outType
reflect
.
Type
,
indent
int
)
error
{
// Always emit the auto-generated name.
autoFuncName
:=
g
.
generatedFunctionName
(
inType
,
outType
)
g
.
writeHeader
(
b
,
autoFuncName
,
g
.
typeName
(
inType
),
g
.
typeName
(
outType
),
indent
)
if
err
:=
g
.
writeDefaultingFunc
(
b
,
inType
,
indent
+
1
);
err
!=
nil
{
return
err
}
switch
inType
.
Kind
()
{
case
reflect
.
Struct
:
if
err
:=
g
.
writeConversionForStruct
(
b
,
inType
,
outType
,
indent
+
1
);
err
!=
nil
{
return
err
}
default
:
return
fmt
.
Errorf
(
"type not supported: %v"
,
inType
)
}
g
.
writeFooter
(
b
,
indent
)
b
.
addLine
(
"
\n
"
,
0
)
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
)
}
return
nil
}
func
(
g
*
conversionGenerator
)
existsConversionFunction
(
inType
,
outType
reflect
.
Type
)
bool
{
if
val
,
found
:=
g
.
convertibles
[
inType
];
found
&&
val
==
outType
{
return
true
}
if
val
,
found
:=
g
.
convertibles
[
outType
];
found
&&
val
==
inType
{
return
true
}
return
false
}
// 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
}
var
defaultConversions
[]
typePair
=
[]
typePair
{}
func
(
g
*
conversionGenerator
)
OverwritePackage
(
pkg
,
overwrite
string
)
{
g
.
pkgOverwrites
[
pkg
]
=
overwrite
}
pkg/runtime/conversion_generator_test.go
deleted
100644 → 0
View file @
d56ec66e
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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
runtime
import
(
"reflect"
"testing"
"k8s.io/kubernetes/pkg/api/unversioned"
)
type
InternalSubtype
struct
{
String
string
}
type
Internal
struct
{
TypeMeta
Bool
bool
Complex
InternalSubtype
}
type
ExternalSubtype
struct
{
String
string
}
type
External
struct
{
TypeMeta
Complex
ExternalSubtype
}
func
(
obj
*
Internal
)
GetObjectKind
()
unversioned
.
ObjectKind
{
return
&
obj
.
TypeMeta
}
func
(
obj
*
External
)
GetObjectKind
()
unversioned
.
ObjectKind
{
return
&
obj
.
TypeMeta
}
func
TestGenerateConversionsForStruct
(
t
*
testing
.
T
)
{
internalGV
:=
unversioned
.
GroupVersion
{
Group
:
"test.group"
,
Version
:
APIVersionInternal
}
externalGV
:=
unversioned
.
GroupVersion
{
Group
:
"test.group"
,
Version
:
"external"
}
scheme
:=
NewScheme
()
scheme
.
Log
(
t
)
scheme
.
AddKnownTypeWithName
(
internalGV
.
WithKind
(
"Complex"
),
&
Internal
{})
scheme
.
AddKnownTypeWithName
(
externalGV
.
WithKind
(
"Complex"
),
&
External
{})
generator
:=
NewConversionGenerator
(
scheme
,
"foo"
)
typedGenerator
,
ok
:=
generator
.
(
*
conversionGenerator
)
if
!
ok
{
t
.
Fatalf
(
"error converting to conversionGenerator"
)
}
internalType
:=
reflect
.
TypeOf
(
Internal
{})
externalType
:=
reflect
.
TypeOf
(
External
{})
err
:=
typedGenerator
.
generateConversionsForStruct
(
internalType
,
externalType
)
if
err
==
nil
{
t
.
Errorf
(
"expected error for asymmetrical field"
)
}
// we are expecting Convert_runtime_InternalSubtype_To_runtime_ExternalSubtype to be generated
// even though the conversion for the parent type cannot be auto generated
if
len
(
typedGenerator
.
publicFuncs
)
!=
1
{
t
.
Errorf
(
"expected to find one public conversion for the Complex type but found: %v"
,
typedGenerator
.
publicFuncs
)
}
}
pkg/runtime/swagger_doc_generator.go
View file @
ff1da767
...
@@ -113,6 +113,37 @@ func fieldName(field *ast.Field) string {
...
@@ -113,6 +113,37 @@ func fieldName(field *ast.Field) string {
return
jsonTag
return
jsonTag
}
}
// 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
}
func
writeFuncHeader
(
b
*
buffer
,
structName
string
,
indent
int
)
{
func
writeFuncHeader
(
b
*
buffer
,
structName
string
,
indent
int
)
{
s
:=
fmt
.
Sprintf
(
"var map_%s = map[string]string {
\n
"
,
structName
)
s
:=
fmt
.
Sprintf
(
"var map_%s = map[string]string {
\n
"
,
structName
)
b
.
addLine
(
s
,
indent
)
b
.
addLine
(
s
,
indent
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment