Skip to content

Commit 6148356

Browse files
authored
feat(bindnode): named type converters (#573)
allow custom converters based off schema name, rather than go type -- this is particularly useful for enabling interface members of structs to be used with bindnode
1 parent f75d95e commit 6148356

File tree

5 files changed

+459
-80
lines changed

5 files changed

+459
-80
lines changed

node/bindnode/api.go

+180-24
Original file line numberDiff line numberDiff line change
@@ -85,26 +85,38 @@ type converter struct {
8585
customToAny func(interface{}) (datamodel.Node, error)
8686
}
8787

88-
type config map[reflect.Type]*converter
88+
type config struct {
89+
namedConverters map[schema.TypeName]*converter
90+
typeConverters map[reflect.Type]*converter
91+
}
8992

9093
// this mainly exists to short-circuit the nonPtrType() call; the `Type()` variant
9194
// exists for completeness
92-
func (c config) converterFor(val reflect.Value) *converter {
93-
if len(c) == 0 {
95+
func (c *config) converterFor(typeName schema.TypeName, val reflect.Value) *converter {
96+
if c == nil {
9497
return nil
9598
}
96-
return c[nonPtrType(val)]
99+
100+
if namedConverter, ok := c.namedConverters[typeName]; ok {
101+
return namedConverter
102+
}
103+
104+
return c.typeConverters[nonPtrType(val)]
97105
}
98106

99-
func (c config) converterForType(typ reflect.Type) *converter {
100-
if len(c) == 0 {
107+
func (c *config) converterForType(typeName schema.TypeName, typ reflect.Type) *converter {
108+
if c == nil {
101109
return nil
102110
}
103-
return c[typ]
111+
if namedConverter, ok := c.namedConverters[typeName]; ok {
112+
return namedConverter
113+
}
114+
115+
return c.typeConverters[typ]
104116
}
105117

106118
// Option is able to apply custom options to the bindnode API
107-
type Option func(config)
119+
type Option func(*config)
108120

109121
// TypedBoolConverter adds custom converter functions for a particular
110122
// type as identified by a pointer in the first argument.
@@ -121,8 +133,8 @@ func TypedBoolConverter(ptrVal interface{}, from func(bool) (interface{}, error)
121133
customFromBool: from,
122134
customToBool: to,
123135
}
124-
return func(cfg config) {
125-
cfg[customType] = converter
136+
return func(cfg *config) {
137+
cfg.typeConverters[customType] = converter
126138
}
127139
}
128140

@@ -141,8 +153,8 @@ func TypedIntConverter(ptrVal interface{}, from func(int64) (interface{}, error)
141153
customFromInt: from,
142154
customToInt: to,
143155
}
144-
return func(cfg config) {
145-
cfg[customType] = converter
156+
return func(cfg *config) {
157+
cfg.typeConverters[customType] = converter
146158
}
147159
}
148160

@@ -161,8 +173,8 @@ func TypedFloatConverter(ptrVal interface{}, from func(float64) (interface{}, er
161173
customFromFloat: from,
162174
customToFloat: to,
163175
}
164-
return func(cfg config) {
165-
cfg[customType] = converter
176+
return func(cfg *config) {
177+
cfg.typeConverters[customType] = converter
166178
}
167179
}
168180

@@ -181,8 +193,8 @@ func TypedStringConverter(ptrVal interface{}, from func(string) (interface{}, er
181193
customFromString: from,
182194
customToString: to,
183195
}
184-
return func(cfg config) {
185-
cfg[customType] = converter
196+
return func(cfg *config) {
197+
cfg.typeConverters[customType] = converter
186198
}
187199
}
188200

@@ -201,8 +213,8 @@ func TypedBytesConverter(ptrVal interface{}, from func([]byte) (interface{}, err
201213
customFromBytes: from,
202214
customToBytes: to,
203215
}
204-
return func(cfg config) {
205-
cfg[customType] = converter
216+
return func(cfg *config) {
217+
cfg.typeConverters[customType] = converter
206218
}
207219
}
208220

@@ -225,8 +237,8 @@ func TypedLinkConverter(ptrVal interface{}, from func(cid.Cid) (interface{}, err
225237
customFromLink: from,
226238
customToLink: to,
227239
}
228-
return func(cfg config) {
229-
cfg[customType] = converter
240+
return func(cfg *config) {
241+
cfg.typeConverters[customType] = converter
230242
}
231243
}
232244

@@ -248,18 +260,162 @@ func TypedAnyConverter(ptrVal interface{}, from func(datamodel.Node) (interface{
248260
customFromAny: from,
249261
customToAny: to,
250262
}
251-
return func(cfg config) {
252-
cfg[customType] = converter
263+
return func(cfg *config) {
264+
cfg.typeConverters[customType] = converter
265+
}
266+
}
267+
268+
// NamedBoolConverter adds custom converter functions for given
269+
// named schema type.
270+
// The fromFunc is of the form: func(bool) (interface{}, error)
271+
// and toFunc is of the form: func(interface{}) (bool, error)
272+
// where interface{} is a pointer form of the type we are converting.
273+
//
274+
// NamedBoolConverter is an EXPERIMENTAL API and may be removed or
275+
// changed in a future release.
276+
func NamedBoolConverter(typeName schema.TypeName, from func(bool) (interface{}, error), to func(interface{}) (bool, error)) Option {
277+
converter := &converter{
278+
kind: schema.TypeKind_Bool,
279+
customFromBool: from,
280+
customToBool: to,
281+
}
282+
return func(cfg *config) {
283+
cfg.namedConverters[typeName] = converter
284+
}
285+
}
286+
287+
// NamedIntConverter adds custom converter functions for given
288+
// named schema type.
289+
// The fromFunc is of the form: func(int64) (interface{}, error)
290+
// and toFunc is of the form: func(interface{}) (int64, error)
291+
// where interface{} is a pointer form of the type we are converting.
292+
//
293+
// NamedIntConverter is an EXPERIMENTAL API and may be removed or
294+
// changed in a future release.
295+
func NamedIntConverter(typeName schema.TypeName, from func(int64) (interface{}, error), to func(interface{}) (int64, error)) Option {
296+
converter := &converter{
297+
kind: schema.TypeKind_Int,
298+
customFromInt: from,
299+
customToInt: to,
300+
}
301+
return func(cfg *config) {
302+
cfg.namedConverters[typeName] = converter
303+
}
304+
}
305+
306+
// NamedFloatConverter adds custom converter functions for given
307+
// named schema type.
308+
// The fromFunc is of the form: func(float64) (interface{}, error)
309+
// and toFunc is of the form: func(interface{}) (float64, error)
310+
// where interface{} is a pointer form of the type we are converting.
311+
//
312+
// NamedFloatConverter is an EXPERIMENTAL API and may be removed or
313+
// changed in a future release.
314+
func NamedFloatConverter(typeName schema.TypeName, from func(float64) (interface{}, error), to func(interface{}) (float64, error)) Option {
315+
converter := &converter{
316+
kind: schema.TypeKind_Float,
317+
customFromFloat: from,
318+
customToFloat: to,
319+
}
320+
return func(cfg *config) {
321+
cfg.namedConverters[typeName] = converter
322+
}
323+
}
324+
325+
// NamedStringConverter adds custom converter functions for given
326+
// named schema type.
327+
// The fromFunc is of the form: func(string) (interface{}, error)
328+
// and toFunc is of the form: func(interface{}) (string, error)
329+
// where interface{} is a pointer form of the type we are converting.
330+
//
331+
// NamedStringConverter is an EXPERIMENTAL API and may be removed or
332+
// changed in a future release.
333+
func NamedStringConverter(typeName schema.TypeName, from func(string) (interface{}, error), to func(interface{}) (string, error)) Option {
334+
converter := &converter{
335+
kind: schema.TypeKind_String,
336+
customFromString: from,
337+
customToString: to,
338+
}
339+
return func(cfg *config) {
340+
cfg.namedConverters[typeName] = converter
341+
}
342+
}
343+
344+
// NamedBytesConverter adds custom converter functions for given
345+
// named schema type.
346+
// The fromFunc is of the form: func([]byte) (interface{}, error)
347+
// and toFunc is of the form: func(interface{}) ([]byte, error)
348+
// where interface{} is a pointer form of the type we are converting.
349+
//
350+
// NamedBytesConverter is an EXPERIMENTAL API and may be removed or
351+
// changed in a future release.
352+
func NamedBytesConverter(typeName schema.TypeName, from func([]byte) (interface{}, error), to func(interface{}) ([]byte, error)) Option {
353+
converter := &converter{
354+
kind: schema.TypeKind_Bytes,
355+
customFromBytes: from,
356+
customToBytes: to,
357+
}
358+
return func(cfg *config) {
359+
cfg.namedConverters[typeName] = converter
360+
}
361+
}
362+
363+
// NamedLinkConverter adds custom converter functions for given
364+
// named schema type.
365+
// The fromFunc is of the form: func([]byte) (interface{}, error)
366+
// and toFunc is of the form: func(interface{}) ([]byte, error)
367+
// where interface{} is a pointer form of the type we are converting.
368+
//
369+
// Beware that this API is only compatible with cidlink.Link types in the data
370+
// model and may result in errors if attempting to convert from other
371+
// datamodel.Link types.
372+
//
373+
// NamedLinkConverter is an EXPERIMENTAL API and may be removed or
374+
// changed in a future release.
375+
func NamedLinkConverter(typeName schema.TypeName, from func(cid.Cid) (interface{}, error), to func(interface{}) (cid.Cid, error)) Option {
376+
converter := &converter{
377+
kind: schema.TypeKind_Link,
378+
customFromLink: from,
379+
customToLink: to,
380+
}
381+
return func(cfg *config) {
382+
cfg.namedConverters[typeName] = converter
383+
}
384+
}
385+
386+
// NamedAnyConverter adds custom converter functions for given
387+
// named schema type.
388+
// The fromFunc is of the form: func(datamodel.Node) (interface{}, error)
389+
// and toFunc is of the form: func(interface{}) (datamodel.Node, error)
390+
// where interface{} is a pointer form of the type we are converting.
391+
//
392+
// This method should be able to deal with all forms of Any and return an error
393+
// if the expected data forms don't match the expected.
394+
//
395+
// NamedAnyConverter is an EXPERIMENTAL API and may be removed or
396+
// changed in a future release.
397+
func NamedAnyConverter(typeName schema.TypeName, from func(datamodel.Node) (interface{}, error), to func(interface{}) (datamodel.Node, error)) Option {
398+
converter := &converter{
399+
kind: schema.TypeKind_Any,
400+
customFromAny: from,
401+
customToAny: to,
402+
}
403+
return func(cfg *config) {
404+
cfg.namedConverters[typeName] = converter
253405
}
254406
}
255407

256-
func applyOptions(opt ...Option) config {
408+
func applyOptions(opt ...Option) *config {
257409
if len(opt) == 0 {
258410
// no need to allocate, we access it via converterFor and converterForType
259411
// which are safe for nil maps
260412
return nil
261413
}
262-
cfg := make(map[reflect.Type]*converter)
414+
cfg := &config{
415+
namedConverters: make(map[string]*converter),
416+
typeConverters: make(map[reflect.Type]*converter),
417+
}
418+
263419
for _, o := range opt {
264420
o(cfg)
265421
}

0 commit comments

Comments
 (0)