feat: export dynfunc errors

This commit is contained in:
Adrien Marquès 2021-06-20 21:52:11 +02:00
parent b4a426adcc
commit 8c122e9ddf
4 changed files with 69 additions and 67 deletions

View File

@ -1,50 +1,52 @@
package dynfunc
// cerr allows you to create constant "const" error with type boxing.
type cerr string
// Err allows you to create constant "const" error with type boxing.
type Err string
func (err cerr) Error() string {
func (err Err) Error() string {
return string(err)
}
// errHandlerNotFunc - handler is not a func
const errHandlerNotFunc = cerr("handler must be a func")
const (
// ErrHandlerNotFunc - handler is not a func
ErrHandlerNotFunc = Err("handler must be a func")
// errNoServiceForHandler - no service matching this handler
const errNoServiceForHandler = cerr("no service found for this handler")
// ErrNoServiceForHandler - no service matching this handler
ErrNoServiceForHandler = Err("no service found for this handler")
// errMissingHandlerArgumentParam - missing params arguments for handler
const errMissingHandlerContextArgument = cerr("missing handler first argument of type context.Context")
// errMissingHandlerArgumentParam - missing params arguments for handler
ErrMissingHandlerContextArgument = Err("missing handler first argument of type context.Context")
// errMissingHandlerInputArgument - missing params arguments for handler
const errMissingHandlerInputArgument = cerr("missing handler argument: input struct")
// ErrMissingHandlerInputArgument - missing params arguments for handler
ErrMissingHandlerInputArgument = Err("missing handler argument: input struct")
// errUnexpectedInput - input argument is not expected
const errUnexpectedInput = cerr("unexpected input struct")
// ErrUnexpectedInput - input argument is not expected
ErrUnexpectedInput = Err("unexpected input struct")
// errMissingHandlerOutputArgument - missing output for handler
const errMissingHandlerOutputArgument = cerr("missing handler first output argument: output struct")
// ErrMissingHandlerOutputArgument - missing output for handler
ErrMissingHandlerOutputArgument = Err("missing handler first output argument: output struct")
// errMissingHandlerOutputError - missing error output for handler
const errMissingHandlerOutputError = cerr("missing handler last output argument of type api.Err")
// ErrMissingHandlerOutputError - missing error output for handler
ErrMissingHandlerOutputError = Err("missing handler last output argument of type api.Err")
// errMissingRequestArgument - missing request argument for handler
const errMissingRequestArgument = cerr("handler first argument must be of type api.Request")
// ErrMissingRequestArgument - missing request argument for handler
ErrMissingRequestArgument = Err("handler first argument must be of type api.Request")
// errMissingParamArgument - missing parameters argument for handler
const errMissingParamArgument = cerr("handler second argument must be a struct")
// ErrMissingParamArgument - missing parameters argument for handler
ErrMissingParamArgument = Err("handler second argument must be a struct")
// errUnexportedName - argument is unexported in struct
const errUnexportedName = cerr("unexported name")
// ErrUnexportedName - argument is unexported in struct
ErrUnexportedName = Err("unexported name")
// errWrongOutputArgumentType - wrong type for output first argument
const errWrongOutputArgumentType = cerr("handler first output argument must be a *struct")
// ErrWrongOutputArgumentType - wrong type for output first argument
ErrWrongOutputArgumentType = Err("handler first output argument must be a *struct")
// errMissingConfigArgument - missing an input/output argument in handler struct
const errMissingConfigArgument = cerr("missing an argument from the configuration")
// ErrMissingConfigArgument - missing an input/output argument in handler struct
ErrMissingConfigArgument = Err("missing an argument from the configuration")
// errWrongParamTypeFromConfig - a configuration parameter type is invalid in the handler param struct
const errWrongParamTypeFromConfig = cerr("invalid struct field type")
// ErrWrongParamTypeFromConfig - a configuration parameter type is invalid in the handler param struct
ErrWrongParamTypeFromConfig = Err("invalid struct field type")
// errMissingHandlerErrorArgument - missing handler output error
const errMissingHandlerErrorArgument = cerr("last output must be of type api.Err")
// ErrMissingHandlerErrorArgument - missing handler output error
ErrMissingHandlerErrorArgument = Err("last output must be of type api.Err")
)

View File

@ -38,7 +38,7 @@ func Build(fn interface{}, service config.Service) (*Handler, error) {
)
if fnType.Kind() != reflect.Func {
return nil, errHandlerNotFunc
return nil, ErrHandlerNotFunc
}
if err := h.signature.ValidateInput(fnType); err != nil {
return nil, fmt.Errorf("input: %w", err)

View File

@ -53,7 +53,7 @@ func (s *Signature) ValidateInput(handlerType reflect.Type) error {
// missing or invalid first arg: context.Context
if handlerType.NumIn() < 1 {
return errMissingHandlerContextArgument
return ErrMissingHandlerContextArgument
}
firstArgType := handlerType.In(0)
@ -65,35 +65,35 @@ func (s *Signature) ValidateInput(handlerType reflect.Type) error {
if len(s.Input) == 0 {
// input struct provided
if handlerType.NumIn() > 1 {
return errUnexpectedInput
return ErrUnexpectedInput
}
return nil
}
// too much arguments
if handlerType.NumIn() > 2 {
return errMissingHandlerInputArgument
return ErrMissingHandlerInputArgument
}
// arg must be a struct
inStruct := handlerType.In(1)
if inStruct.Kind() != reflect.Struct {
return errMissingParamArgument
return ErrMissingParamArgument
}
// check for invalid param
for name, ptype := range s.Input {
if name[0] == strings.ToLower(name)[0] {
return fmt.Errorf("%s: %w", name, errUnexportedName)
return fmt.Errorf("%s: %w", name, ErrUnexportedName)
}
field, exists := inStruct.FieldByName(name)
if !exists {
return fmt.Errorf("%s: %w", name, errMissingConfigArgument)
return fmt.Errorf("%s: %w", name, ErrMissingConfigArgument)
}
if !ptype.AssignableTo(field.Type) {
return fmt.Errorf("%s: %w (%s instead of %s)", name, errWrongParamTypeFromConfig, field.Type, ptype)
return fmt.Errorf("%s: %w (%s instead of %s)", name, ErrWrongParamTypeFromConfig, field.Type, ptype)
}
}
@ -105,13 +105,13 @@ func (s Signature) ValidateOutput(handlerType reflect.Type) error {
errType := reflect.TypeOf(api.ErrUnknown)
if handlerType.NumOut() < 1 {
return errMissingHandlerErrorArgument
return ErrMissingHandlerErrorArgument
}
// last output must be api.Err
lastArgType := handlerType.Out(handlerType.NumOut() - 1)
if !lastArgType.AssignableTo(errType) {
return errMissingHandlerErrorArgument
return ErrMissingHandlerErrorArgument
}
// no output -> ok
@ -120,29 +120,29 @@ func (s Signature) ValidateOutput(handlerType reflect.Type) error {
}
if handlerType.NumOut() < 2 {
return errMissingHandlerOutputArgument
return ErrMissingHandlerOutputArgument
}
// fail if first output is not a pointer to struct
outStructPtr := handlerType.Out(0)
if outStructPtr.Kind() != reflect.Ptr {
return errWrongOutputArgumentType
return ErrWrongOutputArgumentType
}
outStruct := outStructPtr.Elem()
if outStruct.Kind() != reflect.Struct {
return errWrongOutputArgumentType
return ErrWrongOutputArgumentType
}
// fail on invalid output
for name, ptype := range s.Output {
if name[0] == strings.ToLower(name)[0] {
return fmt.Errorf("%s: %w", name, errUnexportedName)
return fmt.Errorf("%s: %w", name, ErrUnexportedName)
}
field, exists := outStruct.FieldByName(name)
if !exists {
return fmt.Errorf("%s: %w", name, errMissingConfigArgument)
return fmt.Errorf("%s: %w", name, ErrMissingConfigArgument)
}
// ignore types evalutating to nil
@ -151,7 +151,7 @@ func (s Signature) ValidateOutput(handlerType reflect.Type) error {
}
if !field.Type.ConvertibleTo(ptype) {
return fmt.Errorf("%s: %w (%s instead of %s)", name, errWrongParamTypeFromConfig, field.Type, ptype)
return fmt.Errorf("%s: %w (%s instead of %s)", name, ErrWrongParamTypeFromConfig, field.Type, ptype)
}
}

View File

@ -30,14 +30,14 @@ func TestInputCheck(t *testing.T) {
Input: map[string]reflect.Type{},
Fn: func(context.Context, int) {},
FnCtx: func(context.Context, int) {},
Err: errUnexpectedInput,
Err: ErrUnexpectedInput,
},
{
Name: "no input 2 given",
Input: map[string]reflect.Type{},
Fn: func(context.Context, int, string) {},
FnCtx: func(context.Context, int, string) {},
Err: errUnexpectedInput,
Err: ErrUnexpectedInput,
},
{
Name: "1 input 0 given",
@ -46,7 +46,7 @@ func TestInputCheck(t *testing.T) {
},
Fn: func(context.Context) {},
FnCtx: func(context.Context) {},
Err: errMissingHandlerInputArgument,
Err: ErrMissingHandlerInputArgument,
},
{
Name: "1 input non-struct given",
@ -55,7 +55,7 @@ func TestInputCheck(t *testing.T) {
},
Fn: func(context.Context, int) {},
FnCtx: func(context.Context, int) {},
Err: errMissingParamArgument,
Err: ErrMissingParamArgument,
},
{
Name: "unexported input",
@ -64,7 +64,7 @@ func TestInputCheck(t *testing.T) {
},
Fn: func(context.Context, struct{}) {},
FnCtx: func(context.Context, struct{}) {},
Err: errUnexportedName,
Err: ErrUnexportedName,
},
{
Name: "1 input empty struct given",
@ -73,7 +73,7 @@ func TestInputCheck(t *testing.T) {
},
Fn: func(context.Context, struct{}) {},
FnCtx: func(context.Context, struct{}) {},
Err: errMissingConfigArgument,
Err: ErrMissingConfigArgument,
},
{
Name: "1 input invalid given",
@ -82,7 +82,7 @@ func TestInputCheck(t *testing.T) {
},
Fn: func(context.Context, struct{ Test1 string }) {},
FnCtx: func(context.Context, struct{ Test1 string }) {},
Err: errWrongParamTypeFromConfig,
Err: ErrWrongParamTypeFromConfig,
},
{
Name: "1 input valid given",
@ -100,7 +100,7 @@ func TestInputCheck(t *testing.T) {
},
Fn: func(context.Context, struct{}) {},
FnCtx: func(context.Context, struct{}) {},
Err: errMissingConfigArgument,
Err: ErrMissingConfigArgument,
},
{
Name: "1 input ptr invalid given",
@ -109,7 +109,7 @@ func TestInputCheck(t *testing.T) {
},
Fn: func(context.Context, struct{ Test1 string }) {},
FnCtx: func(context.Context, struct{ Test1 string }) {},
Err: errWrongParamTypeFromConfig,
Err: ErrWrongParamTypeFromConfig,
},
{
Name: "1 input ptr invalid ptr type given",
@ -118,7 +118,7 @@ func TestInputCheck(t *testing.T) {
},
Fn: func(context.Context, struct{ Test1 *string }) {},
FnCtx: func(context.Context, struct{ Test1 *string }) {},
Err: errWrongParamTypeFromConfig,
Err: ErrWrongParamTypeFromConfig,
},
{
Name: "1 input ptr valid given",
@ -261,13 +261,13 @@ func TestOutputCheck(t *testing.T) {
{
Output: map[string]reflect.Type{},
Fn: func(context.Context) {},
Err: errMissingHandlerOutputArgument,
Err: ErrMissingHandlerOutputArgument,
},
// no input -> with last type not api.Err
{
Output: map[string]reflect.Type{},
Fn: func(context.Context) bool { return true },
Err: errMissingHandlerErrorArgument,
Err: ErrMissingHandlerErrorArgument,
},
// no input -> with api.Err
{
@ -279,13 +279,13 @@ func TestOutputCheck(t *testing.T) {
{
Output: map[string]reflect.Type{},
Fn: func(context.Context) api.Err { return api.ErrSuccess },
Err: errMissingHandlerContextArgument,
Err: ErrMissingHandlerContextArgument,
},
// no input -> invlaid context.Context type
{
Output: map[string]reflect.Type{},
Fn: func(context.Context, int) api.Err { return api.ErrSuccess },
Err: errMissingHandlerContextArgument,
Err: ErrMissingHandlerContextArgument,
},
// func can have output if not specified
{
@ -299,7 +299,7 @@ func TestOutputCheck(t *testing.T) {
"Test1": reflect.TypeOf(int(0)),
},
Fn: func() api.Err { return api.ErrSuccess },
Err: errWrongOutputArgumentType,
Err: ErrWrongOutputArgumentType,
},
// output not a pointer
{
@ -307,7 +307,7 @@ func TestOutputCheck(t *testing.T) {
"Test1": reflect.TypeOf(int(0)),
},
Fn: func() (int, api.Err) { return 0, api.ErrSuccess },
Err: errWrongOutputArgumentType,
Err: ErrWrongOutputArgumentType,
},
// output not a pointer to struct
{
@ -315,7 +315,7 @@ func TestOutputCheck(t *testing.T) {
"Test1": reflect.TypeOf(int(0)),
},
Fn: func() (*int, api.Err) { return nil, api.ErrSuccess },
Err: errWrongOutputArgumentType,
Err: ErrWrongOutputArgumentType,
},
// unexported param name
{
@ -323,7 +323,7 @@ func TestOutputCheck(t *testing.T) {
"test1": reflect.TypeOf(int(0)),
},
Fn: func() (*struct{}, api.Err) { return nil, api.ErrSuccess },
Err: errUnexportedName,
Err: ErrUnexportedName,
},
// output field missing
{
@ -331,7 +331,7 @@ func TestOutputCheck(t *testing.T) {
"Test1": reflect.TypeOf(int(0)),
},
Fn: func() (*struct{}, api.Err) { return nil, api.ErrSuccess },
Err: errMissingConfigArgument,
Err: ErrMissingConfigArgument,
},
// output field invalid type
{
@ -339,7 +339,7 @@ func TestOutputCheck(t *testing.T) {
"Test1": reflect.TypeOf(int(0)),
},
Fn: func() (*struct{ Test1 string }, api.Err) { return nil, api.ErrSuccess },
Err: errWrongParamTypeFromConfig,
Err: ErrWrongParamTypeFromConfig,
},
// output field valid type
{