diff --git a/cryptor/rsa_private_example.pem b/cryptor/rsa_private_example.pem index b66cb29..0949b05 100644 --- a/cryptor/rsa_private_example.pem +++ b/cryptor/rsa_private_example.pem @@ -1,51 +1,51 @@ -----BEGIN rsa private key----- -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 -----END rsa private key----- diff --git a/cryptor/rsa_public_example.pem b/cryptor/rsa_public_example.pem index f9e5ea4..2ee7341 100644 --- a/cryptor/rsa_public_example.pem +++ b/cryptor/rsa_public_example.pem @@ -1,14 +1,14 @@ -----BEGIN rsa public key----- -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsZoYOi7lxZ3zk4zTa/nv -lQzPeXNBthaUpAJmC1b2/ldmglZ8ARGoVv45VCjG1yKO47SiN8ijcA///o/EawGZ -8mJHv8WsPUBxzUF2OQHSZ/kHlbi1vZsbGXLcM9siBU79uqudbyqUm/9OkgnTPCUU -zOlaFtKGeyoDM4TBAX7ec8G8LWBdBAgNAaK/vP+iXuPHxLdKNMDDel67nLNYJ30K -C/fUbwHixn5yQLx4zVLF+rv3pUbQuTeEvS+N19Cg1/+5/xzGK+YYdO1ZH4qgubNB -u9C90wH0FhIuDDfswh8liZbjHcXsJ82QPdaxjbz1bSb8OToackMMocvJVI9ToxSB -dGvGoBk1mnLJRSab6nOD56+iRoGJmy6GTpD2I1ie1PHV1Y2TIjUESnxjgPigaYA5 -i/MkXlD/1CtrDW6hsc0/WgCSD7ZGjNTduUwdwmd2es1XIqnamht+QJE1JTbBFLd9 -r5zXQY/5W5nRQHBmWP5fVHy7a/wmF9N2ZpI/DYMarCRIVBsiMdsE62mq63haB6yw -jkVk+78TXtmXTTE8ts6XImt+m+0RY4lAd+UfHvyOe3dH2y5nGSHG4C8CXB264YdA -pZZjjvVZy5K3lsmmBB+4Kje1Rux36F76qEOvjZ2QvpJDTXSXOk1POu6aZc8N+XRu -vG7hmEPdF3xD68VPSZ5/gfcCAwEAAQ== +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsELqn9abxs7ZK81Ipbp5 ++E/5SrOQycAalnRCES3YPOoAV3Ir+99WjEi9Ak4EYS3VrvgISSNiRx1tHDNpBlBq +GF+Mww8wAUmrcXsEndbHma/g3zHGueua9GK0l+hQJ/grkatykgD81gam/SPOMPnJ +SsoQlsTgM47ZHM35adiXqL/gDMZ70lQdB2+03WJXlvfYZWpHsXDqUkveEZbLgZST +36ax8ap+Ge2GoLlD1sNapRxMBH1PGNdzSdV7ZJmzQ3WmjwDAddLX73YE6vhR5tDA +XhrSweNPtIjUhGJsgkW9VTEQWeW9cZmFo/jYuvEDgBnZOh1DJe2BHx06Ymr8Zcgs +OBYddANsk750II8+qUnBekiJkpo0ldCK2VnsLoLM3YCf3Lk7VYuHP6KiCXyh6Fzb +Y1DTRHJRpMJYT8t5xyQis581DbRVPkcse3ig5KsQLe5XXbBBegVrQJpy1b440uZV +W2eSpStX5ZAfNVsU0ZPdI4/i6UNMEa6c7tf83S21Q0K6SEZ5PrfBJECRDxFzJWea +mwppHY8fdeItVk/cxBJ9XRyOyG9pJKMnOMFD++LzGP6uL5FKov7BSplOIMqsJzwh +4zkIjAhgbGVQZm17kg1xvF30c2fgijNEoSIuVWJzlQyQ9cmSaSmmi+SPmf/C/IcU +DF0nikERs1v58vNESvcZP80CAwEAAQ== -----END rsa public key----- diff --git a/docs/api/packages/xerror.md b/docs/api/packages/xerror.md index d363e11..002765b 100644 --- a/docs/api/packages/xerror.md +++ b/docs/api/packages/xerror.md @@ -35,6 +35,7 @@ import ( - [XError_Info](#XError_Info) - [XError_Error](#XError_Error) - [TryUnwrap](#TryUnwrap) +- [TryCatch](#TryCatch)
@@ -167,12 +168,12 @@ import ( func main() { err1 := xerror.New("error").With("level", "high") - err2 := err1.Wrap(errors.New("invalid username")) + err2 := err1.Wrap(errors.New("invalid username")) - fmt.Println(err2.Error()) + fmt.Println(err2.Error()) - // Output: - // error: invalid username + // Output: + // error: invalid username } ``` @@ -489,3 +490,56 @@ func main() { // true } ``` + +### TryCatch + +简单实现的java风格异常处理(try-catch-finally)。try catch不符合go错误处理风格,谨慎使用。
+ +函数签名: + +```go +func NewTryCatch(ctx context.Context) *TryCatch + +func (tc *TryCatch) Try(tryFunc func(ctx context.Context) error) *TryCatch + +func (tc *TryCatch) Catch(catchFunc func(ctx context.Context, err error)) *TryCatch + +func (tc *TryCatch) Finally(finallyFunc func(ctx context.Context)) *TryCatch + +func (tc *TryCatch) Do() +``` + +示例:[运行](todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + calledFinally := false + calledCatch := false + + tc := xerror.NewTryCatch(context.Background()) + + tc.Try(func(ctx context.Context) error { + return errors.New("error in try block") + }).Catch(func(ctx context.Context, err error) { + calledCatch = true + // Error in try block at /path/xxx.go:{line_number} - Cause: error message + // fmt.Println(err.Error()) + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + fmt.Println(calledCatch) + fmt.Println(calledFinally) + + // Output: + // true + // true +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/xerror.md b/docs/en/api/packages/xerror.md index 07019d4..f4dc44f 100644 --- a/docs/en/api/packages/xerror.md +++ b/docs/en/api/packages/xerror.md @@ -35,6 +35,7 @@ import ( - [XError_Info](#XError_Info) - [XError_Error](#XError_Error) - [TryUnwrap](#TryUnwrap) +- [TryCatch](#TryCatch) @@ -166,12 +167,12 @@ import ( func main() { err1 := xerror.New("error").With("level", "high") - err2 := err1.Wrap(errors.New("invalid username")) + err2 := err1.Wrap(errors.New("invalid username")) - fmt.Println(err2.Error()) + fmt.Println(err2.Error()) - // Output: - // error: invalid username + // Output: + // error: invalid username } ``` @@ -487,3 +488,56 @@ func main() { // true } ``` + +### TryCatch + +Simple simulation of Java-style try-catch. It does not align with Go's error-handling philosophy. It is recommended to use it with caution.
+ +Signature: + +```go +func NewTryCatch(ctx context.Context) *TryCatch + +func (tc *TryCatch) Try(tryFunc func(ctx context.Context) error) *TryCatch + +func (tc *TryCatch) Catch(catchFunc func(ctx context.Context, err error)) *TryCatch + +func (tc *TryCatch) Finally(finallyFunc func(ctx context.Context)) *TryCatch + +func (tc *TryCatch) Do() +``` + +Example:[Run](todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/xerror" +) + +func main() { + calledFinally := false + calledCatch := false + + tc := xerror.NewTryCatch(context.Background()) + + tc.Try(func(ctx context.Context) error { + return errors.New("error message ") + }).Catch(func(ctx context.Context, err error) { + calledCatch = true + // Error in try block at /path/xxx.go:{line_number} - Cause: error message + // fmt.Println(err.Error()) + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + fmt.Println(calledCatch) + fmt.Println(calledFinally) + + // Output: + // true + // true +} +``` diff --git a/xerror/trycatch.go b/xerror/trycatch.go new file mode 100644 index 0000000..e688584 --- /dev/null +++ b/xerror/trycatch.go @@ -0,0 +1,100 @@ +package xerror + +import ( + "context" + "fmt" + "runtime" +) + +// TryCatch is a struct to handle try-catch-finally block. +// This implementation is merely a simulation of Java-style try-catch and does not align with Go's error-handling philosophy. It is recommended to use it with caution. +type TryCatch struct { + ctx context.Context + tryFunc func(ctx context.Context) error + catchFunc func(ctx context.Context, err error) + finallyFunc func(ctx context.Context) +} + +// NewTryCatch creates a new TryCatch instance. +func NewTryCatch(ctx context.Context) *TryCatch { + return &TryCatch{ + ctx: ctx, + } +} + +// Try sets the try function. +func (tc *TryCatch) Try(tryFunc func(ctx context.Context) error) *TryCatch { + tc.tryFunc = tryFunc + return tc +} + +// Catch sets the catch function. +func (tc *TryCatch) Catch(catchFunc func(ctx context.Context, err error)) *TryCatch { + tc.catchFunc = catchFunc + return tc +} + +// Finally sets the finally function. +func (tc *TryCatch) Finally(finallyFunc func(ctx context.Context)) *TryCatch { + tc.finallyFunc = finallyFunc + return tc +} + +// Do executes the try-catch-finally block. +func (tc *TryCatch) Do() { + defer func() { + if r := recover(); r != nil { + if tc.catchFunc != nil { + err := fmt.Errorf("panic: %v", r) + tc.catchFunc(tc.ctx, WrapCatchError(err, "Recovered from panic")) + } + } + + if tc.finallyFunc != nil { + tc.finallyFunc(tc.ctx) + } + }() + + if tc.ctx.Err() != nil { + if tc.catchFunc != nil { + tc.catchFunc(tc.ctx, WrapCatchError(tc.ctx.Err(), "Context cancelled or timed out")) + } + return + } + + if tc.tryFunc != nil { + if err := tc.tryFunc(tc.ctx); err != nil { + if tc.catchFunc != nil { + tc.catchFunc(tc.ctx, WrapCatchError(err, "Error in try block")) + } + } + } +} + +// CatchError is an error type to handle try-catch-finally block. +type CatchError struct { + Msg string + File string + Line int + Cause error +} + +// Error returns the error message. +func (e *CatchError) Error() string { + return fmt.Sprintf("%s at %s:%d - Cause: %v", e.Msg, e.File, e.Line, e.Cause) +} + +// WrapCatchError wraps an error with message, file, and line. +func WrapCatchError(err error, msg string) *CatchError { + _, file, line, ok := runtime.Caller(2) + if !ok { + file = "unknown" + line = 0 + } + return &CatchError{ + Msg: msg, + File: file, + Line: line, + Cause: err, + } +} diff --git a/xerror/trycatch_test.go b/xerror/trycatch_test.go new file mode 100644 index 0000000..08e7a78 --- /dev/null +++ b/xerror/trycatch_test.go @@ -0,0 +1,172 @@ +package xerror + +import ( + "context" + "errors" + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +func TestTryCatchSuccess(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTryCatchSuccess") + + counter := 0 + calledCatch := false + calledFinally := false + + tc := NewTryCatch(context.Background()) + + tc.Try(func(ctx context.Context) error { + counter++ + return nil + }).Catch(func(ctx context.Context, err error) { + calledCatch = true + t.Errorf("Catch should not be called") + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + assert.Equal(1, counter) + assert.Equal(false, calledCatch) + assert.Equal(true, calledFinally) +} + +func TestTryCatchError(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTryCatchError") + + var catchedError error + calledFinally := false + + tc := NewTryCatch(context.Background()) + + tc.Try(func(ctx context.Context) error { + return errors.New("error") + }).Catch(func(ctx context.Context, err error) { + catchedError = errors.New("catched error") + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + assert.Equal("catched error", catchedError.Error()) + assert.Equal(true, calledFinally) +} + +func TestTryCatchPanic(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTryCatchPanic") + + var catchedError error + calledFinally := false + + tc := NewTryCatch(context.Background()) + + tc.Try(func(ctx context.Context) error { + panic("panic info") + }).Catch(func(ctx context.Context, err error) { + catchedError = errors.New("catched error") + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + assert.Equal("catched error", catchedError.Error()) + assert.Equal(true, calledFinally) +} + +func TestTryCatchContextCancelled(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTryCatchContextCancelled") + + var catchedError error + calledFinally := false + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + tc := NewTryCatch(ctx) + + tc.Try(func(ctx context.Context) error { + return nil + }).Catch(func(ctx context.Context, err error) { + catchedError = errors.New("catched error") + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + assert.Equal("catched error", catchedError.Error()) + assert.Equal(true, calledFinally) +} + +func TestTryCatchContextTimeout(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTryCatchContextTimeout") + + var catchedError error + calledFinally := false + + ctx, cancel := context.WithTimeout(context.Background(), 0) + defer cancel() + + tc := NewTryCatch(ctx) + + tc.Try(func(ctx context.Context) error { + return nil + }).Catch(func(ctx context.Context, err error) { + catchedError = errors.New("catched error") + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + assert.Equal("catched error", catchedError.Error()) + assert.Equal(true, calledFinally) +} + +func TestTryCatchContextError(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTryCatchContextError") + + var catchedError error + calledFinally := false + + ctx, cancel := context.WithTimeout(context.Background(), 0) + defer cancel() + + tc := NewTryCatch(ctx) + + tc.Try(func(ctx context.Context) error { + return errors.New("error") + }).Catch(func(ctx context.Context, err error) { + catchedError = errors.New("catched error") + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + assert.Equal("catched error", catchedError.Error()) + assert.Equal(true, calledFinally) +} + +func TestTryCatchNoCatch(t *testing.T) { + t.Parallel() + + assert := internal.NewAssert(t, "TestTryCatchNoCatch") + + calledFinally := false + + tc := NewTryCatch(context.Background()) + + tc.Try(func(ctx context.Context) error { + return errors.New("error") + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + assert.Equal(true, calledFinally) +} diff --git a/xerror/xerror_example_test.go b/xerror/xerror_example_test.go index a936fe0..b03003f 100644 --- a/xerror/xerror_example_test.go +++ b/xerror/xerror_example_test.go @@ -1,6 +1,7 @@ package xerror import ( + "context" "errors" "fmt" "reflect" @@ -61,7 +62,7 @@ func ExampleXError_StackTrace() { // Output: // github.com/duke-git/lancet/v2/xerror.ExampleXError_StackTrace - // 52 + // 53 // true } @@ -154,3 +155,27 @@ func ExampleTryUnwrap() { // 42 // true } + +func ExampleTryCatch() { + calledFinally := false + calledCatch := false + + tc := NewTryCatch(context.Background()) + + tc.Try(func(ctx context.Context) error { + return errors.New("error message") + }).Catch(func(ctx context.Context, err error) { + calledCatch = true + // Error in try block at /path/xxx.go:174 - Cause: error message + // fmt.Println(err.Error()) + }).Finally(func(ctx context.Context) { + calledFinally = true + }).Do() + + fmt.Println(calledCatch) + fmt.Println(calledFinally) + + // Output: + // true + // true +}