mirror of
https://gitee.com/johng/gf.git
synced 2024-11-29 18:57:44 +08:00
improve package gerror, add HasCode/HasError function for package gerror (#2006)
This commit is contained in:
parent
2c70bb6a00
commit
5d51e9fa2c
@ -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
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
}
|
||||
|
110
errors/gerror/gerror_api.go
Normal file
110
errors/gerror/gerror_api.go
Normal file
@ -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),
|
||||
}
|
||||
}
|
139
errors/gerror/gerror_api_code.go
Normal file
139
errors/gerror/gerror_api_code.go
Normal file
@ -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
|
||||
}
|
118
errors/gerror/gerror_api_stack.go
Normal file
118
errors/gerror/gerror_api_stack.go
Normal file
@ -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[:])]
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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[:])]
|
||||
}
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user