From 5d51e9fa2cb3d4e407057c8e36390e11ccb3ba1f Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 15 Jul 2022 10:49:04 +0800 Subject: [PATCH] improve package gerror, add HasCode/HasError function for package gerror (#2006) --- contrib/drivers/clickhouse/clickhouse.go | 15 +- contrib/drivers/pgsql/pgsql.go | 1 + contrib/drivers/sqlite/sqlite.go | 3 +- errors/gerror/gerror.go | 323 ++---------------- errors/gerror/gerror_api.go | 110 ++++++ errors/gerror/gerror_api_code.go | 139 ++++++++ ...{gerror_option.go => gerror_api_option.go} | 0 errors/gerror/gerror_api_stack.go | 118 +++++++ errors/gerror/gerror_error.go | 21 +- errors/gerror/gerror_error_code.go | 2 +- errors/gerror/gerror_interface.go | 57 ---- errors/gerror/gerror_stack.go | 30 -- errors/gerror/gerror_z_unit_test.go | 40 ++- os/gtimer/gtimer_z_unit_test.go | 6 +- util/gvalid/gvalid_validator_check_map.go | 3 +- util/gvalid/gvalid_validator_check_value.go | 10 +- .../gvalid_z_unit_feature_recursive_test.go | 58 ++++ 17 files changed, 512 insertions(+), 424 deletions(-) create mode 100644 errors/gerror/gerror_api.go create mode 100644 errors/gerror/gerror_api_code.go rename errors/gerror/{gerror_option.go => gerror_api_option.go} (100%) create mode 100644 errors/gerror/gerror_api_stack.go delete mode 100644 errors/gerror/gerror_interface.go delete mode 100644 errors/gerror/gerror_stack.go diff --git a/contrib/drivers/clickhouse/clickhouse.go b/contrib/drivers/clickhouse/clickhouse.go index a5cc60794..09e98e284 100644 --- a/contrib/drivers/clickhouse/clickhouse.go +++ b/contrib/drivers/clickhouse/clickhouse.go @@ -13,6 +13,10 @@ import ( "database/sql/driver" "errors" "fmt" + "net/url" + "strings" + "time" + "github.com/ClickHouse/clickhouse-go/v2" "github.com/gogf/gf/v2/container/gmap" "github.com/gogf/gf/v2/database/gdb" @@ -24,9 +28,6 @@ import ( "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" "github.com/google/uuid" - "net/url" - "strings" - "time" ) // Driver is the driver for postgresql database. @@ -148,7 +149,13 @@ func (d *Driver) TableFields( if link, err = d.SlaveLink(useSchema); err != nil { return nil } - getColumnsSql := fmt.Sprintf("select name,position,default_expression,comment,type,is_in_partition_key,is_in_sorting_key,is_in_primary_key,is_in_sampling_key from `system`.columns c where database = '%s' and `table` = '%s'", d.GetConfig().Name, table) + var ( + columns = "name,position,default_expression,comment,type,is_in_partition_key,is_in_sorting_key,is_in_primary_key,is_in_sampling_key" + getColumnsSql = fmt.Sprintf( + "select %s from `system`.columns c where database = '%s' and `table` = '%s'", + columns, d.GetConfig().Name, table, + ) + ) result, err = d.DoSelect(ctx, link, getColumnsSql) if err != nil { return nil diff --git a/contrib/drivers/pgsql/pgsql.go b/contrib/drivers/pgsql/pgsql.go index be9f338cc..436527184 100644 --- a/contrib/drivers/pgsql/pgsql.go +++ b/contrib/drivers/pgsql/pgsql.go @@ -218,6 +218,7 @@ ORDER BY c.relname`, querySchema, ) + query, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(query)) result, err = d.DoSelect(ctx, link, query) if err != nil { return diff --git a/contrib/drivers/sqlite/sqlite.go b/contrib/drivers/sqlite/sqlite.go index 85adfc467..46150ec7c 100644 --- a/contrib/drivers/sqlite/sqlite.go +++ b/contrib/drivers/sqlite/sqlite.go @@ -36,8 +36,7 @@ type Driver struct { var ( // tableFieldsMap caches the table information retrieved from database. tableFieldsMap = gmap.New(true) - // Error - ErrorSave = gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by sqlite driver`) + ErrorSave = gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by sqlite driver`) ) func init() { diff --git a/errors/gerror/gerror.go b/errors/gerror/gerror.go index ee3e6c746..2995f372b 100644 --- a/errors/gerror/gerror.go +++ b/errors/gerror/gerror.go @@ -4,316 +4,55 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package gerror provides simple functions to manipulate errors. +// Package gerror provides rich functionalities to manipulate errors. // -// Very note that, this package is quite a basic package, which SHOULD NOT import extra packages +// For maintainers, please very note that, +// this package is quite a basic package, which SHOULD NOT import extra packages // except standard packages and internal packages, to avoid cycle imports. package gerror import ( - "fmt" - "strings" - "github.com/gogf/gf/v2/errors/gcode" ) -// New creates and returns an error which is formatted from given text. -func New(text string) error { - return &Error{ - stack: callers(), - text: text, - code: gcode.CodeNil, - } +// IIs is the interface for Is feature. +type IIs interface { + Error() string + Is(target error) bool } -// Newf returns an error that formats as the given format and args. -func Newf(format string, args ...interface{}) error { - return &Error{ - stack: callers(), - text: fmt.Sprintf(format, args...), - code: gcode.CodeNil, - } +// IEqual is the interface for Equal feature. +type IEqual interface { + Error() string + Equal(target error) bool } -// NewSkip creates and returns an error which is formatted from given text. -// The parameter `skip` specifies the stack callers skipped amount. -func NewSkip(skip int, text string) error { - return &Error{ - stack: callers(skip), - text: text, - code: gcode.CodeNil, - } +// ICode is the interface for Code feature. +type ICode interface { + Error() string + Code() gcode.Code } -// NewSkipf returns an error that formats as the given format and args. -// The parameter `skip` specifies the stack callers skipped amount. -func NewSkipf(skip int, format string, args ...interface{}) error { - return &Error{ - stack: callers(skip), - text: fmt.Sprintf(format, args...), - code: gcode.CodeNil, - } +// IStack is the interface for Stack feature. +type IStack interface { + Error() string + Stack() string } -// Wrap wraps error with text. It returns nil if given err is nil. -// Note that it does not lose the error code of wrapped error, as it inherits the error code from it. -func Wrap(err error, text string) error { - if err == nil { - return nil - } - return &Error{ - error: err, - stack: callers(), - text: text, - code: Code(err), - } +// ICause is the interface for Cause feature. +type ICause interface { + Error() string + Cause() error } -// Wrapf returns an error annotating err with a stack trace at the point Wrapf is called, and the format specifier. -// It returns nil if given `err` is nil. -// Note that it does not lose the error code of wrapped error, as it inherits the error code from it. -func Wrapf(err error, format string, args ...interface{}) error { - if err == nil { - return nil - } - return &Error{ - error: err, - stack: callers(), - text: fmt.Sprintf(format, args...), - code: Code(err), - } +// ICurrent is the interface for Current feature. +type ICurrent interface { + Error() string + Current() error } -// WrapSkip wraps error with text. It returns nil if given err is nil. -// The parameter `skip` specifies the stack callers skipped amount. -// Note that it does not lose the error code of wrapped error, as it inherits the error code from it. -func WrapSkip(skip int, err error, text string) error { - if err == nil { - return nil - } - return &Error{ - error: err, - stack: callers(skip), - text: text, - code: Code(err), - } -} - -// WrapSkipf wraps error with text that is formatted with given format and args. It returns nil if given err is nil. -// The parameter `skip` specifies the stack callers skipped amount. -// Note that it does not lose the error code of wrapped error, as it inherits the error code from it. -func WrapSkipf(skip int, err error, format string, args ...interface{}) error { - if err == nil { - return nil - } - return &Error{ - error: err, - stack: callers(skip), - text: fmt.Sprintf(format, args...), - code: Code(err), - } -} - -// NewCode creates and returns an error that has error code and given text. -func NewCode(code gcode.Code, text ...string) error { - return &Error{ - stack: callers(), - text: strings.Join(text, ", "), - code: code, - } -} - -// NewCodef returns an error that has error code and formats as the given format and args. -func NewCodef(code gcode.Code, format string, args ...interface{}) error { - return &Error{ - stack: callers(), - text: fmt.Sprintf(format, args...), - code: code, - } -} - -// NewCodeSkip creates and returns an error which has error code and is formatted from given text. -// The parameter `skip` specifies the stack callers skipped amount. -func NewCodeSkip(code gcode.Code, skip int, text ...string) error { - return &Error{ - stack: callers(skip), - text: strings.Join(text, ", "), - code: code, - } -} - -// NewCodeSkipf returns an error that has error code and formats as the given format and args. -// The parameter `skip` specifies the stack callers skipped amount. -func NewCodeSkipf(code gcode.Code, skip int, format string, args ...interface{}) error { - return &Error{ - stack: callers(skip), - text: fmt.Sprintf(format, args...), - code: code, - } -} - -// WrapCode wraps error with code and text. -// It returns nil if given err is nil. -func WrapCode(code gcode.Code, err error, text ...string) error { - if err == nil { - return nil - } - return &Error{ - error: err, - stack: callers(), - text: strings.Join(text, ", "), - code: code, - } -} - -// WrapCodef wraps error with code and format specifier. -// It returns nil if given `err` is nil. -func WrapCodef(code gcode.Code, err error, format string, args ...interface{}) error { - if err == nil { - return nil - } - return &Error{ - error: err, - stack: callers(), - text: fmt.Sprintf(format, args...), - code: code, - } -} - -// WrapCodeSkip wraps error with code and text. -// It returns nil if given err is nil. -// The parameter `skip` specifies the stack callers skipped amount. -func WrapCodeSkip(code gcode.Code, skip int, err error, text ...string) error { - if err == nil { - return nil - } - return &Error{ - error: err, - stack: callers(skip), - text: strings.Join(text, ", "), - code: code, - } -} - -// WrapCodeSkipf wraps error with code and text that is formatted with given format and args. -// It returns nil if given err is nil. -// The parameter `skip` specifies the stack callers skipped amount. -func WrapCodeSkipf(code gcode.Code, skip int, err error, format string, args ...interface{}) error { - if err == nil { - return nil - } - return &Error{ - error: err, - stack: callers(skip), - text: fmt.Sprintf(format, args...), - code: code, - } -} - -// Code returns the error code of current error. -// It returns CodeNil if it has no error code neither it does not implement interface Code. -func Code(err error) gcode.Code { - if err == nil { - return gcode.CodeNil - } - if e, ok := err.(iCode); ok { - return e.Code() - } - if e, ok := err.(iNext); ok { - return Code(e.Next()) - } - if e, ok := err.(iUnwrap); ok { - return Code(e.Unwrap()) - } - return gcode.CodeNil -} - -// Cause returns the root cause error of `err`. -func Cause(err error) error { - if err == nil { - return nil - } - if e, ok := err.(iCause); ok { - return e.Cause() - } - if e, ok := err.(iNext); ok { - return Cause(e.Next()) - } - if e, ok := err.(iUnwrap); ok { - return Cause(e.Unwrap()) - } - return err -} - -// Stack returns the stack callers as string. -// It returns the error string directly if the `err` does not support stacks. -func Stack(err error) string { - if err == nil { - return "" - } - if e, ok := err.(iStack); ok { - return e.Stack() - } - return err.Error() -} - -// Current creates and returns the current level error. -// It returns nil if current level error is nil. -func Current(err error) error { - if err == nil { - return nil - } - if e, ok := err.(iCurrent); ok { - return e.Current() - } - return err -} - -// Next returns the next level error. -// It returns nil if current level error or the next level error is nil. -func Next(err error) error { - if err == nil { - return nil - } - if e, ok := err.(iNext); ok { - return e.Next() - } - return nil -} - -// Unwrap is alias of function `Next`. -// It is just for implements for stdlib errors.Unwrap from Go version 1.17. -func Unwrap(err error) error { - return Next(err) -} - -// HasStack checks and returns whether `err` implemented interface `iStack`. -func HasStack(err error) bool { - _, ok := err.(iStack) - return ok -} - -// Equal reports whether current error `err` equals to error `target`. -// Please note that, in default comparison for `Error`, -// the errors are considered the same if both the `code` and `text` of them are the same. -func Equal(err, target error) bool { - if err == target { - return true - } - if e, ok := err.(iEqual); ok { - return e.Equal(target) - } - if e, ok := target.(iEqual); ok { - return e.Equal(err) - } - return false -} - -// Is reports whether current error `err` has error `target` in its chaining errors. -// It is just for implements for stdlib errors.Unwrap from Go version 1.17. -func Is(err, target error) bool { - if e, ok := err.(iIs); ok { - return e.Is(target) - } - return false +// IUnwrap is the interface for Unwrap feature. +type IUnwrap interface { + Error() string + Unwrap() error } diff --git a/errors/gerror/gerror_api.go b/errors/gerror/gerror_api.go new file mode 100644 index 000000000..9f6a8c9e3 --- /dev/null +++ b/errors/gerror/gerror_api.go @@ -0,0 +1,110 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gerror + +import ( + "fmt" + + "github.com/gogf/gf/v2/errors/gcode" +) + +// New creates and returns an error which is formatted from given text. +func New(text string) error { + return &Error{ + stack: callers(), + text: text, + code: gcode.CodeNil, + } +} + +// Newf returns an error that formats as the given format and args. +func Newf(format string, args ...interface{}) error { + return &Error{ + stack: callers(), + text: fmt.Sprintf(format, args...), + code: gcode.CodeNil, + } +} + +// NewSkip creates and returns an error which is formatted from given text. +// The parameter `skip` specifies the stack callers skipped amount. +func NewSkip(skip int, text string) error { + return &Error{ + stack: callers(skip), + text: text, + code: gcode.CodeNil, + } +} + +// NewSkipf returns an error that formats as the given format and args. +// The parameter `skip` specifies the stack callers skipped amount. +func NewSkipf(skip int, format string, args ...interface{}) error { + return &Error{ + stack: callers(skip), + text: fmt.Sprintf(format, args...), + code: gcode.CodeNil, + } +} + +// Wrap wraps error with text. It returns nil if given err is nil. +// Note that it does not lose the error code of wrapped error, as it inherits the error code from it. +func Wrap(err error, text string) error { + if err == nil { + return nil + } + return &Error{ + error: err, + stack: callers(), + text: text, + code: Code(err), + } +} + +// Wrapf returns an error annotating err with a stack trace at the point Wrapf is called, and the format specifier. +// It returns nil if given `err` is nil. +// Note that it does not lose the error code of wrapped error, as it inherits the error code from it. +func Wrapf(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + return &Error{ + error: err, + stack: callers(), + text: fmt.Sprintf(format, args...), + code: Code(err), + } +} + +// WrapSkip wraps error with text. It returns nil if given err is nil. +// The parameter `skip` specifies the stack callers skipped amount. +// Note that it does not lose the error code of wrapped error, as it inherits the error code from it. +func WrapSkip(skip int, err error, text string) error { + if err == nil { + return nil + } + return &Error{ + error: err, + stack: callers(skip), + text: text, + code: Code(err), + } +} + +// WrapSkipf wraps error with text that is formatted with given format and args. It returns nil if given err is nil. +// The parameter `skip` specifies the stack callers skipped amount. +// Note that it does not lose the error code of wrapped error, as it inherits the error code from it. +func WrapSkipf(skip int, err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + return &Error{ + error: err, + stack: callers(skip), + text: fmt.Sprintf(format, args...), + code: Code(err), + } +} diff --git a/errors/gerror/gerror_api_code.go b/errors/gerror/gerror_api_code.go new file mode 100644 index 000000000..f4389eec8 --- /dev/null +++ b/errors/gerror/gerror_api_code.go @@ -0,0 +1,139 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gerror + +import ( + "fmt" + "strings" + + "github.com/gogf/gf/v2/errors/gcode" +) + +// NewCode creates and returns an error that has error code and given text. +func NewCode(code gcode.Code, text ...string) error { + return &Error{ + stack: callers(), + text: strings.Join(text, ", "), + code: code, + } +} + +// NewCodef returns an error that has error code and formats as the given format and args. +func NewCodef(code gcode.Code, format string, args ...interface{}) error { + return &Error{ + stack: callers(), + text: fmt.Sprintf(format, args...), + code: code, + } +} + +// NewCodeSkip creates and returns an error which has error code and is formatted from given text. +// The parameter `skip` specifies the stack callers skipped amount. +func NewCodeSkip(code gcode.Code, skip int, text ...string) error { + return &Error{ + stack: callers(skip), + text: strings.Join(text, ", "), + code: code, + } +} + +// NewCodeSkipf returns an error that has error code and formats as the given format and args. +// The parameter `skip` specifies the stack callers skipped amount. +func NewCodeSkipf(code gcode.Code, skip int, format string, args ...interface{}) error { + return &Error{ + stack: callers(skip), + text: fmt.Sprintf(format, args...), + code: code, + } +} + +// WrapCode wraps error with code and text. +// It returns nil if given err is nil. +func WrapCode(code gcode.Code, err error, text ...string) error { + if err == nil { + return nil + } + return &Error{ + error: err, + stack: callers(), + text: strings.Join(text, ", "), + code: code, + } +} + +// WrapCodef wraps error with code and format specifier. +// It returns nil if given `err` is nil. +func WrapCodef(code gcode.Code, err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + return &Error{ + error: err, + stack: callers(), + text: fmt.Sprintf(format, args...), + code: code, + } +} + +// WrapCodeSkip wraps error with code and text. +// It returns nil if given err is nil. +// The parameter `skip` specifies the stack callers skipped amount. +func WrapCodeSkip(code gcode.Code, skip int, err error, text ...string) error { + if err == nil { + return nil + } + return &Error{ + error: err, + stack: callers(skip), + text: strings.Join(text, ", "), + code: code, + } +} + +// WrapCodeSkipf wraps error with code and text that is formatted with given format and args. +// It returns nil if given err is nil. +// The parameter `skip` specifies the stack callers skipped amount. +func WrapCodeSkipf(code gcode.Code, skip int, err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + return &Error{ + error: err, + stack: callers(skip), + text: fmt.Sprintf(format, args...), + code: code, + } +} + +// Code returns the error code of current error. +// It returns `CodeNil` if it has no error code neither it does not implement interface Code. +func Code(err error) gcode.Code { + if err == nil { + return gcode.CodeNil + } + if e, ok := err.(ICode); ok { + return e.Code() + } + if e, ok := err.(IUnwrap); ok { + return Code(e.Unwrap()) + } + return gcode.CodeNil +} + +// HasCode checks and reports whether `err` has `code` in its chaining errors. +func HasCode(err error, code gcode.Code) bool { + if err == nil { + return false + } + if e, ok := err.(ICode); ok { + return code == e.Code() + } + if e, ok := err.(IUnwrap); ok { + return HasCode(e.Unwrap(), code) + } + return false +} diff --git a/errors/gerror/gerror_option.go b/errors/gerror/gerror_api_option.go similarity index 100% rename from errors/gerror/gerror_option.go rename to errors/gerror/gerror_api_option.go diff --git a/errors/gerror/gerror_api_stack.go b/errors/gerror/gerror_api_stack.go new file mode 100644 index 000000000..72b28230f --- /dev/null +++ b/errors/gerror/gerror_api_stack.go @@ -0,0 +1,118 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gerror + +import ( + "runtime" +) + +// stack represents a stack of program counters. +type stack []uintptr + +const ( + // maxStackDepth marks the max stack depth for error back traces. + maxStackDepth = 32 +) + +// Cause returns the root cause error of `err`. +func Cause(err error) error { + if err == nil { + return nil + } + if e, ok := err.(ICause); ok { + return e.Cause() + } + if e, ok := err.(IUnwrap); ok { + return Cause(e.Unwrap()) + } + return err +} + +// Stack returns the stack callers as string. +// It returns the error string directly if the `err` does not support stacks. +func Stack(err error) string { + if err == nil { + return "" + } + if e, ok := err.(IStack); ok { + return e.Stack() + } + return err.Error() +} + +// Current creates and returns the current level error. +// It returns nil if current level error is nil. +func Current(err error) error { + if err == nil { + return nil + } + if e, ok := err.(ICurrent); ok { + return e.Current() + } + return err +} + +// Unwrap returns the next level error. +// It returns nil if current level error or the next level error is nil. +func Unwrap(err error) error { + if err == nil { + return nil + } + if e, ok := err.(IUnwrap); ok { + return e.Unwrap() + } + return nil +} + +// HasStack checks and reports whether `err` implemented interface `gerror.IStack`. +func HasStack(err error) bool { + _, ok := err.(IStack) + return ok +} + +// Equal reports whether current error `err` equals to error `target`. +// Please note that, in default comparison logic for `Error`, +// the errors are considered the same if both the `code` and `text` of them are the same. +func Equal(err, target error) bool { + if err == target { + return true + } + if e, ok := err.(IEqual); ok { + return e.Equal(target) + } + if e, ok := target.(IEqual); ok { + return e.Equal(err) + } + return false +} + +// Is reports whether current error `err` has error `target` in its chaining errors. +// It is just for implements for stdlib errors.Is from Go version 1.17. +func Is(err, target error) bool { + if e, ok := err.(IIs); ok { + return e.Is(target) + } + return false +} + +// HasError is alias of Is, which more easily understanding semantics. +func HasError(err, target error) bool { + return Is(err, target) +} + +// callers returns the stack callers. +// Note that it here just retrieves the caller memory address array not the caller information. +func callers(skip ...int) stack { + var ( + pcs [maxStackDepth]uintptr + n = 3 + ) + if len(skip) > 0 { + n += skip[0] + } + return pcs[:runtime.Callers(n, pcs[:])] +} diff --git a/errors/gerror/gerror_error.go b/errors/gerror/gerror_error.go index 5a860d118..8a41bf79f 100644 --- a/errors/gerror/gerror_error.go +++ b/errors/gerror/gerror_error.go @@ -68,7 +68,7 @@ func (err *Error) Cause() error { if e, ok := loop.error.(*Error); ok { // Internal Error struct. loop = e - } else if e, ok := loop.error.(iCause); ok { + } else if e, ok := loop.error.(ICause); ok { // Other Error that implements ApiCause interface. return e.Cause() } else { @@ -76,6 +76,7 @@ func (err *Error) Cause() error { } } else { // return loop + // // To be compatible with Case of https://github.com/pkg/errors. return errors.New(loop.text) } @@ -97,21 +98,15 @@ func (err *Error) Current() error { } } -// Next returns the next level error. -// It returns nil if current level error or the next level error is nil. -func (err *Error) Next() error { +// Unwrap is alias of function `Next`. +// It is just for implements for stdlib errors.Unwrap from Go version 1.17. +func (err *Error) Unwrap() error { if err == nil { return nil } return err.error } -// Unwrap is alias of function `Next`. -// It is just for implements for stdlib errors.Unwrap from Go version 1.17. -func (err *Error) Unwrap() error { - return err.Next() -} - // Equal reports whether current error `err` equals to error `target`. // Please note that, in default comparison for `Error`, // the errors are considered the same if both the `code` and `text` of them are the same. @@ -132,19 +127,19 @@ func (err *Error) Equal(target error) bool { } // Is reports whether current error `err` has error `target` in its chaining errors. -// It is just for implements for stdlib errors.Unwrap from Go version 1.17. +// It is just for implements for stdlib errors.Is from Go version 1.17. func (err *Error) Is(target error) bool { if Equal(err, target) { return true } - nextErr := err.Next() + nextErr := err.Unwrap() if nextErr == nil { return false } if Equal(nextErr, target) { return true } - if e, ok := nextErr.(iIs); ok { + if e, ok := nextErr.(IIs); ok { return e.Is(target) } return false diff --git a/errors/gerror/gerror_error_code.go b/errors/gerror/gerror_error_code.go index 880cc54ff..1000e9f9d 100644 --- a/errors/gerror/gerror_error_code.go +++ b/errors/gerror/gerror_error_code.go @@ -17,7 +17,7 @@ func (err *Error) Code() gcode.Code { return gcode.CodeNil } if err.code == gcode.CodeNil { - return Code(err.Next()) + return Code(err.Unwrap()) } return err.code } diff --git a/errors/gerror/gerror_interface.go b/errors/gerror/gerror_interface.go deleted file mode 100644 index f4b5a32d7..000000000 --- a/errors/gerror/gerror_interface.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gerror - -import ( - "github.com/gogf/gf/v2/errors/gcode" -) - -// iIs is the interface for Is feature. -type iIs interface { - Is(target error) bool -} - -// iEqual is the interface for Equal feature. -type iEqual interface { - Equal(target error) bool -} - -// iCode is the interface for Code feature. -type iCode interface { - Error() string - Code() gcode.Code -} - -// iStack is the interface for Stack feature. -type iStack interface { - Error() string - Stack() string -} - -// iCause is the interface for Cause feature. -type iCause interface { - Error() string - Cause() error -} - -// iCurrent is the interface for Current feature. -type iCurrent interface { - Error() string - Current() error -} - -// iNext is the interface for Next feature. -type iNext interface { - Error() string - Next() error -} - -// iUnwrap is the interface for Unwrap feature. -type iUnwrap interface { - Error() string - Unwrap() error -} diff --git a/errors/gerror/gerror_stack.go b/errors/gerror/gerror_stack.go deleted file mode 100644 index b65cce769..000000000 --- a/errors/gerror/gerror_stack.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gerror - -import "runtime" - -// stack represents a stack of program counters. -type stack []uintptr - -const ( - // maxStackDepth marks the max stack depth for error back traces. - maxStackDepth = 32 -) - -// callers returns the stack callers. -// Note that it here just retrieves the caller memory address array not the caller information. -func callers(skip ...int) stack { - var ( - pcs [maxStackDepth]uintptr - n = 3 - ) - if len(skip) > 0 { - n += skip[0] - } - return pcs[:runtime.Callers(n, pcs[:])] -} diff --git a/errors/gerror/gerror_z_unit_test.go b/errors/gerror/gerror_z_unit_test.go index c8c0ad719..3c4abcc90 100644 --- a/errors/gerror/gerror_z_unit_test.go +++ b/errors/gerror/gerror_z_unit_test.go @@ -237,24 +237,6 @@ func Test_Current(t *testing.T) { }) } -func Test_Next(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - err := errors.New("1") - err = gerror.Wrap(err, "2") - err = gerror.Wrap(err, "3") - t.Assert(err.Error(), "3: 2: 1") - - err = gerror.Next(err) - t.Assert(err.Error(), "2: 1") - - err = gerror.Next(err) - t.Assert(err.Error(), "1") - - err = gerror.Next(err) - t.AssertNil(err) - }) -} - func Test_Unwrap(t *testing.T) { gtest.C(t, func(t *gtest.T) { err := errors.New("1") @@ -381,3 +363,25 @@ func Test_Is(t *testing.T) { t.Assert(gerror.Is(err2, err1), true) }) } + +func Test_HashError(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err1 := errors.New("1") + err2 := gerror.Wrap(err1, "2") + err2 = gerror.Wrap(err2, "3") + t.Assert(gerror.HasError(err2, err1), true) + }) +} + +func Test_HashCode(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err1 := errors.New("1") + err2 := gerror.WrapCode(gcode.CodeNotAuthorized, err1, "2") + err3 := gerror.Wrap(err2, "3") + err4 := gerror.Wrap(err3, "4") + t.Assert(gerror.HasCode(err1, gcode.CodeNotAuthorized), false) + t.Assert(gerror.HasCode(err2, gcode.CodeNotAuthorized), true) + t.Assert(gerror.HasCode(err3, gcode.CodeNotAuthorized), true) + t.Assert(gerror.HasCode(err4, gcode.CodeNotAuthorized), true) + }) +} diff --git a/os/gtimer/gtimer_z_unit_test.go b/os/gtimer/gtimer_z_unit_test.go index 68ad0639a..a951e60a9 100644 --- a/os/gtimer/gtimer_z_unit_test.go +++ b/os/gtimer/gtimer_z_unit_test.go @@ -94,12 +94,12 @@ func TestDelayAdd(t *testing.T) { func TestDelayAddEntry(t *testing.T) { gtest.C(t, func(t *gtest.T) { array := garray.New(true) - gtimer.DelayAddEntry(ctx, 200*time.Millisecond, 200*time.Millisecond, func(ctx context.Context) { + gtimer.DelayAddEntry(ctx, 500*time.Millisecond, 500*time.Millisecond, func(ctx context.Context) { array.Append(1) }, false, 2, gtimer.StatusReady) - time.Sleep(300 * time.Millisecond) + time.Sleep(500 * time.Millisecond) t.Assert(array.Len(), 0) - time.Sleep(1000 * time.Millisecond) + time.Sleep(2000 * time.Millisecond) t.Assert(array.Len(), 2) }) } diff --git a/util/gvalid/gvalid_validator_check_map.go b/util/gvalid/gvalid_validator_check_map.go index 2e970b9eb..f774f38e5 100644 --- a/util/gvalid/gvalid_validator_check_map.go +++ b/util/gvalid/gvalid_validator_check_map.go @@ -93,7 +93,8 @@ func (v *Validator) doCheckMap(ctx context.Context, params interface{}) Error { ) // It checks the struct recursively if its attribute is an embedded struct. - // Ignore inputParamMap, rules and messages from parent. + // Ignore inputParamMap, assoc, rules and messages from parent. + validator.assoc = nil validator.rules = nil validator.messages = nil for _, item := range inputParamMap { diff --git a/util/gvalid/gvalid_validator_check_value.go b/util/gvalid/gvalid_validator_check_value.go index 0be8d29ed..4678582c5 100644 --- a/util/gvalid/gvalid_validator_check_value.go +++ b/util/gvalid/gvalid_validator_check_value.go @@ -557,11 +557,15 @@ func (v *Validator) doCheckValueRecursively(ctx context.Context, in doCheckValue }) case reflect.Struct: - // Ignore data, rules and messages from parent. - validator := v.Clone() + // Ignore data, assoc, rules and messages from parent. + var ( + validator = v.Clone() + toBeValidatedObject = reflect.New(in.Type).Interface() + ) + validator.assoc = nil validator.rules = nil validator.messages = nil - if err := validator.Data(reflect.New(in.Type).Interface()).Assoc(in.Value).Run(ctx); err != nil { + if err := validator.Data(toBeValidatedObject).Assoc(in.Value).Run(ctx); err != nil { // It merges the errors into single error map. for k, m := range err.(*validationError).errors { in.ErrorMaps[k] = m diff --git a/util/gvalid/gvalid_z_unit_feature_recursive_test.go b/util/gvalid/gvalid_z_unit_feature_recursive_test.go index b9dd51e0a..f687a5172 100755 --- a/util/gvalid/gvalid_z_unit_feature_recursive_test.go +++ b/util/gvalid/gvalid_z_unit_feature_recursive_test.go @@ -341,3 +341,61 @@ func Test_CheckStruct_Recursively_MapAttribute(t *testing.T) { t.Assert(err, `Student Name is required`) }) } + +// https://github.com/gogf/gf/issues/1983 +func Test_Issue1983(t *testing.T) { + // Error as the attribute Student in Teacher is a initialized struct, which has default value. + gtest.C(t, func(t *gtest.T) { + type Student struct { + Name string `v:"required"` + Age int + } + type Teacher struct { + Students Student + } + var ( + teacher = Teacher{} + data = g.Map{ + "students": nil, + } + ) + err := g.Validator().Assoc(data).Data(teacher).Run(ctx) + t.Assert(err, `The Name field is required`) + }) + // The same as upper, it is not affected by association values. + gtest.C(t, func(t *gtest.T) { + type Student struct { + Name string `v:"required"` + Age int + } + type Teacher struct { + Students Student + } + var ( + teacher = Teacher{} + data = g.Map{ + "name": "john", + "students": nil, + } + ) + err := g.Validator().Assoc(data).Data(teacher).Run(ctx) + t.Assert(err, `The Name field is required`) + }) + gtest.C(t, func(t *gtest.T) { + type Student struct { + Name string `v:"required"` + Age int + } + type Teacher struct { + Students *Student + } + var ( + teacher = Teacher{} + data = g.Map{ + "students": nil, + } + ) + err := g.Validator().Assoc(data).Data(teacher).Run(ctx) + t.AssertNil(err) + }) +}