Skip to content

Commit

Permalink
feat(results): add README for results package with usage examples
Browse files Browse the repository at this point in the history
replace InnerMessage with InnerPayload and update related functions and tests

use strings.Builder in ResultImpl.String() for performance improvements
  • Loading branch information
eser committed Aug 24, 2024
1 parent abf9cd5 commit 3751da4
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 22 deletions.
6 changes: 5 additions & 1 deletion pkg/bliss/httpfx/uris/patterns.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@ func ParsePattern(s string) (_ *Pattern, err error) { //nolint:funlen,gocognit,c
}

if method != "" && !IsValidMethod(method) {
return nil, ErrInvalidMethod.New().WithAttribute(slog.String("pattern", s), slog.String("method", method))
return nil, ErrInvalidMethod.New().
WithAttribute(
slog.String("pattern", s),
slog.String("method", method),
)
}

p := &Pattern{Str: s, Method: method}
Expand Down
113 changes: 113 additions & 0 deletions pkg/bliss/results/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# bliss/results

## Overview
The **results** package provides a structured way to handle and represent operational results, including errors, within the application.

The documentation below provides an overview of the package, its types, functions, and usage examples. For more detailed information, refer to the source code and tests.


## API

### Result Interface
Defines the contract for result types.

```
type Result interface {
error
Unwrap() error
IsError() bool
String() string
Attributes() []slog.Attr
}
```

**Methods:**
- `Error() string`: Returns the error message.
- `Unwrap() error`: Returns the underlying error.
- `IsError() bool`: Indicates if the result is an error.
- `String() string`: Returns the string representation of the result.
- `Attributes() []slog.Attr`: Returns the attributes associated with the result.


### Define function
Creates a new `Definition` object.

```go
// func Define(code string, message string, attributes ...slog.Attr) *Definition

var (
resOk = Define("OP001", "OK")
resNotFound = Define("OP002", "Not Found")
resFailure = Define("OP003", "Fail")
)
```

### Definition.New and Definitions.Wrap methods
Creates a new `Result` implementation from a definition.

Example 1:
```go
var (
resOk = Define("OP001", "OK")
resNotFound = Define("OP002", "Not Found")
resFailure = Define("OP003", "Fail")
)

// func (r *Definition) New(payload ...any) ResultImpl
// func (r *Definition) Wrap(err error, payload ...any) ResultImpl

func FileOp(filename string) Result {
file, err := os.Open(filepath.Clean(filename))
if err != nil {
if os.IsNotExist(err) {
return resNotFound.New()
}

return resFailure.Wrap(err)
}

...

return resOk.New()
}
```

Example 2:
```go
var (
resOk = Define("PARSE001", "OK")
resSyntaxError = Define("PARSE002", "Syntax Error")
resInvalidOperation = Define("PARSE003", "Invalid Operation")
)

// func (r *Definition) New(payload ...any) ResultImpl
// func (r *Definition) Wrap(err error, payload ...any) ResultImpl

func Parse(...) Result {
if ... {
// Output: [PARSE002] Syntax Error: host/path missing / (pattern=..., method=...)
return resSyntaxError.New("host/path missing /").
WithAttribute(
slog.String("pattern", str),
slog.String("method", method),
)
}

if ... {
// Output: [PARSE003] Invalid Operation: method defined twice (pattern=..., method=...)
return resSyntaxError.New("method defined twice").
WithAttribute(
slog.String("pattern", str),
slog.String("method", method),
)
}

...

// Output: [PARSE001] OK
return resOk.New()
}

fmt.Println(Parse(...).String())
```
17 changes: 4 additions & 13 deletions pkg/bliss/results/define.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package results

import (
"log/slog"
"strings"
)

type Definition struct {
Expand All @@ -25,30 +24,22 @@ func Define(code string, message string, attributes ...slog.Attr) *Definition {
}
}

func (r *Definition) New(messages ...string) ResultImpl {
if messages == nil {
messages = []string{}
}

func (r *Definition) New(payload ...any) ResultImpl {
return ResultImpl{
Definition: r,

InnerError: nil,
InnerMessage: strings.Join(messages, ", "),
InnerPayload: payload,
InnerAttributes: []slog.Attr{},
}
}

func (r *Definition) Wrap(err error, messages ...string) ResultImpl {
if messages == nil {
messages = []string{}
}

func (r *Definition) Wrap(err error, payload ...any) ResultImpl {
return ResultImpl{
Definition: r,

InnerError: err,
InnerMessage: strings.Join(messages, ", "),
InnerPayload: payload,
InnerAttributes: []slog.Attr{},
}
}
30 changes: 22 additions & 8 deletions pkg/bliss/results/result.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package results

import (
"fmt"
"log/slog"
"strings"

"github.com/eser/go-service/pkg/bliss/lib"
)
Expand All @@ -16,11 +16,11 @@ type Result interface {
Attributes() []slog.Attr
}

type ResultImpl struct {
type ResultImpl struct { //nolint:errname
Definition *Definition

InnerError error
InnerMessage string
InnerPayload any
InnerAttributes []slog.Attr
}

Expand All @@ -41,20 +41,34 @@ func (r ResultImpl) IsError() bool {
func (r ResultImpl) String() string {
attrs := r.Attributes()

var attrsStr string
builder := strings.Builder{}
builder.WriteRune('[')
builder.WriteString(r.Definition.Code)
builder.WriteString("] ")
builder.WriteString(r.Definition.Message)

if len(attrs) > 0 {
attrsStr = fmt.Sprintf(" (%s)", lib.SerializeSlogAttrs(attrs))
builder.WriteString(" (")
builder.WriteString(lib.SerializeSlogAttrs(attrs))
builder.WriteRune(')')
}

if r.InnerError != nil {
return fmt.Sprintf("[%s] %s%s: %s", r.Definition.Code, r.Definition.Message, attrsStr, r.InnerError.Error())
builder.WriteString(": ")
builder.WriteString(r.InnerError.Error())
}

return fmt.Sprintf("[%s] %s%s", r.Definition.Code, r.Definition.Message, attrsStr)
return builder.String()
}

func (r ResultImpl) Attributes() []slog.Attr {
return append(r.Definition.Attributes, r.InnerAttributes...)
attrs := r.Definition.Attributes

if r.InnerPayload != nil {
attrs = append(attrs, slog.Any("payload", r.InnerPayload))
}

return append(attrs, r.InnerAttributes...)
}

func (r ResultImpl) WithError(err error) ResultImpl {
Expand Down

0 comments on commit 3751da4

Please sign in to comment.