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 package dynfunc
// cerr allows you to create constant "const" error with type boxing. // Err allows you to create constant "const" error with type boxing.
type cerr string type Err string
func (err cerr) Error() string { func (err Err) Error() string {
return string(err) return string(err)
} }
// errHandlerNotFunc - handler is not a func const (
const errHandlerNotFunc = cerr("handler must be a func") // ErrHandlerNotFunc - handler is not a func
ErrHandlerNotFunc = Err("handler must be a func")
// errNoServiceForHandler - no service matching this handler // ErrNoServiceForHandler - no service matching this handler
const errNoServiceForHandler = cerr("no service found for this handler") ErrNoServiceForHandler = Err("no service found for this handler")
// errMissingHandlerArgumentParam - missing params arguments for handler // errMissingHandlerArgumentParam - missing params arguments for handler
const errMissingHandlerContextArgument = cerr("missing handler first argument of type context.Context") ErrMissingHandlerContextArgument = Err("missing handler first argument of type context.Context")
// errMissingHandlerInputArgument - missing params arguments for handler // ErrMissingHandlerInputArgument - missing params arguments for handler
const errMissingHandlerInputArgument = cerr("missing handler argument: input struct") ErrMissingHandlerInputArgument = Err("missing handler argument: input struct")
// errUnexpectedInput - input argument is not expected // ErrUnexpectedInput - input argument is not expected
const errUnexpectedInput = cerr("unexpected input struct") ErrUnexpectedInput = Err("unexpected input struct")
// errMissingHandlerOutputArgument - missing output for handler // ErrMissingHandlerOutputArgument - missing output for handler
const errMissingHandlerOutputArgument = cerr("missing handler first output argument: output struct") ErrMissingHandlerOutputArgument = Err("missing handler first output argument: output struct")
// errMissingHandlerOutputError - missing error output for handler // ErrMissingHandlerOutputError - missing error output for handler
const errMissingHandlerOutputError = cerr("missing handler last output argument of type api.Err") ErrMissingHandlerOutputError = Err("missing handler last output argument of type api.Err")
// errMissingRequestArgument - missing request argument for handler // ErrMissingRequestArgument - missing request argument for handler
const errMissingRequestArgument = cerr("handler first argument must be of type api.Request") ErrMissingRequestArgument = Err("handler first argument must be of type api.Request")
// errMissingParamArgument - missing parameters argument for handler // ErrMissingParamArgument - missing parameters argument for handler
const errMissingParamArgument = cerr("handler second argument must be a struct") ErrMissingParamArgument = Err("handler second argument must be a struct")
// errUnexportedName - argument is unexported in struct // ErrUnexportedName - argument is unexported in struct
const errUnexportedName = cerr("unexported name") ErrUnexportedName = Err("unexported name")
// errWrongOutputArgumentType - wrong type for output first argument // ErrWrongOutputArgumentType - wrong type for output first argument
const errWrongOutputArgumentType = cerr("handler first output argument must be a *struct") ErrWrongOutputArgumentType = Err("handler first output argument must be a *struct")
// errMissingConfigArgument - missing an input/output argument in handler struct // ErrMissingConfigArgument - missing an input/output argument in handler struct
const errMissingConfigArgument = cerr("missing an argument from the configuration") ErrMissingConfigArgument = Err("missing an argument from the configuration")
// errWrongParamTypeFromConfig - a configuration parameter type is invalid in the handler param struct // ErrWrongParamTypeFromConfig - a configuration parameter type is invalid in the handler param struct
const errWrongParamTypeFromConfig = cerr("invalid struct field type") ErrWrongParamTypeFromConfig = Err("invalid struct field type")
// errMissingHandlerErrorArgument - missing handler output error // ErrMissingHandlerErrorArgument - missing handler output error
const errMissingHandlerErrorArgument = cerr("last output must be of type api.Err") 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 { if fnType.Kind() != reflect.Func {
return nil, errHandlerNotFunc return nil, ErrHandlerNotFunc
} }
if err := h.signature.ValidateInput(fnType); err != nil { if err := h.signature.ValidateInput(fnType); err != nil {
return nil, fmt.Errorf("input: %w", err) 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 // missing or invalid first arg: context.Context
if handlerType.NumIn() < 1 { if handlerType.NumIn() < 1 {
return errMissingHandlerContextArgument return ErrMissingHandlerContextArgument
} }
firstArgType := handlerType.In(0) firstArgType := handlerType.In(0)
@ -65,35 +65,35 @@ func (s *Signature) ValidateInput(handlerType reflect.Type) error {
if len(s.Input) == 0 { if len(s.Input) == 0 {
// input struct provided // input struct provided
if handlerType.NumIn() > 1 { if handlerType.NumIn() > 1 {
return errUnexpectedInput return ErrUnexpectedInput
} }
return nil return nil
} }
// too much arguments // too much arguments
if handlerType.NumIn() > 2 { if handlerType.NumIn() > 2 {
return errMissingHandlerInputArgument return ErrMissingHandlerInputArgument
} }
// arg must be a struct // arg must be a struct
inStruct := handlerType.In(1) inStruct := handlerType.In(1)
if inStruct.Kind() != reflect.Struct { if inStruct.Kind() != reflect.Struct {
return errMissingParamArgument return ErrMissingParamArgument
} }
// check for invalid param // check for invalid param
for name, ptype := range s.Input { for name, ptype := range s.Input {
if name[0] == strings.ToLower(name)[0] { 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) field, exists := inStruct.FieldByName(name)
if !exists { if !exists {
return fmt.Errorf("%s: %w", name, errMissingConfigArgument) return fmt.Errorf("%s: %w", name, ErrMissingConfigArgument)
} }
if !ptype.AssignableTo(field.Type) { 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) errType := reflect.TypeOf(api.ErrUnknown)
if handlerType.NumOut() < 1 { if handlerType.NumOut() < 1 {
return errMissingHandlerErrorArgument return ErrMissingHandlerErrorArgument
} }
// last output must be api.Err // last output must be api.Err
lastArgType := handlerType.Out(handlerType.NumOut() - 1) lastArgType := handlerType.Out(handlerType.NumOut() - 1)
if !lastArgType.AssignableTo(errType) { if !lastArgType.AssignableTo(errType) {
return errMissingHandlerErrorArgument return ErrMissingHandlerErrorArgument
} }
// no output -> ok // no output -> ok
@ -120,29 +120,29 @@ func (s Signature) ValidateOutput(handlerType reflect.Type) error {
} }
if handlerType.NumOut() < 2 { if handlerType.NumOut() < 2 {
return errMissingHandlerOutputArgument return ErrMissingHandlerOutputArgument
} }
// fail if first output is not a pointer to struct // fail if first output is not a pointer to struct
outStructPtr := handlerType.Out(0) outStructPtr := handlerType.Out(0)
if outStructPtr.Kind() != reflect.Ptr { if outStructPtr.Kind() != reflect.Ptr {
return errWrongOutputArgumentType return ErrWrongOutputArgumentType
} }
outStruct := outStructPtr.Elem() outStruct := outStructPtr.Elem()
if outStruct.Kind() != reflect.Struct { if outStruct.Kind() != reflect.Struct {
return errWrongOutputArgumentType return ErrWrongOutputArgumentType
} }
// fail on invalid output // fail on invalid output
for name, ptype := range s.Output { for name, ptype := range s.Output {
if name[0] == strings.ToLower(name)[0] { 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) field, exists := outStruct.FieldByName(name)
if !exists { if !exists {
return fmt.Errorf("%s: %w", name, errMissingConfigArgument) return fmt.Errorf("%s: %w", name, ErrMissingConfigArgument)
} }
// ignore types evalutating to nil // ignore types evalutating to nil
@ -151,7 +151,7 @@ func (s Signature) ValidateOutput(handlerType reflect.Type) error {
} }
if !field.Type.ConvertibleTo(ptype) { 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{}, Input: map[string]reflect.Type{},
Fn: func(context.Context, int) {}, Fn: func(context.Context, int) {},
FnCtx: func(context.Context, int) {}, FnCtx: func(context.Context, int) {},
Err: errUnexpectedInput, Err: ErrUnexpectedInput,
}, },
{ {
Name: "no input 2 given", Name: "no input 2 given",
Input: map[string]reflect.Type{}, Input: map[string]reflect.Type{},
Fn: func(context.Context, int, string) {}, Fn: func(context.Context, int, string) {},
FnCtx: func(context.Context, int, string) {}, FnCtx: func(context.Context, int, string) {},
Err: errUnexpectedInput, Err: ErrUnexpectedInput,
}, },
{ {
Name: "1 input 0 given", Name: "1 input 0 given",
@ -46,7 +46,7 @@ func TestInputCheck(t *testing.T) {
}, },
Fn: func(context.Context) {}, Fn: func(context.Context) {},
FnCtx: func(context.Context) {}, FnCtx: func(context.Context) {},
Err: errMissingHandlerInputArgument, Err: ErrMissingHandlerInputArgument,
}, },
{ {
Name: "1 input non-struct given", Name: "1 input non-struct given",
@ -55,7 +55,7 @@ func TestInputCheck(t *testing.T) {
}, },
Fn: func(context.Context, int) {}, Fn: func(context.Context, int) {},
FnCtx: func(context.Context, int) {}, FnCtx: func(context.Context, int) {},
Err: errMissingParamArgument, Err: ErrMissingParamArgument,
}, },
{ {
Name: "unexported input", Name: "unexported input",
@ -64,7 +64,7 @@ func TestInputCheck(t *testing.T) {
}, },
Fn: func(context.Context, struct{}) {}, Fn: func(context.Context, struct{}) {},
FnCtx: func(context.Context, struct{}) {}, FnCtx: func(context.Context, struct{}) {},
Err: errUnexportedName, Err: ErrUnexportedName,
}, },
{ {
Name: "1 input empty struct given", Name: "1 input empty struct given",
@ -73,7 +73,7 @@ func TestInputCheck(t *testing.T) {
}, },
Fn: func(context.Context, struct{}) {}, Fn: func(context.Context, struct{}) {},
FnCtx: func(context.Context, struct{}) {}, FnCtx: func(context.Context, struct{}) {},
Err: errMissingConfigArgument, Err: ErrMissingConfigArgument,
}, },
{ {
Name: "1 input invalid given", Name: "1 input invalid given",
@ -82,7 +82,7 @@ func TestInputCheck(t *testing.T) {
}, },
Fn: func(context.Context, struct{ Test1 string }) {}, Fn: func(context.Context, struct{ Test1 string }) {},
FnCtx: func(context.Context, struct{ Test1 string }) {}, FnCtx: func(context.Context, struct{ Test1 string }) {},
Err: errWrongParamTypeFromConfig, Err: ErrWrongParamTypeFromConfig,
}, },
{ {
Name: "1 input valid given", Name: "1 input valid given",
@ -100,7 +100,7 @@ func TestInputCheck(t *testing.T) {
}, },
Fn: func(context.Context, struct{}) {}, Fn: func(context.Context, struct{}) {},
FnCtx: func(context.Context, struct{}) {}, FnCtx: func(context.Context, struct{}) {},
Err: errMissingConfigArgument, Err: ErrMissingConfigArgument,
}, },
{ {
Name: "1 input ptr invalid given", Name: "1 input ptr invalid given",
@ -109,7 +109,7 @@ func TestInputCheck(t *testing.T) {
}, },
Fn: func(context.Context, struct{ Test1 string }) {}, Fn: func(context.Context, struct{ Test1 string }) {},
FnCtx: 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", Name: "1 input ptr invalid ptr type given",
@ -118,7 +118,7 @@ func TestInputCheck(t *testing.T) {
}, },
Fn: func(context.Context, struct{ Test1 *string }) {}, Fn: func(context.Context, struct{ Test1 *string }) {},
FnCtx: func(context.Context, struct{ Test1 *string }) {}, FnCtx: func(context.Context, struct{ Test1 *string }) {},
Err: errWrongParamTypeFromConfig, Err: ErrWrongParamTypeFromConfig,
}, },
{ {
Name: "1 input ptr valid given", Name: "1 input ptr valid given",
@ -261,13 +261,13 @@ func TestOutputCheck(t *testing.T) {
{ {
Output: map[string]reflect.Type{}, Output: map[string]reflect.Type{},
Fn: func(context.Context) {}, Fn: func(context.Context) {},
Err: errMissingHandlerOutputArgument, Err: ErrMissingHandlerOutputArgument,
}, },
// no input -> with last type not api.Err // no input -> with last type not api.Err
{ {
Output: map[string]reflect.Type{}, Output: map[string]reflect.Type{},
Fn: func(context.Context) bool { return true }, Fn: func(context.Context) bool { return true },
Err: errMissingHandlerErrorArgument, Err: ErrMissingHandlerErrorArgument,
}, },
// no input -> with api.Err // no input -> with api.Err
{ {
@ -279,13 +279,13 @@ func TestOutputCheck(t *testing.T) {
{ {
Output: map[string]reflect.Type{}, Output: map[string]reflect.Type{},
Fn: func(context.Context) api.Err { return api.ErrSuccess }, Fn: func(context.Context) api.Err { return api.ErrSuccess },
Err: errMissingHandlerContextArgument, Err: ErrMissingHandlerContextArgument,
}, },
// no input -> invlaid context.Context type // no input -> invlaid context.Context type
{ {
Output: map[string]reflect.Type{}, Output: map[string]reflect.Type{},
Fn: func(context.Context, int) api.Err { return api.ErrSuccess }, Fn: func(context.Context, int) api.Err { return api.ErrSuccess },
Err: errMissingHandlerContextArgument, Err: ErrMissingHandlerContextArgument,
}, },
// func can have output if not specified // func can have output if not specified
{ {
@ -299,7 +299,7 @@ func TestOutputCheck(t *testing.T) {
"Test1": reflect.TypeOf(int(0)), "Test1": reflect.TypeOf(int(0)),
}, },
Fn: func() api.Err { return api.ErrSuccess }, Fn: func() api.Err { return api.ErrSuccess },
Err: errWrongOutputArgumentType, Err: ErrWrongOutputArgumentType,
}, },
// output not a pointer // output not a pointer
{ {
@ -307,7 +307,7 @@ func TestOutputCheck(t *testing.T) {
"Test1": reflect.TypeOf(int(0)), "Test1": reflect.TypeOf(int(0)),
}, },
Fn: func() (int, api.Err) { return 0, api.ErrSuccess }, Fn: func() (int, api.Err) { return 0, api.ErrSuccess },
Err: errWrongOutputArgumentType, Err: ErrWrongOutputArgumentType,
}, },
// output not a pointer to struct // output not a pointer to struct
{ {
@ -315,7 +315,7 @@ func TestOutputCheck(t *testing.T) {
"Test1": reflect.TypeOf(int(0)), "Test1": reflect.TypeOf(int(0)),
}, },
Fn: func() (*int, api.Err) { return nil, api.ErrSuccess }, Fn: func() (*int, api.Err) { return nil, api.ErrSuccess },
Err: errWrongOutputArgumentType, Err: ErrWrongOutputArgumentType,
}, },
// unexported param name // unexported param name
{ {
@ -323,7 +323,7 @@ func TestOutputCheck(t *testing.T) {
"test1": reflect.TypeOf(int(0)), "test1": reflect.TypeOf(int(0)),
}, },
Fn: func() (*struct{}, api.Err) { return nil, api.ErrSuccess }, Fn: func() (*struct{}, api.Err) { return nil, api.ErrSuccess },
Err: errUnexportedName, Err: ErrUnexportedName,
}, },
// output field missing // output field missing
{ {
@ -331,7 +331,7 @@ func TestOutputCheck(t *testing.T) {
"Test1": reflect.TypeOf(int(0)), "Test1": reflect.TypeOf(int(0)),
}, },
Fn: func() (*struct{}, api.Err) { return nil, api.ErrSuccess }, Fn: func() (*struct{}, api.Err) { return nil, api.ErrSuccess },
Err: errMissingConfigArgument, Err: ErrMissingConfigArgument,
}, },
// output field invalid type // output field invalid type
{ {
@ -339,7 +339,7 @@ func TestOutputCheck(t *testing.T) {
"Test1": reflect.TypeOf(int(0)), "Test1": reflect.TypeOf(int(0)),
}, },
Fn: func() (*struct{ Test1 string }, api.Err) { return nil, api.ErrSuccess }, Fn: func() (*struct{ Test1 string }, api.Err) { return nil, api.ErrSuccess },
Err: errWrongParamTypeFromConfig, Err: ErrWrongParamTypeFromConfig,
}, },
// output field valid type // output field valid type
{ {