Exposes compose `loader.Transform` function…

This should make it easier for people to write custom composefile
parser without duplicating too much code. It takes the default
transformers and any additional number of transformer for any
types. That way it's possible to transform a `cli/compose` map into a
custom type that would use some of `cli/compose` types and its own.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
This commit is contained in:
Vincent Demeester 2018-07-31 09:37:09 +02:00
parent b4e50635a2
commit 0246bc1b3b
No known key found for this signature in database
GPG Key ID: 083CC6FD6EB699A3
1 changed files with 20 additions and 8 deletions

View File

@ -265,11 +265,13 @@ func getServices(configDict map[string]interface{}) map[string]interface{} {
return map[string]interface{}{}
}
func transform(source map[string]interface{}, target interface{}) error {
// Transform converts the source map into the target struct with compose types transformer
// and the specified transformers if any.
func Transform(source map[string]interface{}, target interface{}, additionalTransformers ...Transformer) error {
data := mapstructure.Metadata{}
config := &mapstructure.DecoderConfig{
DecodeHook: mapstructure.ComposeDecodeHookFunc(
createTransformHook(),
createTransformHook(additionalTransformers...),
mapstructure.StringToTimeDurationHookFunc()),
Result: target,
Metadata: &data,
@ -281,7 +283,13 @@ func transform(source map[string]interface{}, target interface{}) error {
return decoder.Decode(source)
}
func createTransformHook() mapstructure.DecodeHookFuncType {
// Transformer defines a map to type transformer
type Transformer struct {
TypeOf reflect.Type
Func func(interface{}) (interface{}, error)
}
func createTransformHook(additionalTransformers ...Transformer) mapstructure.DecodeHookFuncType {
transforms := map[reflect.Type]func(interface{}) (interface{}, error){
reflect.TypeOf(types.External{}): transformExternal,
reflect.TypeOf(types.HealthCheckTest{}): transformHealthCheckTest,
@ -303,6 +311,10 @@ func createTransformHook() mapstructure.DecodeHookFuncType {
reflect.TypeOf(types.BuildConfig{}): transformBuildConfig,
}
for _, transformer := range additionalTransformers {
transforms[transformer.TypeOf] = transformer.Func
}
return func(_ reflect.Type, target reflect.Type, data interface{}) (interface{}, error) {
transform, ok := transforms[target]
if !ok {
@ -380,7 +392,7 @@ func LoadServices(servicesDict map[string]interface{}, workingDir string, lookup
// the serviceDict is not validated if directly used. Use Load() to enable validation
func LoadService(name string, serviceDict map[string]interface{}, workingDir string, lookupEnv template.Mapping) (*types.ServiceConfig, error) {
serviceConfig := &types.ServiceConfig{}
if err := transform(serviceDict, serviceConfig); err != nil {
if err := Transform(serviceDict, serviceConfig); err != nil {
return nil, err
}
serviceConfig.Name = name
@ -509,7 +521,7 @@ func transformUlimits(data interface{}) (interface{}, error) {
// the source Dict is not validated if directly used. Use Load() to enable validation
func LoadNetworks(source map[string]interface{}, version string) (map[string]types.NetworkConfig, error) {
networks := make(map[string]types.NetworkConfig)
err := transform(source, &networks)
err := Transform(source, &networks)
if err != nil {
return networks, err
}
@ -546,7 +558,7 @@ func externalVolumeError(volume, key string) error {
// the source Dict is not validated if directly used. Use Load() to enable validation
func LoadVolumes(source map[string]interface{}, version string) (map[string]types.VolumeConfig, error) {
volumes := make(map[string]types.VolumeConfig)
if err := transform(source, &volumes); err != nil {
if err := Transform(source, &volumes); err != nil {
return volumes, err
}
@ -583,7 +595,7 @@ func LoadVolumes(source map[string]interface{}, version string) (map[string]type
// the source Dict is not validated if directly used. Use Load() to enable validation
func LoadSecrets(source map[string]interface{}, details types.ConfigDetails) (map[string]types.SecretConfig, error) {
secrets := make(map[string]types.SecretConfig)
if err := transform(source, &secrets); err != nil {
if err := Transform(source, &secrets); err != nil {
return secrets, err
}
for name, secret := range secrets {
@ -602,7 +614,7 @@ func LoadSecrets(source map[string]interface{}, details types.ConfigDetails) (ma
// the source Dict is not validated if directly used. Use Load() to enable validation
func LoadConfigObjs(source map[string]interface{}, details types.ConfigDetails) (map[string]types.ConfigObjConfig, error) {
configs := make(map[string]types.ConfigObjConfig)
if err := transform(source, &configs); err != nil {
if err := Transform(source, &configs); err != nil {
return configs, err
}
for name, config := range configs {