4 Commits

3 changed files with 152 additions and 26 deletions

View File

@@ -5,7 +5,7 @@ A collection of generic utility functions for Go projects.
## Installation ## Installation
```bash ```bash
go get github.com/aykhans/go-utils go get go.aykhans.me/utils
``` ```
## Packages ## Packages
@@ -16,7 +16,7 @@ Generic type conversion utilities.
**ToPtr** - Convert any value to a pointer **ToPtr** - Convert any value to a pointer
```go ```go
import "github.com/aykhans/go-utils/common" import "go.aykhans.me/utils/common"
num := 42 num := 42
ptr := common.ToPtr(num) // *int ptr := common.ToPtr(num) // *int
@@ -24,7 +24,7 @@ ptr := common.ToPtr(num) // *int
**IsNilOrZero** - Check if a pointer is nil or points to a zero value **IsNilOrZero** - Check if a pointer is nil or points to a zero value
```go ```go
import "github.com/aykhans/go-utils/common" import "go.aykhans.me/utils/common"
var ptr *int var ptr *int
common.IsNilOrZero(ptr) // true (nil pointer) common.IsNilOrZero(ptr) // true (nil pointer)
@@ -42,7 +42,7 @@ String parsing utilities with generic type support.
**ParseString** - Parse string to various types **ParseString** - Parse string to various types
```go ```go
import "github.com/aykhans/go-utils/parser" import "go.aykhans.me/utils/parser"
num, err := parser.ParseString[int]("42") num, err := parser.ParseString[int]("42")
duration, err := parser.ParseString[time.Duration]("5s") duration, err := parser.ParseString[time.Duration]("5s")
@@ -72,7 +72,7 @@ Slice manipulation utilities.
**Cycle** - Create an infinite cycler through items **Cycle** - Create an infinite cycler through items
```go ```go
import "github.com/aykhans/go-utils/slice" import "go.aykhans.me/utils/slice"
next := slice.Cycle(1, 2, 3) next := slice.Cycle(1, 2, 3)
fmt.Println(next()) // 1 fmt.Println(next()) // 1
@@ -93,7 +93,7 @@ Map utility functions.
**InitMap** - Initialize a map pointer if nil **InitMap** - Initialize a map pointer if nil
```go ```go
import "github.com/aykhans/go-utils/maps" import "go.aykhans.me/utils/maps"
var m map[string]int var m map[string]int
maps.InitMap(&m) maps.InitMap(&m)
@@ -114,7 +114,7 @@ Number utility functions.
**NumLen** - Calculate the number of digits in an integer **NumLen** - Calculate the number of digits in an integer
```go ```go
import "github.com/aykhans/go-utils/number" import "go.aykhans.me/utils/number"
number.NumLen(42) // returns 2 number.NumLen(42) // returns 2
number.NumLen(-128) // returns 3 number.NumLen(-128) // returns 3
@@ -133,68 +133,68 @@ Supported types: `int`, `int8`, `int16`, `int32`, `int64`, `uint`, `uint8`, `uin
Advanced error handling utilities. Advanced error handling utilities.
**HandleError** - Process errors with custom matchers **Handle** - Process errors with custom matchers
```go ```go
import "github.com/aykhans/go-utils/errors" import "go.aykhans.me/utils/errors"
handled, result := errors.HandleError(err, handled, result := errors.Handle(err,
errors.OnSentinelError(io.EOF, func(e error) error { errors.OnSentinel(io.EOF, func(e error) error {
return nil // EOF is expected, ignore it return nil // EOF is expected, ignore it
}), }),
errors.OnCustomError(func(e *CustomError) error { errors.OnType(func(e *CustomError) error {
return fmt.Errorf("custom error: %w", e) return fmt.Errorf("custom error: %w", e)
}), }),
) )
``` ```
**HandleErrorOrDie** - Handle errors or panic if unhandled **MustHandle** - Handle errors or panic if unhandled
```go ```go
result := errors.HandleErrorOrDie(err, result := errors.MustHandle(err,
errors.OnSentinelError(context.Canceled, func(e error) error { errors.OnSentinel(context.Canceled, func(e error) error {
return fmt.Errorf("operation canceled") return fmt.Errorf("operation canceled")
}), }),
) // Panics if err doesn't match any handler ) // Panics if err doesn't match any handler
``` ```
**HandleErrorOrDefault** - Handle errors with a default fallback **HandleOr** - Handle errors with a default fallback
```go ```go
result := errors.HandleErrorOrDefault(err, result := errors.HandleOr(err,
func(e error) error { func(e error) error {
// Default handler for unmatched errors // Default handler for unmatched errors
return fmt.Errorf("unexpected error: %w", e) return fmt.Errorf("unexpected error: %w", e)
}, },
errors.OnSentinelError(context.Canceled, func(e error) error { errors.OnSentinel(context.Canceled, func(e error) error {
return fmt.Errorf("operation canceled") return fmt.Errorf("operation canceled")
}), }),
errors.OnCustomError(func(e *ValidationError) error { errors.OnType(func(e *ValidationError) error {
return fmt.Errorf("validation failed: %w", e) return fmt.Errorf("validation failed: %w", e)
}), }),
) )
// Pass nil to suppress unmatched errors // Pass nil to suppress unmatched errors
result := errors.HandleErrorOrDefault(err, nil, result := errors.HandleOr(err, nil,
errors.OnSentinelError(io.EOF, func(e error) error { errors.OnSentinel(io.EOF, func(e error) error {
return errors.New("EOF handled") return errors.New("EOF handled")
}), }),
) // Returns nil for unmatched errors ) // Returns nil for unmatched errors
``` ```
**OnSentinelError** - Create matcher for sentinel errors (like `io.EOF`) **OnSentinel** - Create matcher for sentinel errors (like `io.EOF`)
```go ```go
matcher := errors.OnSentinelError(io.EOF, func(e error) error { matcher := errors.OnSentinel(io.EOF, func(e error) error {
log.Println("reached end of file") log.Println("reached end of file")
return nil return nil
}) })
``` ```
**OnCustomError** - Create matcher for custom error types **OnType** - Create matcher for custom error types
```go ```go
type ValidationError struct { type ValidationError struct {
Field string Field string
Msg string Msg string
} }
matcher := errors.OnCustomError(func(e *ValidationError) error { matcher := errors.OnType(func(e *ValidationError) error {
log.Printf("validation failed on field %s", e.Field) log.Printf("validation failed on field %s", e.Field)
return fmt.Errorf("invalid input: %w", e) return fmt.Errorf("invalid input: %w", e)
}) })

View File

@@ -21,6 +21,9 @@ type ErrorMatcher struct {
// or (false, nil) if no matcher matches the error. // or (false, nil) if no matcher matches the error.
// If err is nil, returns (true, nil). // If err is nil, returns (true, nil).
// //
// Deprecated: HandleError is deprecated and will be removed in a future release.
// Use Handle instead, which provides the same functionality.
//
// Example: // Example:
// //
// handled, result := HandleError(err, // handled, result := HandleError(err,
@@ -58,11 +61,31 @@ func HandleError(err error, matchers ...ErrorMatcher) (bool, error) {
return false, err // No matcher found return false, err // No matcher found
} }
// Handle processes an error against a list of matchers and executes the appropriate handler.
// It returns (true, handlerResult) if a matching handler is found and executed,
// or (false, nil) if no matcher matches the error.
// If err is nil, returns (true, nil).
//
// Example:
//
// handled, result := Handle(err,
// OnSentinel(io.EOF, func(e error) error {
// return nil // EOF is expected, ignore it
// }),
// OnType(func(e *CustomError) error {
// return fmt.Errorf("custom error: %w", e)
// }),
// )
var Handle = HandleError
// HandleErrorOrDie processes an error against a list of matchers and executes the appropriate handler. // HandleErrorOrDie processes an error against a list of matchers and executes the appropriate handler.
// If a matching handler is found, it returns the handler's result. // If a matching handler is found, it returns the handler's result.
// If no matcher matches the error, it panics with a descriptive message. // If no matcher matches the error, it panics with a descriptive message.
// This function is useful when all expected error types must be handled explicitly. // This function is useful when all expected error types must be handled explicitly.
// //
// Deprecated: HandleErrorOrDie is deprecated and will be removed in a future release.
// Use MustHandle instead, which provides the same functionality.
//
// Example: // Example:
// //
// result := HandleErrorOrDie(err, // result := HandleErrorOrDie(err,
@@ -81,6 +104,23 @@ func HandleErrorOrDie(err error, matchers ...ErrorMatcher) error {
return err return err
} }
// MustHandle processes an error against a list of matchers and executes the appropriate handler.
// If a matching handler is found, it returns the handler's result.
// If no matcher matches the error, it panics with a descriptive message.
// This function is useful when all expected error types must be handled explicitly.
//
// Example:
//
// result := MustHandle(err,
// OnSentinel(context.Canceled, func(e error) error {
// return fmt.Errorf("operation canceled")
// }),
// OnType(func(e *ValidationError) error {
// return fmt.Errorf("validation failed: %w", e)
// }),
// ) // Panics if err doesn't match any handler
var MustHandle = HandleErrorOrDie
// HandleErrorOrDefault processes an error against a list of matchers and executes the appropriate handler. // HandleErrorOrDefault processes an error against a list of matchers and executes the appropriate handler.
// If a matching handler is found, it returns the handler's result. // If a matching handler is found, it returns the handler's result.
// If no matcher matches the error, it executes the default handler (dft) and returns its result. // If no matcher matches the error, it executes the default handler (dft) and returns its result.
@@ -88,6 +128,9 @@ func HandleErrorOrDie(err error, matchers ...ErrorMatcher) error {
// This function is useful when you want to handle specific error cases explicitly // This function is useful when you want to handle specific error cases explicitly
// while providing a fallback handler for all other errors. // while providing a fallback handler for all other errors.
// //
// Deprecated: HandleErrorOrDefault is deprecated and will be removed in a future release.
// Use HandleOr instead, which provides the same functionality.
//
// Example: // Example:
// //
// result := HandleErrorOrDefault(err, // result := HandleErrorOrDefault(err,
@@ -120,6 +163,36 @@ func HandleErrorOrDefault(err error, dft ErrorHandler, matchers ...ErrorMatcher)
return err return err
} }
// HandleOr processes an error against a list of matchers and executes the appropriate handler.
// If a matching handler is found, it returns the handler's result.
// If no matcher matches the error, it executes the default handler (dft) and returns its result.
// If dft is nil, unmatched errors return nil (effectively suppressing the error).
// This function is useful when you want to handle specific error cases explicitly
// while providing a fallback handler for all other errors.
//
// Example:
//
// result := HandleOr(err,
// func(e error) error {
// // Default handler for unmatched errors
// return fmt.Errorf("unexpected error: %w", e)
// },
// OnSentinel(context.Canceled, func(e error) error {
// return fmt.Errorf("operation canceled")
// }),
// OnType(func(e *ValidationError) error {
// return fmt.Errorf("validation failed: %w", e)
// }),
// )
//
// // Suppress unmatched errors by passing nil as default handler
// result := HandleOr(err, nil,
// OnSentinel(io.EOF, func(e error) error {
// return errors.New("EOF handled")
// }),
// ) // Returns nil for unmatched errors
var HandleOr = HandleErrorOrDefault
// OnSentinelError creates an ErrorMatcher for sentinel errors. // OnSentinelError creates an ErrorMatcher for sentinel errors.
// Sentinel errors are predefined error values that are compared using errors.Is. // Sentinel errors are predefined error values that are compared using errors.Is.
// //
@@ -130,6 +203,9 @@ func HandleErrorOrDefault(err error, dft ErrorHandler, matchers ...ErrorMatcher)
// The handler function receives the original error and can return a new error // The handler function receives the original error and can return a new error
// or nil to suppress it. // or nil to suppress it.
// //
// Deprecated: OnSentinelError is deprecated and will be removed in a future release.
// Use OnSentinel instead, which provides the same functionality.
//
// Example: // Example:
// //
// matcher := OnSentinelError(io.EOF, func(e error) error { // matcher := OnSentinelError(io.EOF, func(e error) error {
@@ -144,6 +220,24 @@ func OnSentinelError(sentinelErr error, handler ErrorHandler) ErrorMatcher {
} }
} }
// OnSentinel creates an ErrorMatcher for sentinel errors.
// Sentinel errors are predefined error values that are compared using errors.Is.
//
// This is used with Handle or MustHandle to match specific error
// values like io.EOF, context.Canceled, or custom sentinel errors defined with
// errors.New or fmt.Errorf.
//
// The handler function receives the original error and can return a new error
// or nil to suppress it.
//
// Example:
//
// matcher := OnSentinel(io.EOF, func(e error) error {
// log.Println("reached end of file")
// return nil // suppress EOF error
// })
var OnSentinel = OnSentinelError
// OnCustomError creates an ErrorMatcher for custom error types. // OnCustomError creates an ErrorMatcher for custom error types.
// Custom error types are struct types that implement the error interface, // Custom error types are struct types that implement the error interface,
// and are matched using errors.As to unwrap error chains. // and are matched using errors.As to unwrap error chains.
@@ -155,6 +249,9 @@ func OnSentinelError(sentinelErr error, handler ErrorHandler) ErrorMatcher {
// This is particularly useful for handling errors with additional context or data, // This is particularly useful for handling errors with additional context or data,
// such as validation errors, network errors, or domain-specific errors. // such as validation errors, network errors, or domain-specific errors.
// //
// Deprecated: OnCustomError is deprecated and will be removed in a future release.
// Use OnType instead, which provides the same functionality.
//
// Example: // Example:
// //
// type ValidationError struct { // type ValidationError struct {
@@ -183,3 +280,32 @@ func OnCustomError[T error](handler func(T) error) ErrorMatcher {
IsSentinel: false, IsSentinel: false,
} }
} }
// OnType creates an ErrorMatcher for custom error types.
// Custom error types are struct types that implement the error interface,
// and are matched using errors.As to unwrap error chains.
//
// The type parameter T specifies the error type to match. The handler function
// receives the unwrapped typed error, allowing you to access type-specific fields
// and methods.
//
// This is particularly useful for handling errors with additional context or data,
// such as validation errors, network errors, or domain-specific errors.
//
// Example:
//
// type ValidationError struct {
// Field string
// Msg string
// }
// func (e *ValidationError) Error() string {
// return fmt.Sprintf("%s: %s", e.Field, e.Msg)
// }
//
// matcher := OnType(func(e *ValidationError) error {
// log.Printf("validation failed on field %s: %s", e.Field, e.Msg)
// return fmt.Errorf("invalid input: %w", e)
// })
func OnType[T error](handler func(T) error) ErrorMatcher {
return OnCustomError(handler)
}

2
go.mod
View File

@@ -1,4 +1,4 @@
module github.com/aykhans/go-utils module go.aykhans.me/utils
go 1.25.0 go 1.25.0