Skip to content

Commit

Permalink
[error] Improved abstractions
Browse files Browse the repository at this point in the history
- Some changes due to other's changes
- Add new `Chain` abstraction
- Fix error implementation by last decisions
- Some minor fixes and improvements
  • Loading branch information
OmidHekayati committed Sep 12, 2024
1 parent 0d6d63f commit 8357b62
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 99 deletions.
42 changes: 17 additions & 25 deletions error/chain-error.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,33 @@
package error

import (
"memar/protocol"
error_p "memar/error/protocol"
)

// PastChain unwrap an error and return last chain of error.
func PastChain(err error_p.Error) (last error_p.Error) {
var chainedError, ok = err.(error_p.Chain)
if ok {
return chainedError.PastChain()
}
return
}

// NewChain wrap an error and additional information usually use in logging to save more details about error.
func NewChain(err *Error, info string) (ce *ChainError) {
if err == nil {
func NewChain(err error_p.Error, chain error_p.Error) (ce *ChainError) {
if err == nil || chain == nil {
return
}
return &ChainError{
Err: err,
info: info,
Error: err,
chain: chain,
}
}

// ChainError is a extended implementation of Error to carry custom details along error.
type ChainError struct {
*Err
info string
}

func (ce *ChainError) PastChain() protocol.Error { return ce.Err }

//memar:impl memar/protocol.Stringer
func (ce *ChainError) ToString() string {
return "\n" + ce.Err.ToString() + "\n Chain Info: " + ce.info
error_p.Error
chain error_p.Error
}
func (ce *ChainError) FromString(s string) (err protocol.Error) {
// TODO:::
return
}

// Go compatibility methods. Unwrap provides compatibility for Go 1.13 error chains.
func (ce *ChainError) Error() string { return ce.ToString() }
func (ce *ChainError) Cause() error { return ce.Err }
func (ce *ChainError) Unwrap() error { return ce.Err }

// func (ce *ChainError) Is(error) bool
// func (ce *ChainError) As(any) bool
func (ce *ChainError) PastChain() error_p.Error { return ce.chain }
28 changes: 15 additions & 13 deletions error/error-type.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,23 @@
package error

import (
"memar/protocol"
error_p "memar/error/protocol"
)

type ErrorType protocol.ErrorType

func (et *ErrorType) Set(errorType protocol.ErrorType) { *et |= ErrorType(errorType) }
func (et *ErrorType) Unset(errorType protocol.ErrorType) { *et &= ^ErrorType(errorType) }
func (et ErrorType) Check(errorType protocol.ErrorType) bool {
return errorType&protocol.ErrorType(et) == errorType
//memar:impl memar/error/protocol.Internal
func IsInternal(err error_p.Error) bool {
var interErr, ok = err.(error_p.Internal)
return ok && interErr.Internal()
}

func (et Error) Internal() bool { return et.Check(protocol.ErrorType_Internal) }
func (et Error) Temporary() bool { return et.Check(protocol.ErrorType_Temporary) }
func (et Error) Timeout() bool { return et.Check(protocol.ErrorType_Timeout) }
//memar:impl memar/error/protocol.Temporary
func IsTemporary(err error_p.Error) bool {
var tempErr, ok = err.(error_p.Temporary)
return ok && tempErr.Temporary()
}

func (et *Error) SetInternal() { et.Set(protocol.ErrorType_Internal) }
func (et *Error) SetTemporary() { et.Set(protocol.ErrorType_Temporary) }
func (et *Error) SetTimeout() { et.Set(protocol.ErrorType_Timeout) }
//memar:impl memar/error/protocol.Timeout
func IsTimeout(err error_p.Error) bool {
var timeErr, ok = err.(error_p.Timeout)
return ok && timeErr.Timeout()
}
56 changes: 9 additions & 47 deletions error/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,58 +3,20 @@
package error

import (
"memar/datatype"
"memar/mediatype"
"memar/protocol"
error_p "memar/error/protocol"
)

// Err is the same as the Error.
// Use this type when embed in other struct to solve field & method same name problem(Error struct and Error() method) to satisfy interfaces.
type Err = Error

// Error implements protocol.Error
type Error struct {
ErrorType

mediatype.MT
datatype.DataType
}

// Init initialize Error that implement protocol.Error
// Never change MediaType due to it adds unnecessary complicated troubleshooting errors on SDK.
//
//memar:impl memar/protocol.ObjectLifeCycle
func (e *Error) Init(mediatype string) (err protocol.Error) {
err = e.MT.Init(mediatype)
// if err != nil {
// return
// }
return
}

// Equal compare two Error.
func (e *Error) Equal(err protocol.Error) bool {
if e == nil && err == nil {
// IsEqual compare two Error.
// Suggest to add more logic to Error.Equal() logic such as chain situation!
func IsEqual(base, with error_p.Error) bool {
if base == nil && with == nil {
return true
}
if e != nil && err != nil && e.ID() == err.ID() {
if base != nil &&
with != nil &&
base.MediaType() == with.MediaType() &&
base.DataTypeID() == with.DataTypeID() {
return true
}
// TODO::: check err as chain error
return false
}

func (e *Error) Type() protocol.ErrorType { return protocol.ErrorType(e.ErrorType) }
func (e *Error) CheckType(et protocol.ErrorType) bool { return e.ErrorType.Check(et) }

func (e *Error) Notify() {
// TODO:::
}

// Go compatibility methods. Unwrap provides compatibility for Go 1.13 error chains.
func (e *Error) Error() string { return e.MT.MediaType() }
func (e *Error) Cause() error { return nil }
func (e *Error) Unwrap() error { return nil }

// func (e *Error) Is(error) bool
// func (e *Error) As(any) bool
11 changes: 7 additions & 4 deletions error/protocol/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,24 @@ package error_p
import (
// primitive_p "memar/computer/language/primitive/protocol"
datatype_p "memar/datatype/protocol"
mediatype_p "memar/mediatype/protocol"
)

// Error is base behaviors that any Error capsule must implement.
//
// It is similar to opaque error model describe here: https://dave.cheney.net/paste/gocon-spring-2016.pdf
// or this RFC: https://tools.ietf.org/html/rfc7807
// Other suggestions:
// - opaque error model: https://dave.cheney.net/paste/gocon-spring-2016.pdf
// - RFC7807: https://tools.ietf.org/html/rfc7807
// - https://doc.rust-lang.org/stable/std/error/trait.Error.html
type Error interface {
datatype_p.DataType
mediatype_p.MediaType

// Can't un-comment below due to Golang import cycle problem, So add it manually!
// Can't un-comment below due to Golang import cycle problem, So add it manually.
// primitive_p.Equivalence[Error]
Equal(with Error) bool

// ADT
// MediaType

// Below methods comment in favor of log_p.Event_Message interface.
// Error() string
Expand Down
4 changes: 4 additions & 0 deletions error/protocol/gui.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

package error_p

// GUI indicate how Error capsule must act in GUI applications.
//
// GUI can use also in CLI apps by e.g. use `//go:build gui` tag,
// But strongly suggest DON'T think in this way and just use `Log` concept
type GUI interface {
// Notify error to user by graphic, sound and vibration (Haptic Feedback)
Notify()
Expand Down
8 changes: 8 additions & 0 deletions error/protocol/optional.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ package error_p
// Belows are some other error behaviors that Errors capsule CAN implement,
// but any error package can introduce new behaviors for any purpose.

// Chian errors can use in situation that error occurred but can't handle in that layer and
// Upper layer also can't handle it directly and decide to log it!
// Use chain errors to return multiple error to caller and
// Usually caller log them and return another error to caller that almost always SDK client e.g. GUI, Apps, ...
type Chain interface {
PastChain() (last Error)
}

type Internal interface {
// Who cause the error?
// Internal : means calling process logic has runtime bugs like HTTP server error status codes ( 500 – 599 ).
Expand Down
18 changes: 8 additions & 10 deletions error/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,17 @@
package error

import (
"memar/protocol"
error_p "memar/error/protocol"
)

func ToGoError(err protocol.Error) error {
func ToGoError(err error_p.Error) error {
if err == nil {
return nil
}

var exErr = err.(*Error)
if exErr != nil {
return exErr
}

return &errorString{msg: err.ToString()}
var errStr errorString
errStr.msg = err.Summary()
return &errStr
}

// errorString is a trivial implementation of error.
Expand All @@ -26,15 +23,16 @@ type errorString struct {

func (e *errorString) Error() string { return e.msg }

func ToError(err error) protocol.Error {
func ToError(err error) error_p.Error {
if err == nil {
return nil
}

var exErr = err.(*Error)
var exErr = err.(error_p.Error)
if exErr != nil {
return exErr
}

// TODO:::
return nil
}

0 comments on commit 8357b62

Please sign in to comment.