1/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package conversion
18
19import (
20	"fmt"
21	"reflect"
22)
23
24type typePair struct {
25	source reflect.Type
26	dest   reflect.Type
27}
28
29type NameFunc func(t reflect.Type) string
30
31var DefaultNameFunc = func(t reflect.Type) string { return t.Name() }
32
33// ConversionFunc converts the object a into the object b, reusing arrays or objects
34// or pointers if necessary. It should return an error if the object cannot be converted
35// or if some data is invalid. If you do not wish a and b to share fields or nested
36// objects, you must copy a before calling this function.
37type ConversionFunc func(a, b interface{}, scope Scope) error
38
39// Converter knows how to convert one type to another.
40type Converter struct {
41	// Map from the conversion pair to a function which can
42	// do the conversion.
43	conversionFuncs          ConversionFuncs
44	generatedConversionFuncs ConversionFuncs
45
46	// Set of conversions that should be treated as a no-op
47	ignoredConversions        map[typePair]struct{}
48	ignoredUntypedConversions map[typePair]struct{}
49
50	// nameFunc is called to retrieve the name of a type; this name is used for the
51	// purpose of deciding whether two types match or not (i.e., will we attempt to
52	// do a conversion). The default returns the go type name.
53	nameFunc func(t reflect.Type) string
54}
55
56// NewConverter creates a new Converter object.
57func NewConverter(nameFn NameFunc) *Converter {
58	c := &Converter{
59		conversionFuncs:           NewConversionFuncs(),
60		generatedConversionFuncs:  NewConversionFuncs(),
61		ignoredConversions:        make(map[typePair]struct{}),
62		ignoredUntypedConversions: make(map[typePair]struct{}),
63		nameFunc:                  nameFn,
64	}
65	c.RegisterUntypedConversionFunc(
66		(*[]byte)(nil), (*[]byte)(nil),
67		func(a, b interface{}, s Scope) error {
68			return Convert_Slice_byte_To_Slice_byte(a.(*[]byte), b.(*[]byte), s)
69		},
70	)
71	return c
72}
73
74// WithConversions returns a Converter that is a copy of c but with the additional
75// fns merged on top.
76func (c *Converter) WithConversions(fns ConversionFuncs) *Converter {
77	copied := *c
78	copied.conversionFuncs = c.conversionFuncs.Merge(fns)
79	return &copied
80}
81
82// DefaultMeta returns meta for a given type.
83func (c *Converter) DefaultMeta(t reflect.Type) *Meta {
84	return &Meta{}
85}
86
87// Convert_Slice_byte_To_Slice_byte prevents recursing into every byte
88func Convert_Slice_byte_To_Slice_byte(in *[]byte, out *[]byte, s Scope) error {
89	if *in == nil {
90		*out = nil
91		return nil
92	}
93	*out = make([]byte, len(*in))
94	copy(*out, *in)
95	return nil
96}
97
98// Scope is passed to conversion funcs to allow them to continue an ongoing conversion.
99// If multiple converters exist in the system, Scope will allow you to use the correct one
100// from a conversion function--that is, the one your conversion function was called by.
101type Scope interface {
102	// Call Convert to convert sub-objects. Note that if you call it with your own exact
103	// parameters, you'll run out of stack space before anything useful happens.
104	Convert(src, dest interface{}) error
105
106	// Meta returns any information originally passed to Convert.
107	Meta() *Meta
108}
109
110func NewConversionFuncs() ConversionFuncs {
111	return ConversionFuncs{
112		untyped: make(map[typePair]ConversionFunc),
113	}
114}
115
116type ConversionFuncs struct {
117	untyped map[typePair]ConversionFunc
118}
119
120// AddUntyped adds the provided conversion function to the lookup table for the types that are
121// supplied as a and b. a and b must be pointers or an error is returned. This method overwrites
122// previously defined functions.
123func (c ConversionFuncs) AddUntyped(a, b interface{}, fn ConversionFunc) error {
124	tA, tB := reflect.TypeOf(a), reflect.TypeOf(b)
125	if tA.Kind() != reflect.Ptr {
126		return fmt.Errorf("the type %T must be a pointer to register as an untyped conversion", a)
127	}
128	if tB.Kind() != reflect.Ptr {
129		return fmt.Errorf("the type %T must be a pointer to register as an untyped conversion", b)
130	}
131	c.untyped[typePair{tA, tB}] = fn
132	return nil
133}
134
135// Merge returns a new ConversionFuncs that contains all conversions from
136// both other and c, with other conversions taking precedence.
137func (c ConversionFuncs) Merge(other ConversionFuncs) ConversionFuncs {
138	merged := NewConversionFuncs()
139	for k, v := range c.untyped {
140		merged.untyped[k] = v
141	}
142	for k, v := range other.untyped {
143		merged.untyped[k] = v
144	}
145	return merged
146}
147
148// Meta is supplied by Scheme, when it calls Convert.
149type Meta struct {
150	// Context is an optional field that callers may use to pass info to conversion functions.
151	Context interface{}
152}
153
154// scope contains information about an ongoing conversion.
155type scope struct {
156	converter *Converter
157	meta      *Meta
158}
159
160// Convert continues a conversion.
161func (s *scope) Convert(src, dest interface{}) error {
162	return s.converter.Convert(src, dest, s.meta)
163}
164
165// Meta returns the meta object that was originally passed to Convert.
166func (s *scope) Meta() *Meta {
167	return s.meta
168}
169
170// RegisterUntypedConversionFunc registers a function that converts between a and b by passing objects of those
171// types to the provided function. The function *must* accept objects of a and b - this machinery will not enforce
172// any other guarantee.
173func (c *Converter) RegisterUntypedConversionFunc(a, b interface{}, fn ConversionFunc) error {
174	return c.conversionFuncs.AddUntyped(a, b, fn)
175}
176
177// RegisterGeneratedUntypedConversionFunc registers a function that converts between a and b by passing objects of those
178// types to the provided function. The function *must* accept objects of a and b - this machinery will not enforce
179// any other guarantee.
180func (c *Converter) RegisterGeneratedUntypedConversionFunc(a, b interface{}, fn ConversionFunc) error {
181	return c.generatedConversionFuncs.AddUntyped(a, b, fn)
182}
183
184// RegisterIgnoredConversion registers a "no-op" for conversion, where any requested
185// conversion between from and to is ignored.
186func (c *Converter) RegisterIgnoredConversion(from, to interface{}) error {
187	typeFrom := reflect.TypeOf(from)
188	typeTo := reflect.TypeOf(to)
189	if reflect.TypeOf(from).Kind() != reflect.Ptr {
190		return fmt.Errorf("expected pointer arg for 'from' param 0, got: %v", typeFrom)
191	}
192	if typeTo.Kind() != reflect.Ptr {
193		return fmt.Errorf("expected pointer arg for 'to' param 1, got: %v", typeTo)
194	}
195	c.ignoredConversions[typePair{typeFrom.Elem(), typeTo.Elem()}] = struct{}{}
196	c.ignoredUntypedConversions[typePair{typeFrom, typeTo}] = struct{}{}
197	return nil
198}
199
200// Convert will translate src to dest if it knows how. Both must be pointers.
201// If no conversion func is registered and the default copying mechanism
202// doesn't work on this type pair, an error will be returned.
203// 'meta' is given to allow you to pass information to conversion functions,
204// it is not used by Convert() other than storing it in the scope.
205// Not safe for objects with cyclic references!
206func (c *Converter) Convert(src, dest interface{}, meta *Meta) error {
207	pair := typePair{reflect.TypeOf(src), reflect.TypeOf(dest)}
208	scope := &scope{
209		converter: c,
210		meta:      meta,
211	}
212
213	// ignore conversions of this type
214	if _, ok := c.ignoredUntypedConversions[pair]; ok {
215		return nil
216	}
217	if fn, ok := c.conversionFuncs.untyped[pair]; ok {
218		return fn(src, dest, scope)
219	}
220	if fn, ok := c.generatedConversionFuncs.untyped[pair]; ok {
221		return fn(src, dest, scope)
222	}
223
224	dv, err := EnforcePtr(dest)
225	if err != nil {
226		return err
227	}
228	sv, err := EnforcePtr(src)
229	if err != nil {
230		return err
231	}
232	return fmt.Errorf("converting (%s) to (%s): unknown conversion", sv.Type(), dv.Type())
233}
234