-
Notifications
You must be signed in to change notification settings - Fork 0
errs.Err
Error handling in Sabi framework is a little unique.
Sabi uses errs.Err
type instead of Golang standard error
.
errs.Err
takes an any structure type which indicates a reason of an error on creating it.
And errs.Err
has some functionalities to make it easier to hold and get error informations.
The code which creates an Golang standard error
is as follows.
It is needed for error
to implement a structure type and its Error
method, at least.
if there is an error causing the new error, it is needed to implement Unwrap
method, too.
The amount of these codes are non-negligible, and demotivate to implements a lot of error
(s) for detailed error handling.
(error)
type FailToDoSomethingError struct { Name string, Amount int }
func (err FailToDoSomethingError) Error() string { ... }
err := FailToDoSomethingError{Name:name, Amount:amount }
On the other hand, The code which creates a errs.Err
is easier, as follows.
It is only needed for errs.Err
to implement a structure type.
(errs.Err)
type FailToDoSomething struct { Name string, Amount int }
err := errs.New(FailToDoSomething{Name:name, Amount:amount})
If there is a causing error, the code to create an error becomes as follows:
(error)
type FailToDoSomethingError struct { Name string, Amount int, Cause error }
func (err FailToDoSomethingError) Error() string { ... }
func (err FailToDoSomethingError) Unwrap() error { return err.Cause }
err := FailToDoSomethingError{Name:name, Amount:amount, Cause:cause }
(errs.Err)
type FailToDoSomething struct { Name string, Amount int }
err := errs.New(FailToDoSomething{Name:name, Amount:amount}, cause)
The code of an error which indicates no error is as follows:
(error)
var err error = nil
(errs.Err)
err := errs.Ok()
To distinct error kinds, a type switch statement can be used.
For Golang error
, type switch is applied to a type of an error.
switch err.(type) {
case nil:
...
case FailToDoSomethingError:
...
default;
...
}
For errs.Err
, type switch is applied to a type of its reason.
switch err.Reason().(type) {
case nil:
...
case FailToDoSomething:
...
default:
...
}
Since Golang error
is an interface for a structure type, error informations are held in its structure fields.
These field values can be gotten with type cast of the error structure.
type FailToDoSomethingError struct { Name string }
func (e FailToDoSomethingError) Error() string { ... }
func doSomething() error { ... }
err := doSomething()
casted, ok := err.(FailToDoSomethingError)
fmt.Printf("Name = %s\n", casted.Name)
errs.Err
is also a structure type but it holds another structure type which indicates a reason of the error, and error informations are held in the fields of the reason structure type.
These field values can be gotten with type cast of the reason structure.
type FailToDoSomething struct { Name string }
func doSomething() errs.Err { ... }
err := doSomething()
reason, ok := err.Reason().(FailToDoSomething)
fmt.Printf("Name = %s\n", reason.Name)
In addition, errs.Err
provides another way to get informations in itself, Get
method and Situation
method.
Get
method gets a field value of an error reason by a field name, and Situation
method gets all fields of an error reason with a map. Since these two methods are not needed to cast type, the code become more simple.
However both of these methods uses reflection features of Golang, and take more time to process. Therefore, these two method should be suppressed to use.
type FailToDoSomething struct { Name string }
func doSomething() errs.Err { ... }
err := doSomething()
fmt.Printf("Name = %s\n", reason.Get("Name"))
m := err.Situation()
fmt.Printf("Name = %s\n", m["Name"])
In general, a function returns an error, and the error is checked if it is non error, then next function is executed. The example code is following:
func doSomething() error { ... }
func doNextThing() error { ... }
err := doSomething()
if err != null {
return err
}
return doNextThing()
In the case using Sabi framework, the similar code can be written more simply by using IfOk
methods, like:
func doSomething() errs.Err { ... }
func doNextThing() errs.Err { ... }
return doSomething().IfOk(doNextThing)
In Sabi framework, a function with the signature func() errs.Err
can be operated as a 'runner' and used with sabi.Seq
or sabi.Para
, etc, too.
errs.Err
supports notification of error creations.
Notification handlers can be registered with errs.AddSyncHandler
, which is for synchronous handling, or errs.AddAsyncHandler
, which is for asynchronous handling.
When a errs.Err
is created with errs.New
function, those registered handlers are executed in the function.
Handlers are passed a errs.Err
instance and a errs.ErrOcc
instance.
errs.ErrOcc
is a structure having time when the error occurred, file name and line number where the error occurred.
The following code is a sample of adding error notification handlers:
errs.AddSyncHandler(func(err errs.Err, occ errs.ErrOcc) {
fmt.Printf("%s (%s:%d) %v\n",
occ.Time().Format("2006-01-02T15:04:05"),
occ.File(), occ.Line(), err)
})
errs.FixCfg()
type FailToDoSomething struct {Name string}
errs.New(FailToDoSomething{Name:"abc"})
---
(stdout)
2023-05-25T00:34:20 (main.go:19) {reason=FailToDoSomething, Name=abc}