2017-06-26 21:46:34 +00:00
|
|
|
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
Add custom decoders to eliminate boilerplate
This change overhauls the approach to custom decoding. Instead of decoding
the parts of the struct that are "trivial" in one pass, and then patching up
the structure afterwards with custom decoding, the decoder itself understands
the notion of custom decoder functions.
First, the general purpose logic has moved out of pkg/pack/encoding and into
a new package, pkg/util/mapper. Most functions are now members of a new top-
level type, Mapper, which may be initialized with custom decoders. This
is a map from target type to a function that can decode objects into it.
Second, the AST-specific decoding logic is rewritten to use it. All AST nodes
are now supported, including definitions, statements, and expressions. The
overall approach here is to simply define a custom decoder for any interface
type that will occur in a node field position. The mapper, upon encountering
such a type, will consult the custom decoder map; if a decoder is found, it
will be used, otherwise an error results. This decoder then needs to switch
on the type discriminated kind field that is present in the metadata, creating
a concrete struct of the right type, and then converting it to the desired
interface type. Note that, subtly, interface types used only for "marker"
purposes don't require any custom decoding, because they do not appear in
field positions and therefore won't be encountered during the decoding process.
2017-01-16 17:41:26 +00:00
|
|
|
|
|
|
|
package mapper
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
)
|
|
|
|
|
Implement mapper.Encode "for real"
This change implements `mapper.Encode` "for real" (that is, in a way
that isn't a complete embarrassment). It uses the obvious reflection
trickery to encode a tagged struct and its values as a JSON-like
in-memory map and collection of keyed values.
During this, I took the opportunity to also clean up a few other things
that had been bugging me. Namely, the presence of `mapper.Object` was
always error prone, since it isn't a true "typedef" in the sence that
it carries extra RTTI. Instead of doing that, let's just use the real
`map[string]interface{}` "JSON-map-like" object type. Even better, we
no longer require resource providers to deal with the mapper
infrastructure. Instead, the `Check` function can simply return an
array of errors. It's still best practice to return field-specific errors
to facilitate better diagnostics, but it's no longer required; and I've
added `resource.NewFieldError` to eliminate the need to import mapper.
As of this change, we can also consistently emit RPC structs with `lumi`
tags, rather than `lumi` tags on the way in and `json` on the way out.
This completes pulumi/lumi#183.
2017-06-06 00:43:52 +00:00
|
|
|
// MappingError represents a collection of decoding errors, defined below.
|
|
|
|
type MappingError interface {
|
2017-03-12 21:13:44 +00:00
|
|
|
error
|
Implement mapper.Encode "for real"
This change implements `mapper.Encode` "for real" (that is, in a way
that isn't a complete embarrassment). It uses the obvious reflection
trickery to encode a tagged struct and its values as a JSON-like
in-memory map and collection of keyed values.
During this, I took the opportunity to also clean up a few other things
that had been bugging me. Namely, the presence of `mapper.Object` was
always error prone, since it isn't a true "typedef" in the sence that
it carries extra RTTI. Instead of doing that, let's just use the real
`map[string]interface{}` "JSON-map-like" object type. Even better, we
no longer require resource providers to deal with the mapper
infrastructure. Instead, the `Check` function can simply return an
array of errors. It's still best practice to return field-specific errors
to facilitate better diagnostics, but it's no longer required; and I've
added `resource.NewFieldError` to eliminate the need to import mapper.
As of this change, we can also consistently emit RPC structs with `lumi`
tags, rather than `lumi` tags on the way in and `json` on the way out.
This completes pulumi/lumi#183.
2017-06-06 00:43:52 +00:00
|
|
|
Failures() []error // the full set of errors (each of one of the below types).
|
|
|
|
AddFailure(err error) // registers a new failure.
|
Add custom decoders to eliminate boilerplate
This change overhauls the approach to custom decoding. Instead of decoding
the parts of the struct that are "trivial" in one pass, and then patching up
the structure afterwards with custom decoding, the decoder itself understands
the notion of custom decoder functions.
First, the general purpose logic has moved out of pkg/pack/encoding and into
a new package, pkg/util/mapper. Most functions are now members of a new top-
level type, Mapper, which may be initialized with custom decoders. This
is a map from target type to a function that can decode objects into it.
Second, the AST-specific decoding logic is rewritten to use it. All AST nodes
are now supported, including definitions, statements, and expressions. The
overall approach here is to simply define a custom decoder for any interface
type that will occur in a node field position. The mapper, upon encountering
such a type, will consult the custom decoder map; if a decoder is found, it
will be used, otherwise an error results. This decoder then needs to switch
on the type discriminated kind field that is present in the metadata, creating
a concrete struct of the right type, and then converting it to the desired
interface type. Note that, subtly, interface types used only for "marker"
purposes don't require any custom decoding, because they do not appear in
field positions and therefore won't be encountered during the decoding process.
2017-01-16 17:41:26 +00:00
|
|
|
}
|
|
|
|
|
Implement mapper.Encode "for real"
This change implements `mapper.Encode` "for real" (that is, in a way
that isn't a complete embarrassment). It uses the obvious reflection
trickery to encode a tagged struct and its values as a JSON-like
in-memory map and collection of keyed values.
During this, I took the opportunity to also clean up a few other things
that had been bugging me. Namely, the presence of `mapper.Object` was
always error prone, since it isn't a true "typedef" in the sence that
it carries extra RTTI. Instead of doing that, let's just use the real
`map[string]interface{}` "JSON-map-like" object type. Even better, we
no longer require resource providers to deal with the mapper
infrastructure. Instead, the `Check` function can simply return an
array of errors. It's still best practice to return field-specific errors
to facilitate better diagnostics, but it's no longer required; and I've
added `resource.NewFieldError` to eliminate the need to import mapper.
As of this change, we can also consistently emit RPC structs with `lumi`
tags, rather than `lumi` tags on the way in and `json` on the way out.
This completes pulumi/lumi#183.
2017-06-06 00:43:52 +00:00
|
|
|
// mappingError is a concrete implementation of MappingError; it is private, and we prefer to use the above interface
|
2017-03-12 21:13:44 +00:00
|
|
|
// type, to avoid tricky non-nil nils in common usage patterns (see https://golang.org/doc/faq#nil_error).
|
Implement mapper.Encode "for real"
This change implements `mapper.Encode` "for real" (that is, in a way
that isn't a complete embarrassment). It uses the obvious reflection
trickery to encode a tagged struct and its values as a JSON-like
in-memory map and collection of keyed values.
During this, I took the opportunity to also clean up a few other things
that had been bugging me. Namely, the presence of `mapper.Object` was
always error prone, since it isn't a true "typedef" in the sence that
it carries extra RTTI. Instead of doing that, let's just use the real
`map[string]interface{}` "JSON-map-like" object type. Even better, we
no longer require resource providers to deal with the mapper
infrastructure. Instead, the `Check` function can simply return an
array of errors. It's still best practice to return field-specific errors
to facilitate better diagnostics, but it's no longer required; and I've
added `resource.NewFieldError` to eliminate the need to import mapper.
As of this change, we can also consistently emit RPC structs with `lumi`
tags, rather than `lumi` tags on the way in and `json` on the way out.
This completes pulumi/lumi#183.
2017-06-06 00:43:52 +00:00
|
|
|
type mappingError struct {
|
|
|
|
failures []error
|
Add custom decoders to eliminate boilerplate
This change overhauls the approach to custom decoding. Instead of decoding
the parts of the struct that are "trivial" in one pass, and then patching up
the structure afterwards with custom decoding, the decoder itself understands
the notion of custom decoder functions.
First, the general purpose logic has moved out of pkg/pack/encoding and into
a new package, pkg/util/mapper. Most functions are now members of a new top-
level type, Mapper, which may be initialized with custom decoders. This
is a map from target type to a function that can decode objects into it.
Second, the AST-specific decoding logic is rewritten to use it. All AST nodes
are now supported, including definitions, statements, and expressions. The
overall approach here is to simply define a custom decoder for any interface
type that will occur in a node field position. The mapper, upon encountering
such a type, will consult the custom decoder map; if a decoder is found, it
will be used, otherwise an error results. This decoder then needs to switch
on the type discriminated kind field that is present in the metadata, creating
a concrete struct of the right type, and then converting it to the desired
interface type. Note that, subtly, interface types used only for "marker"
purposes don't require any custom decoding, because they do not appear in
field positions and therefore won't be encountered during the decoding process.
2017-01-16 17:41:26 +00:00
|
|
|
}
|
2017-03-12 21:13:44 +00:00
|
|
|
|
Implement mapper.Encode "for real"
This change implements `mapper.Encode` "for real" (that is, in a way
that isn't a complete embarrassment). It uses the obvious reflection
trickery to encode a tagged struct and its values as a JSON-like
in-memory map and collection of keyed values.
During this, I took the opportunity to also clean up a few other things
that had been bugging me. Namely, the presence of `mapper.Object` was
always error prone, since it isn't a true "typedef" in the sence that
it carries extra RTTI. Instead of doing that, let's just use the real
`map[string]interface{}` "JSON-map-like" object type. Even better, we
no longer require resource providers to deal with the mapper
infrastructure. Instead, the `Check` function can simply return an
array of errors. It's still best practice to return field-specific errors
to facilitate better diagnostics, but it's no longer required; and I've
added `resource.NewFieldError` to eliminate the need to import mapper.
As of this change, we can also consistently emit RPC structs with `lumi`
tags, rather than `lumi` tags on the way in and `json` on the way out.
This completes pulumi/lumi#183.
2017-06-06 00:43:52 +00:00
|
|
|
var _ error = (*mappingError)(nil) // ensure this implements the error interface.
|
2017-03-12 21:13:44 +00:00
|
|
|
|
Implement mapper.Encode "for real"
This change implements `mapper.Encode` "for real" (that is, in a way
that isn't a complete embarrassment). It uses the obvious reflection
trickery to encode a tagged struct and its values as a JSON-like
in-memory map and collection of keyed values.
During this, I took the opportunity to also clean up a few other things
that had been bugging me. Namely, the presence of `mapper.Object` was
always error prone, since it isn't a true "typedef" in the sence that
it carries extra RTTI. Instead of doing that, let's just use the real
`map[string]interface{}` "JSON-map-like" object type. Even better, we
no longer require resource providers to deal with the mapper
infrastructure. Instead, the `Check` function can simply return an
array of errors. It's still best practice to return field-specific errors
to facilitate better diagnostics, but it's no longer required; and I've
added `resource.NewFieldError` to eliminate the need to import mapper.
As of this change, we can also consistently emit RPC structs with `lumi`
tags, rather than `lumi` tags on the way in and `json` on the way out.
This completes pulumi/lumi#183.
2017-06-06 00:43:52 +00:00
|
|
|
func NewMappingError(errs []error) MappingError {
|
|
|
|
return &mappingError{failures: errs}
|
2017-03-12 21:13:44 +00:00
|
|
|
}
|
|
|
|
|
Implement mapper.Encode "for real"
This change implements `mapper.Encode` "for real" (that is, in a way
that isn't a complete embarrassment). It uses the obvious reflection
trickery to encode a tagged struct and its values as a JSON-like
in-memory map and collection of keyed values.
During this, I took the opportunity to also clean up a few other things
that had been bugging me. Namely, the presence of `mapper.Object` was
always error prone, since it isn't a true "typedef" in the sence that
it carries extra RTTI. Instead of doing that, let's just use the real
`map[string]interface{}` "JSON-map-like" object type. Even better, we
no longer require resource providers to deal with the mapper
infrastructure. Instead, the `Check` function can simply return an
array of errors. It's still best practice to return field-specific errors
to facilitate better diagnostics, but it's no longer required; and I've
added `resource.NewFieldError` to eliminate the need to import mapper.
As of this change, we can also consistently emit RPC structs with `lumi`
tags, rather than `lumi` tags on the way in and `json` on the way out.
This completes pulumi/lumi#183.
2017-06-06 00:43:52 +00:00
|
|
|
func (e *mappingError) Failures() []error { return e.failures }
|
2017-03-12 21:13:44 +00:00
|
|
|
|
Implement mapper.Encode "for real"
This change implements `mapper.Encode` "for real" (that is, in a way
that isn't a complete embarrassment). It uses the obvious reflection
trickery to encode a tagged struct and its values as a JSON-like
in-memory map and collection of keyed values.
During this, I took the opportunity to also clean up a few other things
that had been bugging me. Namely, the presence of `mapper.Object` was
always error prone, since it isn't a true "typedef" in the sence that
it carries extra RTTI. Instead of doing that, let's just use the real
`map[string]interface{}` "JSON-map-like" object type. Even better, we
no longer require resource providers to deal with the mapper
infrastructure. Instead, the `Check` function can simply return an
array of errors. It's still best practice to return field-specific errors
to facilitate better diagnostics, but it's no longer required; and I've
added `resource.NewFieldError` to eliminate the need to import mapper.
As of this change, we can also consistently emit RPC structs with `lumi`
tags, rather than `lumi` tags on the way in and `json` on the way out.
This completes pulumi/lumi#183.
2017-06-06 00:43:52 +00:00
|
|
|
func (e *mappingError) AddFailure(err error) {
|
2017-03-12 21:13:44 +00:00
|
|
|
e.failures = append(e.failures, err)
|
|
|
|
}
|
|
|
|
|
Implement mapper.Encode "for real"
This change implements `mapper.Encode` "for real" (that is, in a way
that isn't a complete embarrassment). It uses the obvious reflection
trickery to encode a tagged struct and its values as a JSON-like
in-memory map and collection of keyed values.
During this, I took the opportunity to also clean up a few other things
that had been bugging me. Namely, the presence of `mapper.Object` was
always error prone, since it isn't a true "typedef" in the sence that
it carries extra RTTI. Instead of doing that, let's just use the real
`map[string]interface{}` "JSON-map-like" object type. Even better, we
no longer require resource providers to deal with the mapper
infrastructure. Instead, the `Check` function can simply return an
array of errors. It's still best practice to return field-specific errors
to facilitate better diagnostics, but it's no longer required; and I've
added `resource.NewFieldError` to eliminate the need to import mapper.
As of this change, we can also consistently emit RPC structs with `lumi`
tags, rather than `lumi` tags on the way in and `json` on the way out.
This completes pulumi/lumi#183.
2017-06-06 00:43:52 +00:00
|
|
|
func (e *mappingError) Error() string {
|
|
|
|
str := fmt.Sprintf("%d failures decoding:", len(e.failures))
|
2017-03-12 21:13:44 +00:00
|
|
|
for _, failure := range e.failures {
|
Implement mapper.Encode "for real"
This change implements `mapper.Encode` "for real" (that is, in a way
that isn't a complete embarrassment). It uses the obvious reflection
trickery to encode a tagged struct and its values as a JSON-like
in-memory map and collection of keyed values.
During this, I took the opportunity to also clean up a few other things
that had been bugging me. Namely, the presence of `mapper.Object` was
always error prone, since it isn't a true "typedef" in the sence that
it carries extra RTTI. Instead of doing that, let's just use the real
`map[string]interface{}` "JSON-map-like" object type. Even better, we
no longer require resource providers to deal with the mapper
infrastructure. Instead, the `Check` function can simply return an
array of errors. It's still best practice to return field-specific errors
to facilitate better diagnostics, but it's no longer required; and I've
added `resource.NewFieldError` to eliminate the need to import mapper.
As of this change, we can also consistently emit RPC structs with `lumi`
tags, rather than `lumi` tags on the way in and `json` on the way out.
This completes pulumi/lumi#183.
2017-06-06 00:43:52 +00:00
|
|
|
switch f := failure.(type) {
|
|
|
|
case FieldError:
|
|
|
|
str += fmt.Sprintf("\n\t%v: %v", f.Field(), f.Reason())
|
|
|
|
default:
|
|
|
|
str += fmt.Sprintf("\n\t%v", f)
|
|
|
|
}
|
2017-03-12 21:13:44 +00:00
|
|
|
}
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
|
|
|
|
// FieldError represents a failure during decoding of a specific field.
|
|
|
|
type FieldError interface {
|
|
|
|
error
|
|
|
|
Field() string // returns the name of the field with a problem.
|
|
|
|
Reason() string // returns a full diagnostic string about the error.
|
|
|
|
}
|
|
|
|
|
|
|
|
// fieldError is used when a general purpose error occurs decoding a field.
|
|
|
|
type fieldError struct {
|
Simplify Check; make it tolerant of computed values
This change simplifies the generated Check interface for providers.
Instead of
Check(ctx context.Context, obj *T) ([]error, error)
where T is the resource type, we have
Check(ctx context.Context, obj *T, property string) error
This is done so that we can drive the calls to Check one property
at a time, allowing us to skip any that are computed. (Otherwise,
we may fail the verification erroneously.)
This has the added advantage that the Check implementations are
simpler and can simply return a single error. Furthermore, the
generated RPC code handles wrapping the result, so we can just do
return errors.New("bad");
rather than the previous reflection-laden junk
return resource.NewFieldError(
reflect.TypeOf(obj), awsservice.AWSResource_Property,
errors.New("bad"))
2017-06-16 20:34:11 +00:00
|
|
|
Type string
|
2017-03-12 21:13:44 +00:00
|
|
|
Fld string
|
|
|
|
Message string
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ error = (*fieldError)(nil) // ensure this implements the error interface.
|
|
|
|
var _ FieldError = (*fieldError)(nil) // ensure this implements the fieldError interface.
|
|
|
|
|
Simplify Check; make it tolerant of computed values
This change simplifies the generated Check interface for providers.
Instead of
Check(ctx context.Context, obj *T) ([]error, error)
where T is the resource type, we have
Check(ctx context.Context, obj *T, property string) error
This is done so that we can drive the calls to Check one property
at a time, allowing us to skip any that are computed. (Otherwise,
we may fail the verification erroneously.)
This has the added advantage that the Check implementations are
simpler and can simply return a single error. Furthermore, the
generated RPC code handles wrapping the result, so we can just do
return errors.New("bad");
rather than the previous reflection-laden junk
return resource.NewFieldError(
reflect.TypeOf(obj), awsservice.AWSResource_Property,
errors.New("bad"))
2017-06-16 20:34:11 +00:00
|
|
|
func NewFieldError(ty string, fld string, err error) FieldError {
|
2017-03-12 21:13:44 +00:00
|
|
|
return &fieldError{
|
|
|
|
Type: ty,
|
|
|
|
Fld: fld,
|
|
|
|
Message: fmt.Sprintf("An error occurred decoding '%v.%v': %v", ty, fld, err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Simplify Check; make it tolerant of computed values
This change simplifies the generated Check interface for providers.
Instead of
Check(ctx context.Context, obj *T) ([]error, error)
where T is the resource type, we have
Check(ctx context.Context, obj *T, property string) error
This is done so that we can drive the calls to Check one property
at a time, allowing us to skip any that are computed. (Otherwise,
we may fail the verification erroneously.)
This has the added advantage that the Check implementations are
simpler and can simply return a single error. Furthermore, the
generated RPC code handles wrapping the result, so we can just do
return errors.New("bad");
rather than the previous reflection-laden junk
return resource.NewFieldError(
reflect.TypeOf(obj), awsservice.AWSResource_Property,
errors.New("bad"))
2017-06-16 20:34:11 +00:00
|
|
|
func NewTypeFieldError(ty reflect.Type, fld string, err error) FieldError {
|
|
|
|
return NewFieldError(ty.Name(), fld, err)
|
|
|
|
}
|
|
|
|
|
2017-03-12 21:13:44 +00:00
|
|
|
func (e *fieldError) Error() string { return e.Message }
|
|
|
|
func (e *fieldError) Field() string { return e.Fld }
|
|
|
|
func (e *fieldError) Reason() string { return e.Message }
|
|
|
|
|
|
|
|
// MissingError is used when a required field is missing on an object of a given type.
|
|
|
|
type MissingError struct {
|
|
|
|
Type reflect.Type
|
|
|
|
Fld string
|
|
|
|
Message string
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ error = (*MissingError)(nil) // ensure this implements the error interface.
|
|
|
|
var _ FieldError = (*MissingError)(nil) // ensure this implements the FieldError interface.
|
|
|
|
|
Implement mapper.Encode "for real"
This change implements `mapper.Encode` "for real" (that is, in a way
that isn't a complete embarrassment). It uses the obvious reflection
trickery to encode a tagged struct and its values as a JSON-like
in-memory map and collection of keyed values.
During this, I took the opportunity to also clean up a few other things
that had been bugging me. Namely, the presence of `mapper.Object` was
always error prone, since it isn't a true "typedef" in the sence that
it carries extra RTTI. Instead of doing that, let's just use the real
`map[string]interface{}` "JSON-map-like" object type. Even better, we
no longer require resource providers to deal with the mapper
infrastructure. Instead, the `Check` function can simply return an
array of errors. It's still best practice to return field-specific errors
to facilitate better diagnostics, but it's no longer required; and I've
added `resource.NewFieldError` to eliminate the need to import mapper.
As of this change, we can also consistently emit RPC structs with `lumi`
tags, rather than `lumi` tags on the way in and `json` on the way out.
This completes pulumi/lumi#183.
2017-06-06 00:43:52 +00:00
|
|
|
func NewMissingError(ty reflect.Type, fld string) *MissingError {
|
2017-03-12 21:13:44 +00:00
|
|
|
return &MissingError{
|
|
|
|
Type: ty,
|
|
|
|
Fld: fld,
|
|
|
|
Message: fmt.Sprintf("Missing required field '%v' on '%v'", fld, ty),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *MissingError) Error() string { return e.Message }
|
|
|
|
func (e *MissingError) Field() string { return e.Fld }
|
|
|
|
func (e *MissingError) Reason() string { return e.Message }
|
|
|
|
|
|
|
|
// UnrecognizedError is used when a field is unrecognized on the given type.
|
|
|
|
type UnrecognizedError struct {
|
|
|
|
Type reflect.Type
|
|
|
|
Fld string
|
|
|
|
Message string
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ error = (*UnrecognizedError)(nil) // ensure this implements the error interface.
|
|
|
|
var _ FieldError = (*UnrecognizedError)(nil) // ensure this implements the FieldError interface.
|
|
|
|
|
Implement mapper.Encode "for real"
This change implements `mapper.Encode` "for real" (that is, in a way
that isn't a complete embarrassment). It uses the obvious reflection
trickery to encode a tagged struct and its values as a JSON-like
in-memory map and collection of keyed values.
During this, I took the opportunity to also clean up a few other things
that had been bugging me. Namely, the presence of `mapper.Object` was
always error prone, since it isn't a true "typedef" in the sence that
it carries extra RTTI. Instead of doing that, let's just use the real
`map[string]interface{}` "JSON-map-like" object type. Even better, we
no longer require resource providers to deal with the mapper
infrastructure. Instead, the `Check` function can simply return an
array of errors. It's still best practice to return field-specific errors
to facilitate better diagnostics, but it's no longer required; and I've
added `resource.NewFieldError` to eliminate the need to import mapper.
As of this change, we can also consistently emit RPC structs with `lumi`
tags, rather than `lumi` tags on the way in and `json` on the way out.
This completes pulumi/lumi#183.
2017-06-06 00:43:52 +00:00
|
|
|
func NewUnrecognizedError(ty reflect.Type, fld string) *UnrecognizedError {
|
2017-03-12 21:13:44 +00:00
|
|
|
return &UnrecognizedError{
|
|
|
|
Type: ty,
|
|
|
|
Fld: fld,
|
|
|
|
Message: fmt.Sprintf("Unrecognized field '%v' on '%v'", fld, ty),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *UnrecognizedError) Error() string { return e.Message }
|
|
|
|
func (e *UnrecognizedError) Field() string { return e.Fld }
|
|
|
|
func (e *UnrecognizedError) Reason() string { return e.Message }
|
|
|
|
|
|
|
|
type WrongTypeError struct {
|
|
|
|
Type reflect.Type
|
|
|
|
Fld string
|
|
|
|
Expect reflect.Type
|
|
|
|
Actual reflect.Type
|
|
|
|
Message string
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ error = (*WrongTypeError)(nil) // ensure this implements the error interface.
|
|
|
|
var _ FieldError = (*WrongTypeError)(nil) // ensure this implements the FieldError interface.
|
|
|
|
|
Implement mapper.Encode "for real"
This change implements `mapper.Encode` "for real" (that is, in a way
that isn't a complete embarrassment). It uses the obvious reflection
trickery to encode a tagged struct and its values as a JSON-like
in-memory map and collection of keyed values.
During this, I took the opportunity to also clean up a few other things
that had been bugging me. Namely, the presence of `mapper.Object` was
always error prone, since it isn't a true "typedef" in the sence that
it carries extra RTTI. Instead of doing that, let's just use the real
`map[string]interface{}` "JSON-map-like" object type. Even better, we
no longer require resource providers to deal with the mapper
infrastructure. Instead, the `Check` function can simply return an
array of errors. It's still best practice to return field-specific errors
to facilitate better diagnostics, but it's no longer required; and I've
added `resource.NewFieldError` to eliminate the need to import mapper.
As of this change, we can also consistently emit RPC structs with `lumi`
tags, rather than `lumi` tags on the way in and `json` on the way out.
This completes pulumi/lumi#183.
2017-06-06 00:43:52 +00:00
|
|
|
func NewWrongTypeError(ty reflect.Type, fld string, expect reflect.Type, actual reflect.Type) *WrongTypeError {
|
2017-03-12 21:13:44 +00:00
|
|
|
return &WrongTypeError{
|
|
|
|
Type: ty,
|
|
|
|
Fld: fld,
|
|
|
|
Expect: expect,
|
|
|
|
Actual: actual,
|
|
|
|
Message: fmt.Sprintf(
|
|
|
|
"Field '%v' on '%v' must be a '%v'; got '%v' instead", fld, ty, expect, actual),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *WrongTypeError) Error() string { return e.Message }
|
|
|
|
func (e *WrongTypeError) Field() string { return e.Fld }
|
|
|
|
func (e *WrongTypeError) Reason() string { return e.Message }
|