mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
Upgrade to new go errors and some minor fixes and improvements including easier debugging of invalid routes and e.t.c.
Former-commit-id: 5809157b952ccc61a67a9861470774b3a6fee024
This commit is contained in:
parent
c8236a8d3e
commit
221978e41a
20
README.md
20
README.md
|
@ -12,26 +12,6 @@ Learn what [others say about Iris](https://iris-go.com/testimonials/) and **star
|
|||
|
||||
[![https://www.facebook.com/iris.framework/posts/3276606095684693](https://iris-go.com/images/iris-112-released.png)](https://www.facebook.com/iris.framework/posts/3276606095684693)
|
||||
|
||||
## It's your time to decide
|
||||
|
||||
The Go Team released the go modules[...](https://github.com/kataras/iris/issues/1370)
|
||||
|
||||
[![](https://api.gh-polls.com/poll/01DP9QJSMZK2FHP9K5CM712CQT/Keep%20the%20same%20import%20path)](https://api.gh-polls.com/poll/01DP9QJSMZK2FHP9K5CM712CQT/Keep%20the%20same%20import%20path/vote)
|
||||
[![](https://api.gh-polls.com/poll/01DP9QJSMZK2FHP9K5CM712CQT/Add%20version%20suffix)](https://api.gh-polls.com/poll/01DP9QJSMZK2FHP9K5CM712CQT/Add%20version%20suffix/vote)
|
||||
|
||||
> Read how [we support you](https://github.com/kataras/iris/wiki/Support).
|
||||
|
||||
[![Iris vs .NET Core(C#) vs Node.js (Express)](https://github.com/kataras/iris/raw/master/_benchmarks/benchmarks_graph_22_october_2018_gray.png)](https://github.com/kataras/iris/blob/master/_benchmarks/README.md)
|
||||
|
||||
<details>
|
||||
<summary>Third-party benchmark</summary>
|
||||
|
||||
[![](https://github.com/kataras/iris/raw/master/_benchmarks/benchmarks_third_party_source_snapshot_go_23_october_2018.png)](https://github.com/iris-contrib/third-party-benchmarks#full-table)
|
||||
|
||||
> Last updated at: 01 March of 2019. Click to the image to view all results. You can run this in your own hardware by following the [steps here](https://github.com/iris-contrib/third-party-benchmarks#usage).
|
||||
|
||||
</details>
|
||||
|
||||
## Learning Iris
|
||||
|
||||
<details>
|
||||
|
|
26
cache/cache_test.go
vendored
26
cache/cache_test.go
vendored
|
@ -1,6 +1,7 @@
|
|||
package cache_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
@ -12,7 +13,6 @@ import (
|
|||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/core/errors"
|
||||
|
||||
"github.com/gavv/httpexpect"
|
||||
"github.com/kataras/iris/httptest"
|
||||
|
@ -21,9 +21,17 @@ import (
|
|||
var (
|
||||
cacheDuration = 2 * time.Second
|
||||
expectedBodyStr = "Imagine it as a big message to achieve x20 response performance!"
|
||||
errTestFailed = errors.New("expected the main handler to be executed %d times instead of %d")
|
||||
)
|
||||
|
||||
type testError struct {
|
||||
expected int
|
||||
got uint32
|
||||
}
|
||||
|
||||
func (h *testError) Error() string {
|
||||
return fmt.Sprintf("expected the main handler to be executed %d times instead of %d", h.expected, h.got)
|
||||
}
|
||||
|
||||
func runTest(e *httpexpect.Expect, path string, counterPtr *uint32, expectedBodyStr string, nocache string) error {
|
||||
e.GET(path).Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr)
|
||||
time.Sleep(cacheDuration / 5) // lets wait for a while, cache should be saved and ready
|
||||
|
@ -31,7 +39,7 @@ func runTest(e *httpexpect.Expect, path string, counterPtr *uint32, expectedBody
|
|||
counter := atomic.LoadUint32(counterPtr)
|
||||
if counter > 1 {
|
||||
// n should be 1 because it doesn't changed after the first call
|
||||
return errTestFailed.Format(1, counter)
|
||||
return &testError{1, counter}
|
||||
}
|
||||
time.Sleep(cacheDuration)
|
||||
|
||||
|
@ -42,7 +50,7 @@ func runTest(e *httpexpect.Expect, path string, counterPtr *uint32, expectedBody
|
|||
e.GET(path).Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr)
|
||||
counter = atomic.LoadUint32(counterPtr)
|
||||
if counter != 2 {
|
||||
return errTestFailed.Format(2, counter)
|
||||
return &testError{2, counter}
|
||||
}
|
||||
|
||||
// we have cache response saved for the path, we have some time more here, but here
|
||||
|
@ -51,7 +59,7 @@ func runTest(e *httpexpect.Expect, path string, counterPtr *uint32, expectedBody
|
|||
e.GET(path).WithHeader("Authorization", "basic or anything").Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr)
|
||||
counter = atomic.LoadUint32(counterPtr)
|
||||
if counter != 4 {
|
||||
return errTestFailed.Format(4, counter)
|
||||
return &testError{4, counter}
|
||||
}
|
||||
|
||||
if nocache != "" {
|
||||
|
@ -69,14 +77,14 @@ func runTest(e *httpexpect.Expect, path string, counterPtr *uint32, expectedBody
|
|||
e.GET(nocache).Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr) // counter should be 6
|
||||
counter = atomic.LoadUint32(counterPtr)
|
||||
if counter != 6 { // 4 before, 5 with the first call to store the cache, and six with the no cache, again original handler executation
|
||||
return errTestFailed.Format(6, counter)
|
||||
return &testError{6, counter}
|
||||
}
|
||||
|
||||
// let's call again the path the expiration is not passed so it should be cached
|
||||
e.GET(path).Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr)
|
||||
counter = atomic.LoadUint32(counterPtr)
|
||||
if counter != 6 {
|
||||
return errTestFailed.Format(6, counter)
|
||||
return &testError{6, counter}
|
||||
}
|
||||
|
||||
// but now check for the No
|
||||
|
@ -194,7 +202,7 @@ func TestCacheValidator(t *testing.T) {
|
|||
counter := atomic.LoadUint32(&n)
|
||||
if counter > 1 {
|
||||
// n should be 1 because it doesn't changed after the first call
|
||||
t.Fatal(errTestFailed.Format(1, counter))
|
||||
t.Fatalf("%s: %v", t.Name(), &testError{1, counter})
|
||||
}
|
||||
// don't execute from cache, execute the original, counter should ++ here
|
||||
e.GET("/invalid").Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr) // counter = 2
|
||||
|
@ -203,6 +211,6 @@ func TestCacheValidator(t *testing.T) {
|
|||
counter = atomic.LoadUint32(&n)
|
||||
if counter != 3 {
|
||||
// n should be 1 because it doesn't changed after the first call
|
||||
t.Fatalf(t.Name()+": %v", errTestFailed.Format(3, counter))
|
||||
t.Fatalf("%s: %v", t.Name(), &testError{3, counter})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package iris
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
@ -16,7 +17,6 @@ import (
|
|||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/core/errors"
|
||||
)
|
||||
|
||||
const globalConfigurationKeyword = "~"
|
||||
|
@ -54,26 +54,24 @@ func homeDir() (home string) {
|
|||
return
|
||||
}
|
||||
|
||||
var errConfigurationDecode = errors.New("error while trying to decode configuration")
|
||||
|
||||
func parseYAML(filename string) (Configuration, error) {
|
||||
c := DefaultConfiguration()
|
||||
// get the abs
|
||||
// which will try to find the 'filename' from current workind dir too.
|
||||
yamlAbsPath, err := filepath.Abs(filename)
|
||||
if err != nil {
|
||||
return c, errConfigurationDecode.AppendErr(err)
|
||||
return c, fmt.Errorf("parse yaml: %w", err)
|
||||
}
|
||||
|
||||
// read the raw contents of the file
|
||||
data, err := ioutil.ReadFile(yamlAbsPath)
|
||||
if err != nil {
|
||||
return c, errConfigurationDecode.AppendErr(err)
|
||||
return c, fmt.Errorf("parse yaml: %w", err)
|
||||
}
|
||||
|
||||
// put the file's contents as yaml to the default configuration(c)
|
||||
if err := yaml.Unmarshal(data, &c); err != nil {
|
||||
return c, errConfigurationDecode.AppendErr(err)
|
||||
return c, fmt.Errorf("parse yaml: %w", err)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
@ -141,18 +139,18 @@ func TOML(filename string) Configuration {
|
|||
// which will try to find the 'filename' from current workind dir too.
|
||||
tomlAbsPath, err := filepath.Abs(filename)
|
||||
if err != nil {
|
||||
panic(errConfigurationDecode.AppendErr(err))
|
||||
panic(fmt.Errorf("toml: %w", err))
|
||||
}
|
||||
|
||||
// read the raw contents of the file
|
||||
data, err := ioutil.ReadFile(tomlAbsPath)
|
||||
if err != nil {
|
||||
panic(errConfigurationDecode.AppendErr(err))
|
||||
panic(fmt.Errorf("toml :%w", err))
|
||||
}
|
||||
|
||||
// put the file's contents as toml to the default configuration(c)
|
||||
if _, err := toml.Decode(string(data), &c); err != nil {
|
||||
panic(errConfigurationDecode.AppendErr(err))
|
||||
panic(fmt.Errorf("toml :%w", err))
|
||||
}
|
||||
// Author's notes:
|
||||
// The toml's 'usual thing' for key naming is: the_config_key instead of TheConfigKey
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -21,7 +22,6 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"github.com/kataras/iris/core/memstore"
|
||||
|
||||
"github.com/Shopify/goreferrer"
|
||||
|
@ -656,7 +656,7 @@ type Context interface {
|
|||
// like the HTTP Method is not "GET" or "HEAD" or if the "modtime" is zero
|
||||
// or if parsing time from the header failed.
|
||||
//
|
||||
// It's mostly used internally, e.g. `context#WriteWithExpiration`.
|
||||
// It's mostly used internally, e.g. `context#WriteWithExpiration`. See `ErrPreconditionFailed` too.
|
||||
//
|
||||
// Note that modtime.UTC() is being used instead of just modtime, so
|
||||
// you don't have to know the internals in order to make that works.
|
||||
|
@ -1916,7 +1916,18 @@ func (ctx *context) URLParamEscape(name string) string {
|
|||
return DecodeQuery(ctx.URLParam(name))
|
||||
}
|
||||
|
||||
var errURLParamNotFound = errors.New("url param '%s' does not exist")
|
||||
// ErrNotFound is the type error which API users can make use of
|
||||
// to check if a `Context` action of a `Handler` is type of Not Found,
|
||||
// e.g. URL Query Parameters.
|
||||
// Example:
|
||||
//
|
||||
// n, err := context.URLParamInt("url_query_param_name")
|
||||
// if errors.Is(err, context.ErrNotFound) {
|
||||
// // [handle error...]
|
||||
// }
|
||||
// Another usage would be `err == context.ErrNotFound`
|
||||
// HOWEVER prefer use the new `errors.Is` as API details may change in the future.
|
||||
var ErrNotFound = errors.New("not found")
|
||||
|
||||
// URLParamInt returns the url query parameter as int value from a request,
|
||||
// returns -1 and an error if parse failed or not found.
|
||||
|
@ -1929,7 +1940,7 @@ func (ctx *context) URLParamInt(name string) (int, error) {
|
|||
return n, nil
|
||||
}
|
||||
|
||||
return -1, errURLParamNotFound.Format(name)
|
||||
return -1, ErrNotFound
|
||||
}
|
||||
|
||||
// URLParamIntDefault returns the url query parameter as int value from a request,
|
||||
|
@ -1969,7 +1980,7 @@ func (ctx *context) URLParamInt64(name string) (int64, error) {
|
|||
return n, nil
|
||||
}
|
||||
|
||||
return -1, errURLParamNotFound.Format(name)
|
||||
return -1, ErrNotFound
|
||||
}
|
||||
|
||||
// URLParamInt64Default returns the url query parameter as int64 value from a request,
|
||||
|
@ -1994,7 +2005,7 @@ func (ctx *context) URLParamFloat64(name string) (float64, error) {
|
|||
return n, nil
|
||||
}
|
||||
|
||||
return -1, errURLParamNotFound.Format(name)
|
||||
return -1, ErrNotFound
|
||||
}
|
||||
|
||||
// URLParamFloat64Default returns the url query parameter as float64 value from a request,
|
||||
|
@ -2178,8 +2189,6 @@ func (ctx *context) PostValueTrim(name string) string {
|
|||
return strings.TrimSpace(ctx.PostValue(name))
|
||||
}
|
||||
|
||||
var errUnableToFindPostValue = errors.New("unable to find post value '%s'")
|
||||
|
||||
// PostValueInt returns the parsed form data from POST, PATCH,
|
||||
// or PUT body parameters based on a "name", as int.
|
||||
//
|
||||
|
@ -2187,7 +2196,7 @@ var errUnableToFindPostValue = errors.New("unable to find post value '%s'")
|
|||
func (ctx *context) PostValueInt(name string) (int, error) {
|
||||
v := ctx.PostValue(name)
|
||||
if v == "" {
|
||||
return -1, errUnableToFindPostValue.Format(name)
|
||||
return -1, ErrNotFound
|
||||
}
|
||||
return strconv.Atoi(v)
|
||||
}
|
||||
|
@ -2211,7 +2220,7 @@ func (ctx *context) PostValueIntDefault(name string, def int) int {
|
|||
func (ctx *context) PostValueInt64(name string) (int64, error) {
|
||||
v := ctx.PostValue(name)
|
||||
if v == "" {
|
||||
return -1, errUnableToFindPostValue.Format(name)
|
||||
return -1, ErrNotFound
|
||||
}
|
||||
return strconv.ParseInt(v, 10, 64)
|
||||
}
|
||||
|
@ -2235,7 +2244,7 @@ func (ctx *context) PostValueInt64Default(name string, def int64) int64 {
|
|||
func (ctx *context) PostValueFloat64(name string) (float64, error) {
|
||||
v := ctx.PostValue(name)
|
||||
if v == "" {
|
||||
return -1, errUnableToFindPostValue.Format(name)
|
||||
return -1, ErrNotFound
|
||||
}
|
||||
return strconv.ParseFloat(v, 64)
|
||||
}
|
||||
|
@ -2259,7 +2268,7 @@ func (ctx *context) PostValueFloat64Default(name string, def float64) float64 {
|
|||
func (ctx *context) PostValueBool(name string) (bool, error) {
|
||||
v := ctx.PostValue(name)
|
||||
if v == "" {
|
||||
return false, errUnableToFindPostValue.Format(name)
|
||||
return false, ErrNotFound
|
||||
}
|
||||
|
||||
return strconv.ParseBool(v)
|
||||
|
@ -2496,7 +2505,7 @@ func GetBody(r *http.Request, resetBody bool) ([]byte, error) {
|
|||
// However you are still free to read the `ctx.Request().Body io.Reader` manually.
|
||||
func (ctx *context) UnmarshalBody(outPtr interface{}, unmarshaler Unmarshaler) error {
|
||||
if ctx.request.Body == nil {
|
||||
return errors.New("unmarshal: empty body")
|
||||
return fmt.Errorf("unmarshal: empty body: %w", ErrNotFound)
|
||||
}
|
||||
|
||||
rawData, err := ctx.GetBody()
|
||||
|
@ -2696,6 +2705,19 @@ func (ctx *context) SetLastModified(modtime time.Time) {
|
|||
}
|
||||
}
|
||||
|
||||
// ErrPreconditionFailed may be returned from `Context` methods
|
||||
// that has to perform one or more client side preconditions before the actual check, e.g. `CheckIfModifiedSince`.
|
||||
// Usage:
|
||||
// ok, err := context.CheckIfModifiedSince(modTime)
|
||||
// if err != nil {
|
||||
// if errors.Is(err, context.ErrPreconditionFailed) {
|
||||
// [handle missing client conditions,such as not valid request method...]
|
||||
// }else {
|
||||
// [the error is probably a time parse error...]
|
||||
// }
|
||||
// }
|
||||
var ErrPreconditionFailed = errors.New("precondition failed")
|
||||
|
||||
// CheckIfModifiedSince checks if the response is modified since the "modtime".
|
||||
// Note that it has nothing to do with server-side caching.
|
||||
// It does those checks by checking if the "If-Modified-Since" request header
|
||||
|
@ -2707,20 +2729,20 @@ func (ctx *context) SetLastModified(modtime time.Time) {
|
|||
// it's not modified since, because it may return false but without even
|
||||
// had the chance to check the client-side (request) header due to some errors,
|
||||
// like the HTTP Method is not "GET" or "HEAD" or if the "modtime" is zero
|
||||
// or if parsing time from the header failed.
|
||||
// or if parsing time from the header failed. See `ErrPreconditionFailed` too.
|
||||
//
|
||||
// It's mostly used internally, e.g. `context#WriteWithExpiration`.
|
||||
func (ctx *context) CheckIfModifiedSince(modtime time.Time) (bool, error) {
|
||||
if method := ctx.Method(); method != http.MethodGet && method != http.MethodHead {
|
||||
return false, errors.New("skip: method")
|
||||
return false, fmt.Errorf("method: %w", ErrPreconditionFailed)
|
||||
}
|
||||
ims := ctx.GetHeader(IfModifiedSinceHeaderKey)
|
||||
if ims == "" || IsZeroTime(modtime) {
|
||||
return false, errors.New("skip: zero time")
|
||||
return false, fmt.Errorf("zero time: %w", ErrPreconditionFailed)
|
||||
}
|
||||
t, err := ParseTime(ctx, ims)
|
||||
if err != nil {
|
||||
return false, errors.New("skip: " + err.Error())
|
||||
return false, err
|
||||
}
|
||||
// sub-second precision, so
|
||||
// use mtime < t+1s instead of mtime <= t to check for unmodified.
|
||||
|
@ -2811,7 +2833,9 @@ func (ctx *context) ClientSupportsGzip() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
var errClientDoesNotSupportGzip = errors.New("client doesn't support gzip compression")
|
||||
// ErrGzipNotSupported may be returned from `WriteGzip` methods if
|
||||
// the client does not support the "gzip" compression.
|
||||
var ErrGzipNotSupported = errors.New("client does not support gzip compression")
|
||||
|
||||
// WriteGzip accepts bytes, which are compressed to gzip format and sent to the client.
|
||||
// returns the number of bytes written and an error ( if the client doesn't support gzip compression)
|
||||
|
@ -2820,7 +2844,7 @@ var errClientDoesNotSupportGzip = errors.New("client doesn't support gzip compre
|
|||
// to write more data many times without any troubles.
|
||||
func (ctx *context) WriteGzip(b []byte) (int, error) {
|
||||
if !ctx.ClientSupportsGzip() {
|
||||
return 0, errClientDoesNotSupportGzip
|
||||
return 0, ErrGzipNotSupported
|
||||
}
|
||||
|
||||
return ctx.GzipResponseWriter().Write(b)
|
||||
|
@ -2832,7 +2856,7 @@ func (ctx *context) TryWriteGzip(b []byte) (int, error) {
|
|||
n, err := ctx.WriteGzip(b)
|
||||
if err != nil {
|
||||
// check if the error came from gzip not allowed and not the writer itself
|
||||
if _, ok := err.(errors.Error); ok {
|
||||
if errors.Is(err, ErrGzipNotSupported) {
|
||||
// client didn't supported gzip, write them uncompressed:
|
||||
return ctx.writer.Write(b)
|
||||
}
|
||||
|
@ -4074,8 +4098,6 @@ func (n *NegotiationAcceptBuilder) EncodingGzip() *NegotiationAcceptBuilder {
|
|||
// | Serve files |
|
||||
// +------------------------------------------------------------+
|
||||
|
||||
var errServeContent = errors.New("while trying to serve content to the client. Trace %s")
|
||||
|
||||
// ServeContent serves content, headers are autoset
|
||||
// receives three parameters, it's low-level function, instead you can use .ServeFile(string,bool)/SendFile(string,string)
|
||||
//
|
||||
|
@ -4103,7 +4125,7 @@ func (ctx *context) ServeContent(content io.ReadSeeker, filename string, modtime
|
|||
out = ctx.writer
|
||||
}
|
||||
_, err := io.Copy(out, content)
|
||||
return errServeContent.With(err) ///TODO: add an int64 as return value for the content length written like other writers or let it as it's in order to keep the stable api?
|
||||
return err ///TODO: add an int64 as return value for the content length written like other writers or let it as it's in order to keep the stable api?
|
||||
}
|
||||
|
||||
// ServeFile serves a view file, to send a file ( zip for example) to the client you should use the SendFile(serverfilename,clientfilename)
|
||||
|
@ -4386,8 +4408,14 @@ func (ctx *context) IsRecording() (*ResponseRecorder, bool) {
|
|||
return rr, ok
|
||||
}
|
||||
|
||||
// non-detailed error log for transacton unexpected panic
|
||||
var errTransactionInterrupted = errors.New("transaction interrupted, recovery from panic:\n%s")
|
||||
// ErrPanicRecovery may be returned from `Context` actions of a `Handler`
|
||||
// which recovers from a manual panic.
|
||||
// var ErrPanicRecovery = errors.New("recovery from panic")
|
||||
|
||||
// ErrTransactionInterrupt can be used to manually force-complete a Context's transaction
|
||||
// and log(warn) the wrapped error's message.
|
||||
// Usage: `... return fmt.Errorf("my custom error message: %w", context.ErrTransactionInterrupt)`.
|
||||
var ErrTransactionInterrupt = errors.New("transaction interrupted")
|
||||
|
||||
// BeginTransaction starts a scoped transaction.
|
||||
//
|
||||
|
@ -4415,7 +4443,7 @@ func (ctx *context) BeginTransaction(pipe func(t *Transaction)) {
|
|||
t := newTransaction(ctx) // it calls this *context, so the overriding with a new pool's New of context.Context wil not work here.
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ctx.Application().Logger().Warn(errTransactionInterrupted.Format(err).Error())
|
||||
ctx.Application().Logger().Warn(fmt.Errorf("recovery from panic: %w", ErrTransactionInterrupt))
|
||||
// complete (again or not , doesn't matters) the scope without loud
|
||||
t.Complete(nil)
|
||||
// we continue as normal, no need to return here*
|
||||
|
|
|
@ -2,13 +2,12 @@ package context
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris/core/errors"
|
||||
)
|
||||
|
||||
// ResponseWriter interface is used by the context to serve an HTTP handler to
|
||||
|
|
356
core/errgroup/errgroup.go
Normal file
356
core/errgroup/errgroup.go
Normal file
|
@ -0,0 +1,356 @@
|
|||
package errgroup
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Check reports whether the "err" is not nil.
|
||||
// If it is a group then it returns true if that or its children contains any error.
|
||||
func Check(err error) error {
|
||||
if isNotNil(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Walk loops through each of the errors of "err".
|
||||
// If "err" is *Group then it fires the "visitor" for each of its errors, including children.
|
||||
// if "err" is *Error then it fires the "visitor" with its type and wrapped error.
|
||||
// Otherwise it fires the "visitor" once with typ of nil and err as "err".
|
||||
func Walk(err error, visitor func(typ interface{}, err error)) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if group, ok := err.(*Group); ok {
|
||||
list := group.getAllErrors()
|
||||
for _, entry := range list {
|
||||
if e, ok := entry.(*Error); ok {
|
||||
visitor(e.Type, e.Err) // e.Unwrap() <-no.
|
||||
} else {
|
||||
visitor(nil, err)
|
||||
}
|
||||
}
|
||||
} else if e, ok := err.(*Error); ok {
|
||||
visitor(e.Type, e.Err)
|
||||
} else {
|
||||
visitor(nil, err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
/*
|
||||
func Errors(err error, conv bool) []error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if group, ok := err.(*Group); ok {
|
||||
list := group.getAllErrors()
|
||||
if conv {
|
||||
for i, entry := range list {
|
||||
if _, ok := entry.(*Error); !ok {
|
||||
list[i] = &Error{Err: entry, Type: group.Type}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
func Type(err error) interface{} {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if e, ok := err.(*Error); ok && e.Err != nil {
|
||||
return e.Type
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Fill(parent *Group, errors []*Error) {
|
||||
for _, err := range errors {
|
||||
if err.Type == parent.Type {
|
||||
parent.Add(err)
|
||||
continue
|
||||
}
|
||||
|
||||
parent.Group(err.Type).Err(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
// Error implements the error interface.
|
||||
// It is a special error type which keep the "Type" of the
|
||||
// Group that it's created through Group's `Err` and `Errf` methods.
|
||||
type Error struct {
|
||||
Err error `json:"error" xml:"Error" yaml:"Error" toml:"Error" sql:"error"`
|
||||
Type interface{} `json:"type" xml:"Type" yaml:"Type" toml:"Type" sql:"type"`
|
||||
}
|
||||
|
||||
// Error returns the error message of the "Err".
|
||||
func (e *Error) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
// Unwrap calls and returns the result of the "Err" Unwrap method or nil.
|
||||
func (e *Error) Unwrap() error {
|
||||
return errors.Unwrap(e.Err)
|
||||
}
|
||||
|
||||
// Is reports whether the "err" is an *Error.
|
||||
func (e *Error) Is(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
ok := errors.Is(e.Err, err)
|
||||
if !ok {
|
||||
te, ok := err.(*Error)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return errors.Is(e.Err, te.Err)
|
||||
}
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// As reports whether the "target" can be used as &Error{target.Type: ?}.
|
||||
func (e *Error) As(target interface{}) bool {
|
||||
if target == nil {
|
||||
return target == e
|
||||
}
|
||||
|
||||
ok := errors.As(e.Err, target)
|
||||
if !ok {
|
||||
te, ok := target.(*Error)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if te.Type != nil {
|
||||
if te.Type != e.Type {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return errors.As(e.Err, &te.Err)
|
||||
}
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// Group is an error container of a specific Type and can have child containers per type too.
|
||||
type Group struct {
|
||||
parent *Group
|
||||
// a list of children groups, used to get or create new group through Group method.
|
||||
children map[interface{}]*Group
|
||||
depth int
|
||||
|
||||
Type interface{}
|
||||
Errors []error // []*Error
|
||||
|
||||
// if true then this Group's Error method will return the messages of the errors made by this Group's Group method.
|
||||
// Defaults to true.
|
||||
IncludeChildren bool // it clones.
|
||||
// IncludeTypeText bool
|
||||
index int // group index.
|
||||
}
|
||||
|
||||
// New returns a new empty Group.
|
||||
func New(typ interface{}) *Group {
|
||||
return &Group{
|
||||
Type: typ,
|
||||
IncludeChildren: true,
|
||||
}
|
||||
}
|
||||
|
||||
const delim = "\n"
|
||||
|
||||
func (g *Group) Error() (s string) {
|
||||
if len(g.Errors) > 0 {
|
||||
msgs := make([]string, len(g.Errors), len(g.Errors))
|
||||
for i, err := range g.Errors {
|
||||
msgs[i] = err.Error()
|
||||
}
|
||||
|
||||
s = strings.Join(msgs, delim)
|
||||
}
|
||||
|
||||
if g.IncludeChildren && len(g.children) > 0 {
|
||||
// return with order of definition.
|
||||
groups := g.getAllChildren()
|
||||
sortGroups(groups)
|
||||
|
||||
for _, ge := range groups {
|
||||
for _, childErr := range ge.Errors {
|
||||
s += childErr.Error() + delim
|
||||
}
|
||||
}
|
||||
|
||||
if s != "" {
|
||||
return s[:len(s)-1]
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Group) getAllErrors() []error {
|
||||
list := g.Errors[:]
|
||||
|
||||
if len(g.children) > 0 {
|
||||
// return with order of definition.
|
||||
groups := g.getAllChildren()
|
||||
sortGroups(groups)
|
||||
|
||||
for _, ge := range groups {
|
||||
list = append(list, ge.Errors...)
|
||||
}
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
func (g *Group) getAllChildren() []*Group {
|
||||
if len(g.children) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var groups []*Group
|
||||
for _, child := range g.children {
|
||||
groups = append(groups, append([]*Group{child}, child.getAllChildren()...)...)
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
|
||||
// Unwrap implements the dynamic std errors interface and it returns the parent Group.
|
||||
func (g *Group) Unwrap() error {
|
||||
return g.parent
|
||||
}
|
||||
|
||||
// Group creates a new group of "typ" type, if does not exist, and returns it.
|
||||
func (g *Group) Group(typ interface{}) *Group {
|
||||
if g.children == nil {
|
||||
g.children = make(map[interface{}]*Group)
|
||||
} else {
|
||||
for _, child := range g.children {
|
||||
if child.Type == typ {
|
||||
return child
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
child := &Group{
|
||||
Type: typ,
|
||||
parent: g,
|
||||
depth: g.depth + 1,
|
||||
IncludeChildren: g.IncludeChildren,
|
||||
index: g.index + 1 + len(g.children),
|
||||
}
|
||||
|
||||
g.children[typ] = child
|
||||
|
||||
return child
|
||||
}
|
||||
|
||||
// Add adds an error to the group.
|
||||
func (g *Group) Add(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
g.Errors = append(g.Errors, err)
|
||||
}
|
||||
|
||||
// Addf adds an error to the group like `fmt.Errorf` and returns it.
|
||||
func (g *Group) Addf(format string, args ...interface{}) error {
|
||||
err := fmt.Errorf(format, args...)
|
||||
g.Add(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Err adds an error the the group, it transforms it to an Error type if necessary and returns it.
|
||||
func (g *Group) Err(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
e, ok := err.(*Error)
|
||||
if !ok {
|
||||
if ge, ok := err.(*Group); ok {
|
||||
if g.children == nil {
|
||||
g.children = make(map[interface{}]*Group)
|
||||
}
|
||||
|
||||
g.children[ge.Type] = ge
|
||||
return ge
|
||||
}
|
||||
|
||||
e = &Error{err, 0}
|
||||
}
|
||||
e.Type = g.Type
|
||||
|
||||
g.Add(e)
|
||||
return e
|
||||
}
|
||||
|
||||
// Errf adds an error like `fmt.Errorf` and returns it.
|
||||
func (g *Group) Errf(format string, args ...interface{}) error {
|
||||
return g.Err(fmt.Errorf(format, args...))
|
||||
}
|
||||
|
||||
func sortGroups(groups []*Group) {
|
||||
sort.Slice(groups, func(i, j int) bool {
|
||||
return groups[i].index < groups[j].index
|
||||
})
|
||||
}
|
||||
|
||||
func tryGetTypeText(typ interface{}) string {
|
||||
if typ == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
switch v := typ.(type) {
|
||||
case string:
|
||||
return v
|
||||
case fmt.Stringer:
|
||||
return v.String()
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func isNotNil(err error) bool {
|
||||
if g, ok := err.(*Group); ok {
|
||||
if len(g.Errors) > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(g.children) > 0 {
|
||||
for _, child := range g.children {
|
||||
if isNotNil(child) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return err != nil
|
||||
}
|
173
core/errgroup/errgroup_test.go
Normal file
173
core/errgroup/errgroup_test.go
Normal file
|
@ -0,0 +1,173 @@
|
|||
package errgroup
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestErrorError(t *testing.T) {
|
||||
testErr := errors.New("error")
|
||||
err := &Error{Err: testErr}
|
||||
if expected, got := testErr.Error(), err.Error(); expected != got {
|
||||
t.Fatalf("expected %s but got %s", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorUnwrap(t *testing.T) {
|
||||
wrapped := errors.New("unwrap")
|
||||
|
||||
err := &Error{Err: fmt.Errorf("this wraps:%w", wrapped)}
|
||||
if expected, got := wrapped, errors.Unwrap(err); expected != got {
|
||||
t.Fatalf("expected %#+v but got %#+v", expected, got)
|
||||
}
|
||||
|
||||
}
|
||||
func TestErrorIs(t *testing.T) {
|
||||
testErr := errors.New("is")
|
||||
err := &Error{Err: fmt.Errorf("this is: %w", testErr)}
|
||||
if expected, got := true, errors.Is(err, testErr); expected != got {
|
||||
t.Fatalf("expected %v but got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorAs(t *testing.T) {
|
||||
testErr := errors.New("as")
|
||||
err := &Error{Err: testErr}
|
||||
if expected, got := true, errors.As(err, &testErr); expected != got {
|
||||
t.Fatalf("[testErr as err] expected %v but got %v", expected, got)
|
||||
}
|
||||
if expected, got := true, errors.As(testErr, &err); expected != got {
|
||||
t.Fatalf("[err as testErr] expected %v but got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupError(t *testing.T) {
|
||||
g := New(0)
|
||||
tests := []string{"error 1", "error 2", "error 3"}
|
||||
for _, tt := range tests {
|
||||
g.Add(errors.New(tt))
|
||||
}
|
||||
|
||||
if expected, got := strings.Join(tests, "\n"), g.Error(); expected != got {
|
||||
t.Fatalf("expected '%s' but got '%s'", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroup(t *testing.T) {
|
||||
const (
|
||||
apiErrorsType = iota + 1
|
||||
childAPIErrorsType
|
||||
childAPIErrors2Type = "string type 1"
|
||||
childAPIErrors2Type1 = "string type 2"
|
||||
|
||||
apiErrorsText = "apiErrors error 1"
|
||||
childAPIErrorsText = "apiErrors:child error 1"
|
||||
childAPIErrors2Text = "apiErrors:child2 error 1"
|
||||
childAPIErrors2Text1 = "apiErrors:child2_1 error 1"
|
||||
)
|
||||
|
||||
g := New(nil)
|
||||
apiErrorsGroup := g.Group(apiErrorsType)
|
||||
apiErrorsGroup.Errf(apiErrorsText)
|
||||
|
||||
childAPIErrorsGroup := apiErrorsGroup.Group(childAPIErrorsType)
|
||||
childAPIErrorsGroup.Addf(childAPIErrorsText)
|
||||
childAPIErrorsGroup2 := apiErrorsGroup.Group(childAPIErrors2Type)
|
||||
childAPIErrorsGroup2.Addf(childAPIErrors2Text)
|
||||
childAPIErrorsGroup2Group1 := childAPIErrorsGroup2.Group(childAPIErrors2Type1)
|
||||
childAPIErrorsGroup2Group1.Addf(childAPIErrors2Text1)
|
||||
|
||||
if apiErrorsGroup.Type != apiErrorsType {
|
||||
t.Fatal("invalid type")
|
||||
}
|
||||
|
||||
if childAPIErrorsGroup.Type != childAPIErrorsType {
|
||||
t.Fatal("invalid type")
|
||||
}
|
||||
|
||||
if childAPIErrorsGroup2.Type != childAPIErrors2Type {
|
||||
t.Fatal("invalid type")
|
||||
}
|
||||
|
||||
if childAPIErrorsGroup2Group1.Type != childAPIErrors2Type1 {
|
||||
t.Fatal("invalid type")
|
||||
}
|
||||
|
||||
if expected, got := 2, len(apiErrorsGroup.children); expected != got {
|
||||
t.Fatalf("expected %d but got %d", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := 0, len(childAPIErrorsGroup.children); expected != got {
|
||||
t.Fatalf("expected %d but got %d", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := 1, len(childAPIErrorsGroup2.children); expected != got {
|
||||
t.Fatalf("expected %d but got %d", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := 0, len(childAPIErrorsGroup2Group1.children); expected != got {
|
||||
t.Fatalf("expected %d but got %d", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := 1, apiErrorsGroup.index; expected != got {
|
||||
t.Fatalf("expected index %d but got %d", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := 2, childAPIErrorsGroup.index; expected != got {
|
||||
t.Fatalf("expected index %d but got %d", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := 3, childAPIErrorsGroup2.index; expected != got {
|
||||
t.Fatalf("expected index %d but got %d", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := 4, childAPIErrorsGroup2Group1.index; expected != got {
|
||||
t.Fatalf("expected index %d but got %d", expected, got)
|
||||
}
|
||||
|
||||
t.Run("Error", func(t *testing.T) {
|
||||
if expected, got :=
|
||||
strings.Join([]string{apiErrorsText, childAPIErrorsText, childAPIErrors2Text, childAPIErrors2Text1}, delim), g.Error(); expected != got {
|
||||
t.Fatalf("expected '%s' but got '%s'", expected, got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Walk", func(t *testing.T) {
|
||||
expectedEntries := 4
|
||||
Walk(g, func(typ interface{}, err error) {
|
||||
g.IncludeChildren = false
|
||||
childAPIErrorsGroup.IncludeChildren = false
|
||||
childAPIErrorsGroup2.IncludeChildren = false
|
||||
childAPIErrorsGroup2Group1.IncludeChildren = false
|
||||
|
||||
expectedEntries--
|
||||
var expected string
|
||||
|
||||
switch typ {
|
||||
case apiErrorsType:
|
||||
expected = apiErrorsText
|
||||
case childAPIErrorsType:
|
||||
expected = childAPIErrorsText
|
||||
case childAPIErrors2Type:
|
||||
expected = childAPIErrors2Text
|
||||
case childAPIErrors2Type1:
|
||||
expected = childAPIErrors2Text1
|
||||
}
|
||||
|
||||
if got := err.Error(); expected != got {
|
||||
t.Fatalf("[%v] expected '%s' but got '%s'", typ, expected, got)
|
||||
}
|
||||
})
|
||||
|
||||
if expectedEntries != 0 {
|
||||
t.Fatalf("not valid number of errors [...%d]", expectedEntries)
|
||||
}
|
||||
|
||||
g.IncludeChildren = true
|
||||
childAPIErrorsGroup.IncludeChildren = true
|
||||
childAPIErrorsGroup2.IncludeChildren = true
|
||||
childAPIErrorsGroup2Group1.IncludeChildren = true
|
||||
})
|
||||
}
|
|
@ -1,166 +0,0 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/iris-contrib/go.uuid"
|
||||
)
|
||||
|
||||
// Prefix the error prefix, applies to each error's message.
|
||||
var Prefix = ""
|
||||
|
||||
// Error holds the error message, this message never really changes
|
||||
type Error struct {
|
||||
// ID returns the unique id of the error, it's needed
|
||||
// when we want to check if a specific error returned
|
||||
// but the `Error() string` value is not the same because the error may be dynamic
|
||||
// by a `Format` call.
|
||||
ID string `json:"id"`
|
||||
// The message of the error.
|
||||
Message string `json:"message"`
|
||||
// Apennded is true whenever it's a child error.
|
||||
Appended bool `json:"appended"`
|
||||
// Stack returns the list of the errors that are shown at `Error() string`.
|
||||
Stack []Error `json:"stack"` // filled on AppendX.
|
||||
}
|
||||
|
||||
// New creates and returns an Error with a pre-defined user output message
|
||||
// all methods below that doesn't accept a pointer receiver because actually they are not changing the original message
|
||||
func New(errMsg string) Error {
|
||||
uidv4, _ := uuid.NewV4() // skip error.
|
||||
return Error{
|
||||
ID: uidv4.String(),
|
||||
Message: Prefix + errMsg,
|
||||
}
|
||||
}
|
||||
|
||||
// NewFromErr same as `New` but pointer for nil checks without the need of the `Return()` function.
|
||||
func NewFromErr(err error) *Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errp := New(err.Error())
|
||||
return &errp
|
||||
}
|
||||
|
||||
// Equal returns true if "e" and "to" are matched, by their IDs if it's a core/errors type otherwise it tries to match their error messages.
|
||||
// It will always returns true if the "to" is a children of "e"
|
||||
// or the error messages are exactly the same, otherwise false.
|
||||
func (e Error) Equal(to error) bool {
|
||||
if e2, ok := to.(Error); ok {
|
||||
return e.ID == e2.ID
|
||||
} else if e2, ok := to.(*Error); ok {
|
||||
return e.ID == e2.ID
|
||||
}
|
||||
|
||||
return e.Error() == to.Error()
|
||||
}
|
||||
|
||||
// Empty returns true if the "e" Error has no message on its stack.
|
||||
func (e Error) Empty() bool {
|
||||
return e.Message == ""
|
||||
}
|
||||
|
||||
// NotEmpty returns true if the "e" Error has got a non-empty message on its stack.
|
||||
func (e Error) NotEmpty() bool {
|
||||
return !e.Empty()
|
||||
}
|
||||
|
||||
// String returns the error message
|
||||
func (e Error) String() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
// Error returns the message of the actual error
|
||||
// implements the error
|
||||
func (e Error) Error() string {
|
||||
return e.String()
|
||||
}
|
||||
|
||||
// Format returns a formatted new error based on the arguments
|
||||
// it does NOT change the original error's message
|
||||
func (e Error) Format(a ...interface{}) Error {
|
||||
e.Message = fmt.Sprintf(e.Message, a...)
|
||||
return e
|
||||
}
|
||||
|
||||
func omitNewLine(message string) string {
|
||||
if strings.HasSuffix(message, "\n") {
|
||||
return message[0 : len(message)-2]
|
||||
} else if strings.HasSuffix(message, "\\n") {
|
||||
return message[0 : len(message)-3]
|
||||
}
|
||||
return message
|
||||
}
|
||||
|
||||
// AppendInline appends an error to the stack.
|
||||
// It doesn't try to append a new line if needed.
|
||||
func (e Error) AppendInline(format string, a ...interface{}) Error {
|
||||
msg := fmt.Sprintf(format, a...)
|
||||
e.Message += msg
|
||||
e.Appended = true
|
||||
e.Stack = append(e.Stack, New(omitNewLine(msg)))
|
||||
return e
|
||||
}
|
||||
|
||||
// Append adds a message to the predefined error message and returns a new error
|
||||
// it does NOT change the original error's message
|
||||
func (e Error) Append(format string, a ...interface{}) Error {
|
||||
// if new line is false then append this error but first
|
||||
// we need to add a new line to the first, if it was true then it has the newline already.
|
||||
if e.Message != "" {
|
||||
e.Message += "\n"
|
||||
}
|
||||
|
||||
return e.AppendInline(format, a...)
|
||||
}
|
||||
|
||||
// AppendErr adds an error's message to the predefined error message and returns a new error.
|
||||
// it does NOT change the original error's message
|
||||
func (e Error) AppendErr(err error) Error {
|
||||
return e.Append(err.Error())
|
||||
}
|
||||
|
||||
// HasStack returns true if the Error instance is created using Append/AppendInline/AppendErr funcs.
|
||||
func (e Error) HasStack() bool {
|
||||
return len(e.Stack) > 0
|
||||
}
|
||||
|
||||
// With does the same thing as Format but it receives an error type which if it's nil it returns a nil error.
|
||||
func (e Error) With(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return e.Format(err.Error())
|
||||
}
|
||||
|
||||
// Ignore will ignore the "err" and return nil.
|
||||
func (e Error) Ignore(err error) error {
|
||||
if err == nil {
|
||||
return e
|
||||
}
|
||||
if e.Error() == err.Error() {
|
||||
return nil
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// Panic output the message and after panics.
|
||||
func (e Error) Panic() {
|
||||
_, fn, line, _ := runtime.Caller(1)
|
||||
errMsg := e.Message
|
||||
errMsg += "\nCaller was: " + fmt.Sprintf("%s:%d", fn, line)
|
||||
panic(errMsg)
|
||||
}
|
||||
|
||||
// Panicf output the formatted message and after panics.
|
||||
func (e Error) Panicf(args ...interface{}) {
|
||||
_, fn, line, _ := runtime.Caller(1)
|
||||
errMsg := e.Format(args...).Error()
|
||||
errMsg += "\nCaller was: " + fmt.Sprintf("%s:%d", fn, line)
|
||||
panic(errMsg)
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
// black-box testing
|
||||
package errors_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/core/errors"
|
||||
)
|
||||
|
||||
var errMessage = "User with mail: %s already exists"
|
||||
var errUserAlreadyExists = errors.New(errMessage)
|
||||
var userMail = "user1@mail.go"
|
||||
var expectedUserAlreadyExists = "User with mail: user1@mail.go already exists"
|
||||
|
||||
func ExampleError() {
|
||||
fmt.Print(errUserAlreadyExists.Format(userMail).Append("Please change your mail addr"))
|
||||
|
||||
// Output:
|
||||
// User with mail: user1@mail.go already exists
|
||||
// Please change your mail addr
|
||||
}
|
||||
|
||||
func do(method string, testErr errors.Error, expectingMsg string, t *testing.T) {
|
||||
formattedErr := func() error {
|
||||
return testErr.Format(userMail)
|
||||
}()
|
||||
|
||||
if formattedErr.Error() != expectingMsg {
|
||||
t.Fatalf("error %s failed, expected:\n%s got:\n%s", method, expectingMsg, formattedErr.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormat(t *testing.T) {
|
||||
expected := errors.Prefix + expectedUserAlreadyExists
|
||||
do("Format Test", errUserAlreadyExists, expected, t)
|
||||
}
|
||||
|
||||
func TestAppendErr(t *testing.T) {
|
||||
errChangeMailMsg := "Please change your mail addr"
|
||||
errChangeMail := fmt.Errorf(errChangeMailMsg) // test go standard error
|
||||
errAppended := errUserAlreadyExists.AppendErr(errChangeMail)
|
||||
expectedErrorMessage := errUserAlreadyExists.Format(userMail).Error() + "\n" + errChangeMailMsg
|
||||
|
||||
do("Append Test Standard error type", errAppended, expectedErrorMessage, t)
|
||||
}
|
||||
|
||||
func TestAppendError(t *testing.T) {
|
||||
errors.Prefix = "error: "
|
||||
|
||||
errChangeMailMsg := "Please change your mail addr"
|
||||
errChangeMail := errors.New(errChangeMailMsg)
|
||||
|
||||
errAppended := errUserAlreadyExists.AppendErr(errChangeMail)
|
||||
expectedErrorMessage := errUserAlreadyExists.Format(userMail).Error() + "\n" + errChangeMail.Error()
|
||||
|
||||
do("Append Test Error type", errAppended, expectedErrorMessage, t)
|
||||
}
|
||||
|
||||
func TestAppend(t *testing.T) {
|
||||
errors.Prefix = "error: "
|
||||
|
||||
errChangeMailMsg := "Please change your mail addr"
|
||||
expectedErrorMessage := errUserAlreadyExists.Format(userMail).Error() + "\n" + errChangeMailMsg
|
||||
errAppended := errUserAlreadyExists.Append(errChangeMailMsg)
|
||||
do("Append Test string Message", errAppended, expectedErrorMessage, t)
|
||||
}
|
||||
|
||||
func TestNewLine(t *testing.T) {
|
||||
err := errors.New(errMessage)
|
||||
expected := errors.Prefix + expectedUserAlreadyExists
|
||||
do("NewLine Test", err, expected, t)
|
||||
}
|
||||
|
||||
func TestPrefix(t *testing.T) {
|
||||
errors.Prefix = "MyPrefix: "
|
||||
|
||||
errUpdatedPrefix := errors.New(errMessage)
|
||||
expected := errors.Prefix + expectedUserAlreadyExists
|
||||
do("Prefix Test with "+errors.Prefix, errUpdatedPrefix, expected, t)
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// StackError contains the Stack method.
|
||||
type StackError interface {
|
||||
Stack() []Error
|
||||
Error() string
|
||||
}
|
||||
|
||||
// PrintAndReturnErrors prints the "err" to the given "printer",
|
||||
// printer will be called multiple times if the "err" is a StackError, where it contains more than one error.
|
||||
func PrintAndReturnErrors(err error, printer func(string, ...interface{})) error {
|
||||
if err == nil || err.Error() == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if stackErr, ok := err.(StackError); ok {
|
||||
if len(stackErr.Stack()) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
stack := stackErr.Stack()
|
||||
|
||||
for _, e := range stack {
|
||||
if e.HasStack() {
|
||||
for _, es := range e.Stack {
|
||||
printer("%v", es)
|
||||
}
|
||||
continue
|
||||
}
|
||||
printer("%v", e)
|
||||
}
|
||||
|
||||
return stackErr
|
||||
}
|
||||
|
||||
printer("%v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Reporter is a helper structure which can
|
||||
// stack errors and prints them to a printer of func(string).
|
||||
type Reporter struct {
|
||||
mu sync.Mutex
|
||||
wrapper Error
|
||||
}
|
||||
|
||||
// NewReporter returns a new empty error reporter.
|
||||
func NewReporter() *Reporter {
|
||||
return &Reporter{wrapper: New("")}
|
||||
}
|
||||
|
||||
// AddErr adds an error to the error stack.
|
||||
// if "err" is a StackError then
|
||||
// each of these errors will be printed as individual.
|
||||
//
|
||||
// Returns true if this "err" is not nil and it's added to the reporter's stack.
|
||||
func (r *Reporter) AddErr(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if stackErr, ok := err.(StackError); ok {
|
||||
r.addStack("", stackErr.Stack())
|
||||
} else {
|
||||
r.mu.Lock()
|
||||
r.wrapper = r.wrapper.AppendErr(err)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Add adds a formatted message as an error to the error stack.
|
||||
//
|
||||
// Returns true if this "err" is not nil and it's added to the reporter's stack.
|
||||
func (r *Reporter) Add(format string, a ...interface{}) bool {
|
||||
if format == "" && len(a) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// usually used as: "module: %v", err so
|
||||
// check if the first argument is error and if that error is empty then don't add it.
|
||||
if len(a) > 0 {
|
||||
f := a[0]
|
||||
if e, ok := f.(interface {
|
||||
Error() string
|
||||
}); ok {
|
||||
if e.Error() == "" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.mu.Lock()
|
||||
r.wrapper = r.wrapper.Append(format, a...)
|
||||
r.mu.Unlock()
|
||||
return true
|
||||
}
|
||||
|
||||
// Describe same as `Add` but if "err" is nil then it does nothing.
|
||||
func (r *Reporter) Describe(format string, err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if stackErr, ok := err.(StackError); ok {
|
||||
r.addStack(format, stackErr.Stack())
|
||||
return
|
||||
}
|
||||
|
||||
r.Add(format, err)
|
||||
}
|
||||
|
||||
// PrintStack prints all the errors to the given "printer".
|
||||
// Returns itself in order to be used as printer and return the full error in the same time.
|
||||
func (r *Reporter) PrintStack(printer func(string, ...interface{})) error {
|
||||
return PrintAndReturnErrors(r, printer)
|
||||
}
|
||||
|
||||
// Stack returns the list of the errors in the stack.
|
||||
func (r *Reporter) Stack() []Error {
|
||||
return r.wrapper.Stack
|
||||
}
|
||||
|
||||
func (r *Reporter) addStack(format string, stack []Error) {
|
||||
for _, e := range stack {
|
||||
if e.Error() == "" {
|
||||
continue
|
||||
}
|
||||
r.mu.Lock()
|
||||
if format != "" {
|
||||
e = New(format).Format(e)
|
||||
}
|
||||
r.wrapper = r.wrapper.AppendErr(e)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Error implements the error, returns the full error string.
|
||||
func (r *Reporter) Error() string {
|
||||
return r.wrapper.Error()
|
||||
}
|
||||
|
||||
// Return returns nil if the error is empty, otherwise returns the full error.
|
||||
func (r *Reporter) Return() error {
|
||||
if r.Error() == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
// black-box testing
|
||||
package errors_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/core/errors"
|
||||
)
|
||||
|
||||
func TestReporterAdd(t *testing.T) {
|
||||
errors.Prefix = ""
|
||||
|
||||
r := errors.NewReporter()
|
||||
|
||||
tests := []string{"err1", "err3", "err4\nerr5"}
|
||||
for _, tt := range tests {
|
||||
r.Add(tt)
|
||||
}
|
||||
|
||||
for i, e := range r.Stack() {
|
||||
tt := tests[i]
|
||||
if expected, got := tt, e.Error(); expected != got {
|
||||
t.Fatalf("[%d] expected %s but got %s", i, expected, got)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,12 @@
|
|||
package handlerconv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/core/errors"
|
||||
)
|
||||
|
||||
var errHandler = errors.New(`
|
||||
Passed argument is not a func(context.Context) neither one of these types:
|
||||
- http.Handler
|
||||
- func(w http.ResponseWriter, r *http.Request)
|
||||
- func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)
|
||||
---------------------------------------------------------------------
|
||||
It seems to be a %T points to: %v`)
|
||||
|
||||
// FromStd converts native http.Handler & http.HandlerFunc to context.Handler.
|
||||
//
|
||||
// Supported form types:
|
||||
|
@ -63,7 +55,13 @@ func FromStd(handler interface{}) context.Handler {
|
|||
//
|
||||
// No valid handler passed
|
||||
//
|
||||
panic(errHandler.Format(handler, handler))
|
||||
panic(fmt.Errorf(`
|
||||
Passed argument is not a func(context.Context) neither one of these types:
|
||||
- http.Handler
|
||||
- func(w http.ResponseWriter, r *http.Request)
|
||||
- func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)
|
||||
---------------------------------------------------------------------
|
||||
It seems to be a %T points to: %v`, handler, handler))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package host
|
|||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
@ -12,7 +13,6 @@ import (
|
|||
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"github.com/kataras/iris/core/netutil"
|
||||
)
|
||||
|
||||
|
@ -260,7 +260,7 @@ func (su *Supervisor) ListenAndServeTLS(certFile string, keyFile string) error {
|
|||
}
|
||||
|
||||
if su.Server.TLSConfig == nil {
|
||||
return errors.New("certFile or keyFile missing")
|
||||
return errors.New("empty certFile or keyFile and Server.TLSConfig")
|
||||
}
|
||||
|
||||
return su.supervise(func() error { return su.Server.ListenAndServeTLS("", "") })
|
||||
|
|
|
@ -11,8 +11,6 @@ import (
|
|||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/core/errors"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -102,14 +100,60 @@ func (e Entry) StringTrim() string {
|
|||
return strings.TrimSpace(e.String())
|
||||
}
|
||||
|
||||
var errFindParse = errors.New("unable to find the %s with key: %s")
|
||||
// ErrEntryNotFound may be returned from memstore methods if
|
||||
// a key (with a certain kind) was not found.
|
||||
// Usage:
|
||||
//
|
||||
// var e *ErrEntryNotFound
|
||||
// errors.As(err, &e)
|
||||
// To check for specific key error:
|
||||
// errors.As(err, &ErrEntryNotFound{Key: "key"})
|
||||
// To check for specific key-kind error:
|
||||
// errors.As(err, &ErrEntryNotFound{Key: "key", Kind: reflect.Int})
|
||||
type ErrEntryNotFound struct {
|
||||
Key string // the entry's key.
|
||||
Kind reflect.Kind // i.e bool, int, string...
|
||||
}
|
||||
|
||||
func (e *ErrEntryNotFound) Error() string {
|
||||
return fmt.Sprintf("not found: %s as %s", e.Key, e.Kind.String())
|
||||
}
|
||||
|
||||
// As can be used to manually check if the error came from the memstore
|
||||
// is a not found entry, the key must much in order to return true.
|
||||
// Usage:
|
||||
// errors.As(err, &ErrEntryNotFound{Key: "key", Kind: reflect.Int})
|
||||
//
|
||||
// Do NOT use this method directly, prefer` errors.As` method as explained above.
|
||||
//
|
||||
// Implements: go/src/errors/wrap.go#84
|
||||
func (e *ErrEntryNotFound) As(target interface{}) bool {
|
||||
v, ok := target.(*ErrEntryNotFound)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.Key != "" && v.Key != e.Key {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.Kind != reflect.Invalid && v.Kind != e.Kind {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (e Entry) notFound(kind reflect.Kind) *ErrEntryNotFound {
|
||||
return &ErrEntryNotFound{Key: e.Key, Kind: kind}
|
||||
}
|
||||
|
||||
// IntDefault returns the entry's value as int.
|
||||
// If not found returns "def" and a non-nil error.
|
||||
func (e Entry) IntDefault(def int) (int, error) {
|
||||
v := e.ValueRaw
|
||||
if v == nil {
|
||||
return def, errFindParse.Format("int", e.Key)
|
||||
return def, e.notFound(reflect.Int)
|
||||
}
|
||||
|
||||
switch vv := v.(type) {
|
||||
|
@ -141,7 +185,7 @@ func (e Entry) IntDefault(def int) (int, error) {
|
|||
return int(vv), nil
|
||||
}
|
||||
|
||||
return def, errFindParse.Format("int", e.Key)
|
||||
return def, e.notFound(reflect.Int)
|
||||
}
|
||||
|
||||
// Int8Default returns the entry's value as int8.
|
||||
|
@ -149,7 +193,7 @@ func (e Entry) IntDefault(def int) (int, error) {
|
|||
func (e Entry) Int8Default(def int8) (int8, error) {
|
||||
v := e.ValueRaw
|
||||
if v == nil {
|
||||
return def, errFindParse.Format("int8", e.Key)
|
||||
return def, e.notFound(reflect.Int8)
|
||||
}
|
||||
|
||||
switch vv := v.(type) {
|
||||
|
@ -171,7 +215,7 @@ func (e Entry) Int8Default(def int8) (int8, error) {
|
|||
return int8(vv), nil
|
||||
}
|
||||
|
||||
return def, errFindParse.Format("int8", e.Key)
|
||||
return def, e.notFound(reflect.Int8)
|
||||
}
|
||||
|
||||
// Int16Default returns the entry's value as int16.
|
||||
|
@ -179,7 +223,7 @@ func (e Entry) Int8Default(def int8) (int8, error) {
|
|||
func (e Entry) Int16Default(def int16) (int16, error) {
|
||||
v := e.ValueRaw
|
||||
if v == nil {
|
||||
return def, errFindParse.Format("int16", e.Key)
|
||||
return def, e.notFound(reflect.Int16)
|
||||
}
|
||||
|
||||
switch vv := v.(type) {
|
||||
|
@ -201,7 +245,7 @@ func (e Entry) Int16Default(def int16) (int16, error) {
|
|||
return int16(vv), nil
|
||||
}
|
||||
|
||||
return def, errFindParse.Format("int16", e.Key)
|
||||
return def, e.notFound(reflect.Int16)
|
||||
}
|
||||
|
||||
// Int32Default returns the entry's value as int32.
|
||||
|
@ -209,7 +253,7 @@ func (e Entry) Int16Default(def int16) (int16, error) {
|
|||
func (e Entry) Int32Default(def int32) (int32, error) {
|
||||
v := e.ValueRaw
|
||||
if v == nil {
|
||||
return def, errFindParse.Format("int32", e.Key)
|
||||
return def, e.notFound(reflect.Int32)
|
||||
}
|
||||
|
||||
switch vv := v.(type) {
|
||||
|
@ -231,7 +275,7 @@ func (e Entry) Int32Default(def int32) (int32, error) {
|
|||
return int32(vv), nil
|
||||
}
|
||||
|
||||
return def, errFindParse.Format("int32", e.Key)
|
||||
return def, e.notFound(reflect.Int32)
|
||||
}
|
||||
|
||||
// Int64Default returns the entry's value as int64.
|
||||
|
@ -239,7 +283,7 @@ func (e Entry) Int32Default(def int32) (int32, error) {
|
|||
func (e Entry) Int64Default(def int64) (int64, error) {
|
||||
v := e.ValueRaw
|
||||
if v == nil {
|
||||
return def, errFindParse.Format("int64", e.Key)
|
||||
return def, e.notFound(reflect.Int64)
|
||||
}
|
||||
|
||||
switch vv := v.(type) {
|
||||
|
@ -255,7 +299,7 @@ func (e Entry) Int64Default(def int64) (int64, error) {
|
|||
return int64(vv), nil
|
||||
}
|
||||
|
||||
return def, errFindParse.Format("int64", e.Key)
|
||||
return def, e.notFound(reflect.Int64)
|
||||
}
|
||||
|
||||
// UintDefault returns the entry's value as uint.
|
||||
|
@ -263,7 +307,7 @@ func (e Entry) Int64Default(def int64) (int64, error) {
|
|||
func (e Entry) UintDefault(def uint) (uint, error) {
|
||||
v := e.ValueRaw
|
||||
if v == nil {
|
||||
return def, errFindParse.Format("uint", e.Key)
|
||||
return def, e.notFound(reflect.Uint)
|
||||
}
|
||||
|
||||
x64 := strconv.IntSize == 64
|
||||
|
@ -279,7 +323,7 @@ func (e Entry) UintDefault(def uint) (uint, error) {
|
|||
return def, err
|
||||
}
|
||||
if val > uint64(maxValue) {
|
||||
return def, errFindParse.Format("uint", e.Key)
|
||||
return def, e.notFound(reflect.Uint)
|
||||
}
|
||||
return uint(val), nil
|
||||
case uint:
|
||||
|
@ -292,17 +336,17 @@ func (e Entry) UintDefault(def uint) (uint, error) {
|
|||
return uint(vv), nil
|
||||
case uint64:
|
||||
if vv > uint64(maxValue) {
|
||||
return def, errFindParse.Format("uint", e.Key)
|
||||
return def, e.notFound(reflect.Uint)
|
||||
}
|
||||
return uint(vv), nil
|
||||
case int:
|
||||
if vv < 0 || vv > int(maxValue) {
|
||||
return def, errFindParse.Format("uint", e.Key)
|
||||
return def, e.notFound(reflect.Uint)
|
||||
}
|
||||
return uint(vv), nil
|
||||
}
|
||||
|
||||
return def, errFindParse.Format("uint", e.Key)
|
||||
return def, e.notFound(reflect.Uint)
|
||||
}
|
||||
|
||||
// Uint8Default returns the entry's value as uint8.
|
||||
|
@ -310,7 +354,7 @@ func (e Entry) UintDefault(def uint) (uint, error) {
|
|||
func (e Entry) Uint8Default(def uint8) (uint8, error) {
|
||||
v := e.ValueRaw
|
||||
if v == nil {
|
||||
return def, errFindParse.Format("uint8", e.Key)
|
||||
return def, e.notFound(reflect.Uint8)
|
||||
}
|
||||
|
||||
switch vv := v.(type) {
|
||||
|
@ -320,39 +364,39 @@ func (e Entry) Uint8Default(def uint8) (uint8, error) {
|
|||
return def, err
|
||||
}
|
||||
if val > math.MaxUint8 {
|
||||
return def, errFindParse.Format("uint8", e.Key)
|
||||
return def, e.notFound(reflect.Uint8)
|
||||
}
|
||||
return uint8(val), nil
|
||||
case uint:
|
||||
if vv > math.MaxUint8 {
|
||||
return def, errFindParse.Format("uint8", e.Key)
|
||||
return def, e.notFound(reflect.Uint8)
|
||||
}
|
||||
return uint8(vv), nil
|
||||
case uint8:
|
||||
return vv, nil
|
||||
case uint16:
|
||||
if vv > math.MaxUint8 {
|
||||
return def, errFindParse.Format("uint8", e.Key)
|
||||
return def, e.notFound(reflect.Uint8)
|
||||
}
|
||||
return uint8(vv), nil
|
||||
case uint32:
|
||||
if vv > math.MaxUint8 {
|
||||
return def, errFindParse.Format("uint8", e.Key)
|
||||
return def, e.notFound(reflect.Uint8)
|
||||
}
|
||||
return uint8(vv), nil
|
||||
case uint64:
|
||||
if vv > math.MaxUint8 {
|
||||
return def, errFindParse.Format("uint8", e.Key)
|
||||
return def, e.notFound(reflect.Uint8)
|
||||
}
|
||||
return uint8(vv), nil
|
||||
case int:
|
||||
if vv < 0 || vv > math.MaxUint8 {
|
||||
return def, errFindParse.Format("uint8", e.Key)
|
||||
return def, e.notFound(reflect.Uint8)
|
||||
}
|
||||
return uint8(vv), nil
|
||||
}
|
||||
|
||||
return def, errFindParse.Format("uint8", e.Key)
|
||||
return def, e.notFound(reflect.Uint8)
|
||||
}
|
||||
|
||||
// Uint16Default returns the entry's value as uint16.
|
||||
|
@ -360,7 +404,7 @@ func (e Entry) Uint8Default(def uint8) (uint8, error) {
|
|||
func (e Entry) Uint16Default(def uint16) (uint16, error) {
|
||||
v := e.ValueRaw
|
||||
if v == nil {
|
||||
return def, errFindParse.Format("uint16", e.Key)
|
||||
return def, e.notFound(reflect.Uint16)
|
||||
}
|
||||
|
||||
switch vv := v.(type) {
|
||||
|
@ -370,12 +414,12 @@ func (e Entry) Uint16Default(def uint16) (uint16, error) {
|
|||
return def, err
|
||||
}
|
||||
if val > math.MaxUint16 {
|
||||
return def, errFindParse.Format("uint16", e.Key)
|
||||
return def, e.notFound(reflect.Uint16)
|
||||
}
|
||||
return uint16(val), nil
|
||||
case uint:
|
||||
if vv > math.MaxUint16 {
|
||||
return def, errFindParse.Format("uint16", e.Key)
|
||||
return def, e.notFound(reflect.Uint16)
|
||||
}
|
||||
return uint16(vv), nil
|
||||
case uint8:
|
||||
|
@ -384,22 +428,22 @@ func (e Entry) Uint16Default(def uint16) (uint16, error) {
|
|||
return vv, nil
|
||||
case uint32:
|
||||
if vv > math.MaxUint16 {
|
||||
return def, errFindParse.Format("uint16", e.Key)
|
||||
return def, e.notFound(reflect.Uint16)
|
||||
}
|
||||
return uint16(vv), nil
|
||||
case uint64:
|
||||
if vv > math.MaxUint16 {
|
||||
return def, errFindParse.Format("uint16", e.Key)
|
||||
return def, e.notFound(reflect.Uint16)
|
||||
}
|
||||
return uint16(vv), nil
|
||||
case int:
|
||||
if vv < 0 || vv > math.MaxUint16 {
|
||||
return def, errFindParse.Format("uint16", e.Key)
|
||||
return def, e.notFound(reflect.Uint16)
|
||||
}
|
||||
return uint16(vv), nil
|
||||
}
|
||||
|
||||
return def, errFindParse.Format("uint16", e.Key)
|
||||
return def, e.notFound(reflect.Uint16)
|
||||
}
|
||||
|
||||
// Uint32Default returns the entry's value as uint32.
|
||||
|
@ -407,7 +451,7 @@ func (e Entry) Uint16Default(def uint16) (uint16, error) {
|
|||
func (e Entry) Uint32Default(def uint32) (uint32, error) {
|
||||
v := e.ValueRaw
|
||||
if v == nil {
|
||||
return def, errFindParse.Format("uint32", e.Key)
|
||||
return def, e.notFound(reflect.Uint32)
|
||||
}
|
||||
|
||||
switch vv := v.(type) {
|
||||
|
@ -417,12 +461,12 @@ func (e Entry) Uint32Default(def uint32) (uint32, error) {
|
|||
return def, err
|
||||
}
|
||||
if val > math.MaxUint32 {
|
||||
return def, errFindParse.Format("uint32", e.Key)
|
||||
return def, e.notFound(reflect.Uint32)
|
||||
}
|
||||
return uint32(val), nil
|
||||
case uint:
|
||||
if vv > math.MaxUint32 {
|
||||
return def, errFindParse.Format("uint32", e.Key)
|
||||
return def, e.notFound(reflect.Uint32)
|
||||
}
|
||||
return uint32(vv), nil
|
||||
case uint8:
|
||||
|
@ -433,19 +477,19 @@ func (e Entry) Uint32Default(def uint32) (uint32, error) {
|
|||
return vv, nil
|
||||
case uint64:
|
||||
if vv > math.MaxUint32 {
|
||||
return def, errFindParse.Format("uint32", e.Key)
|
||||
return def, e.notFound(reflect.Uint32)
|
||||
}
|
||||
return uint32(vv), nil
|
||||
case int32:
|
||||
return uint32(vv), nil
|
||||
case int64:
|
||||
if vv < 0 || vv > math.MaxUint32 {
|
||||
return def, errFindParse.Format("uint32", e.Key)
|
||||
return def, e.notFound(reflect.Uint32)
|
||||
}
|
||||
return uint32(vv), nil
|
||||
}
|
||||
|
||||
return def, errFindParse.Format("uint32", e.Key)
|
||||
return def, e.notFound(reflect.Uint32)
|
||||
}
|
||||
|
||||
// Uint64Default returns the entry's value as uint64.
|
||||
|
@ -453,7 +497,7 @@ func (e Entry) Uint32Default(def uint32) (uint32, error) {
|
|||
func (e Entry) Uint64Default(def uint64) (uint64, error) {
|
||||
v := e.ValueRaw
|
||||
if v == nil {
|
||||
return def, errFindParse.Format("uint64", e.Key)
|
||||
return def, e.notFound(reflect.Uint64)
|
||||
}
|
||||
|
||||
switch vv := v.(type) {
|
||||
|
@ -463,7 +507,7 @@ func (e Entry) Uint64Default(def uint64) (uint64, error) {
|
|||
return def, err
|
||||
}
|
||||
if val > math.MaxUint64 {
|
||||
return def, errFindParse.Format("uint64", e.Key)
|
||||
return def, e.notFound(reflect.Uint64)
|
||||
}
|
||||
return uint64(val), nil
|
||||
case uint8:
|
||||
|
@ -480,7 +524,7 @@ func (e Entry) Uint64Default(def uint64) (uint64, error) {
|
|||
return uint64(vv), nil
|
||||
}
|
||||
|
||||
return def, errFindParse.Format("uint64", e.Key)
|
||||
return def, e.notFound(reflect.Uint64)
|
||||
}
|
||||
|
||||
// Float32Default returns the entry's value as float32.
|
||||
|
@ -488,7 +532,7 @@ func (e Entry) Uint64Default(def uint64) (uint64, error) {
|
|||
func (e Entry) Float32Default(key string, def float32) (float32, error) {
|
||||
v := e.ValueRaw
|
||||
if v == nil {
|
||||
return def, errFindParse.Format("float32", e.Key)
|
||||
return def, e.notFound(reflect.Float32)
|
||||
}
|
||||
|
||||
switch vv := v.(type) {
|
||||
|
@ -498,21 +542,21 @@ func (e Entry) Float32Default(key string, def float32) (float32, error) {
|
|||
return def, err
|
||||
}
|
||||
if val > math.MaxFloat32 {
|
||||
return def, errFindParse.Format("float32", e.Key)
|
||||
return def, e.notFound(reflect.Float32)
|
||||
}
|
||||
return float32(val), nil
|
||||
case float32:
|
||||
return vv, nil
|
||||
case float64:
|
||||
if vv > math.MaxFloat32 {
|
||||
return def, errFindParse.Format("float32", e.Key)
|
||||
return def, e.notFound(reflect.Float32)
|
||||
}
|
||||
return float32(vv), nil
|
||||
case int:
|
||||
return float32(vv), nil
|
||||
}
|
||||
|
||||
return def, errFindParse.Format("float32", e.Key)
|
||||
return def, e.notFound(reflect.Float32)
|
||||
}
|
||||
|
||||
// Float64Default returns the entry's value as float64.
|
||||
|
@ -520,7 +564,7 @@ func (e Entry) Float32Default(key string, def float32) (float32, error) {
|
|||
func (e Entry) Float64Default(def float64) (float64, error) {
|
||||
v := e.ValueRaw
|
||||
if v == nil {
|
||||
return def, errFindParse.Format("float64", e.Key)
|
||||
return def, e.notFound(reflect.Float64)
|
||||
}
|
||||
|
||||
switch vv := v.(type) {
|
||||
|
@ -544,7 +588,7 @@ func (e Entry) Float64Default(def float64) (float64, error) {
|
|||
return float64(vv), nil
|
||||
}
|
||||
|
||||
return def, errFindParse.Format("float64", e.Key)
|
||||
return def, e.notFound(reflect.Float64)
|
||||
}
|
||||
|
||||
// BoolDefault returns the user's value as bool.
|
||||
|
@ -556,7 +600,7 @@ func (e Entry) Float64Default(def float64) (float64, error) {
|
|||
func (e Entry) BoolDefault(def bool) (bool, error) {
|
||||
v := e.ValueRaw
|
||||
if v == nil {
|
||||
return def, errFindParse.Format("bool", e.Key)
|
||||
return def, e.notFound(reflect.Bool)
|
||||
}
|
||||
|
||||
switch vv := v.(type) {
|
||||
|
@ -575,7 +619,7 @@ func (e Entry) BoolDefault(def bool) (bool, error) {
|
|||
return false, nil
|
||||
}
|
||||
|
||||
return def, errFindParse.Format("bool", e.Key)
|
||||
return def, e.notFound(reflect.Bool)
|
||||
}
|
||||
|
||||
// Value returns the value of the entry,
|
||||
|
@ -765,7 +809,7 @@ func (r *Store) GetStringTrim(name string) string {
|
|||
func (r *Store) GetInt(key string) (int, error) {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return 0, errFindParse.Format("int", key)
|
||||
return 0, v.notFound(reflect.Int)
|
||||
}
|
||||
return v.IntDefault(-1)
|
||||
}
|
||||
|
@ -785,7 +829,7 @@ func (r *Store) GetIntDefault(key string, def int) int {
|
|||
func (r *Store) GetInt8(key string) (int8, error) {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return -1, errFindParse.Format("int8", key)
|
||||
return -1, v.notFound(reflect.Int8)
|
||||
}
|
||||
return v.Int8Default(-1)
|
||||
}
|
||||
|
@ -805,7 +849,7 @@ func (r *Store) GetInt8Default(key string, def int8) int8 {
|
|||
func (r *Store) GetInt16(key string) (int16, error) {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return -1, errFindParse.Format("int16", key)
|
||||
return -1, v.notFound(reflect.Int16)
|
||||
}
|
||||
return v.Int16Default(-1)
|
||||
}
|
||||
|
@ -825,7 +869,7 @@ func (r *Store) GetInt16Default(key string, def int16) int16 {
|
|||
func (r *Store) GetInt32(key string) (int32, error) {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return -1, errFindParse.Format("int32", key)
|
||||
return -1, v.notFound(reflect.Int32)
|
||||
}
|
||||
return v.Int32Default(-1)
|
||||
}
|
||||
|
@ -845,7 +889,7 @@ func (r *Store) GetInt32Default(key string, def int32) int32 {
|
|||
func (r *Store) GetInt64(key string) (int64, error) {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return -1, errFindParse.Format("int64", key)
|
||||
return -1, v.notFound(reflect.Int64)
|
||||
}
|
||||
return v.Int64Default(-1)
|
||||
}
|
||||
|
@ -865,7 +909,7 @@ func (r *Store) GetInt64Default(key string, def int64) int64 {
|
|||
func (r *Store) GetUint(key string) (uint, error) {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return 0, errFindParse.Format("uint", key)
|
||||
return 0, v.notFound(reflect.Uint)
|
||||
}
|
||||
return v.UintDefault(0)
|
||||
}
|
||||
|
@ -885,7 +929,7 @@ func (r *Store) GetUintDefault(key string, def uint) uint {
|
|||
func (r *Store) GetUint8(key string) (uint8, error) {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return 0, errFindParse.Format("uint8", key)
|
||||
return 0, v.notFound(reflect.Uint8)
|
||||
}
|
||||
return v.Uint8Default(0)
|
||||
}
|
||||
|
@ -905,7 +949,7 @@ func (r *Store) GetUint8Default(key string, def uint8) uint8 {
|
|||
func (r *Store) GetUint16(key string) (uint16, error) {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return 0, errFindParse.Format("uint16", key)
|
||||
return 0, v.notFound(reflect.Uint16)
|
||||
}
|
||||
return v.Uint16Default(0)
|
||||
}
|
||||
|
@ -925,7 +969,7 @@ func (r *Store) GetUint16Default(key string, def uint16) uint16 {
|
|||
func (r *Store) GetUint32(key string) (uint32, error) {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return 0, errFindParse.Format("uint32", key)
|
||||
return 0, v.notFound(reflect.Uint32)
|
||||
}
|
||||
return v.Uint32Default(0)
|
||||
}
|
||||
|
@ -945,7 +989,7 @@ func (r *Store) GetUint32Default(key string, def uint32) uint32 {
|
|||
func (r *Store) GetUint64(key string) (uint64, error) {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return 0, errFindParse.Format("uint64", key)
|
||||
return 0, v.notFound(reflect.Uint64)
|
||||
}
|
||||
return v.Uint64Default(0)
|
||||
}
|
||||
|
@ -965,7 +1009,7 @@ func (r *Store) GetUint64Default(key string, def uint64) uint64 {
|
|||
func (r *Store) GetFloat64(key string) (float64, error) {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return -1, errFindParse.Format("float64", key)
|
||||
return -1, v.notFound(reflect.Float64)
|
||||
}
|
||||
return v.Float64Default(-1)
|
||||
}
|
||||
|
@ -989,7 +1033,7 @@ func (r *Store) GetFloat64Default(key string, def float64) float64 {
|
|||
func (r *Store) GetBool(key string) (bool, error) {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return false, errFindParse.Format("bool", key)
|
||||
return false, v.notFound(reflect.Bool)
|
||||
}
|
||||
|
||||
return v.BoolDefault(false)
|
||||
|
|
|
@ -2,12 +2,13 @@ package netutil
|
|||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
|
@ -32,14 +33,6 @@ func (l tcpKeepAliveListener) Accept() (c net.Conn, err error) {
|
|||
return tc, nil
|
||||
}
|
||||
|
||||
var (
|
||||
errPortAlreadyUsed = errors.New("port is already used")
|
||||
errRemoveUnix = errors.New("unexpected error when trying to remove unix socket file. Addr: %s | Trace: %s")
|
||||
errChmod = errors.New("cannot chmod %#o for %q: %s")
|
||||
errCertKeyMissing = errors.New("you should provide certFile and keyFile for TLS/SSL")
|
||||
errParseTLS = errors.New("couldn't load TLS, certFile=%q, keyFile=%q. Trace: %s")
|
||||
)
|
||||
|
||||
// TCP returns a new tcp(ipv6 if supported by network) and an error on failure.
|
||||
func TCP(addr string) (net.Listener, error) {
|
||||
l, err := net.Listen("tcp", addr)
|
||||
|
@ -68,16 +61,16 @@ func TCPKeepAlive(addr string) (ln net.Listener, err error) {
|
|||
// UNIX returns a new unix(file) Listener.
|
||||
func UNIX(socketFile string, mode os.FileMode) (net.Listener, error) {
|
||||
if errOs := os.Remove(socketFile); errOs != nil && !os.IsNotExist(errOs) {
|
||||
return nil, errRemoveUnix.Format(socketFile, errOs.Error())
|
||||
return nil, fmt.Errorf("%s: %w", socketFile, errOs)
|
||||
}
|
||||
|
||||
l, err := net.Listen("unix", socketFile)
|
||||
if err != nil {
|
||||
return nil, errPortAlreadyUsed.AppendErr(err)
|
||||
return nil, fmt.Errorf("port already in use: %w", err)
|
||||
}
|
||||
|
||||
if err = os.Chmod(socketFile, mode); err != nil {
|
||||
return nil, errChmod.Format(mode, socketFile, err.Error())
|
||||
return nil, fmt.Errorf("cannot chmod %#o for %q: %w", mode, socketFile, err)
|
||||
}
|
||||
|
||||
return l, nil
|
||||
|
@ -86,12 +79,12 @@ func UNIX(socketFile string, mode os.FileMode) (net.Listener, error) {
|
|||
// TLS returns a new TLS Listener and an error on failure.
|
||||
func TLS(addr, certFile, keyFile string) (net.Listener, error) {
|
||||
if certFile == "" || keyFile == "" {
|
||||
return nil, errCertKeyMissing
|
||||
return nil, errors.New("empty certFile or KeyFile")
|
||||
}
|
||||
|
||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return nil, errParseTLS.Format(certFile, keyFile, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return CERT(addr, cert)
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"github.com/kataras/iris/core/errgroup"
|
||||
"github.com/kataras/iris/macro"
|
||||
macroHandler "github.com/kataras/iris/macro/handler"
|
||||
)
|
||||
|
@ -157,6 +160,15 @@ func (repo *repository) register(route *Route) {
|
|||
repo.pos[route.tmpl.Src] = len(repo.routes) - 1
|
||||
}
|
||||
|
||||
type apiError struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (e *apiError) Is(err error) bool {
|
||||
_, ok := err.(*apiError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// APIBuilder the visible API for constructing the router
|
||||
// and child routers.
|
||||
type APIBuilder struct {
|
||||
|
@ -174,7 +186,7 @@ type APIBuilder struct {
|
|||
// the list of possible errors that can be
|
||||
// collected on the build state to log
|
||||
// to the end-user.
|
||||
reporter *errors.Reporter
|
||||
errors *errgroup.Group
|
||||
|
||||
// the per-party handlers, order
|
||||
// of handlers registration matters.
|
||||
|
@ -212,7 +224,7 @@ func NewAPIBuilder() *APIBuilder {
|
|||
api := &APIBuilder{
|
||||
macros: macro.Defaults,
|
||||
errorCodeHandlers: defaultErrorCodeHandlers(),
|
||||
reporter: errors.NewReporter(),
|
||||
errors: errgroup.New("API Builder"),
|
||||
relativePath: "/",
|
||||
routes: new(repository),
|
||||
}
|
||||
|
@ -228,14 +240,9 @@ func (api *APIBuilder) GetRelPath() string {
|
|||
return api.relativePath
|
||||
}
|
||||
|
||||
// GetReport returns an error may caused by party's methods.
|
||||
func (api *APIBuilder) GetReport() error {
|
||||
return api.reporter.Return()
|
||||
}
|
||||
|
||||
// GetReporter returns the reporter for adding errors
|
||||
func (api *APIBuilder) GetReporter() *errors.Reporter {
|
||||
return api.reporter
|
||||
// GetReporter returns the reporter for adding or receiving any errors caused when building the API.
|
||||
func (api *APIBuilder) GetReporter() *errgroup.Group {
|
||||
return api.errors
|
||||
}
|
||||
|
||||
// AllowMethods will re-register the future routes that will be registered
|
||||
|
@ -295,9 +302,11 @@ func (api *APIBuilder) createRoutes(methods []string, relativePath string, handl
|
|||
}
|
||||
}
|
||||
|
||||
filename, line := getCaller()
|
||||
|
||||
fullpath := api.relativePath + relativePath // for now, keep the last "/" if any, "/xyz/"
|
||||
if len(handlers) == 0 {
|
||||
api.reporter.Add("missing handlers for route %s: %s", strings.Join(methods, ", "), fullpath)
|
||||
api.errors.Addf("missing handlers for route[%s:%d] %s: %s", filename, line, strings.Join(methods, ", "), fullpath)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -336,10 +345,13 @@ func (api *APIBuilder) createRoutes(methods []string, relativePath string, handl
|
|||
for i, m := range methods {
|
||||
route, err := NewRoute(m, subdomain, path, possibleMainHandlerName, routeHandlers, *api.macros)
|
||||
if err != nil { // template path parser errors:
|
||||
api.reporter.Add("%v -> %s:%s:%s", err, m, subdomain, path)
|
||||
api.errors.Addf("[%s:%d] %v -> %s:%s:%s", filename, line, err, m, subdomain, path)
|
||||
continue
|
||||
}
|
||||
|
||||
route.SourceFileName = filename
|
||||
route.SourceLineNumber = line
|
||||
|
||||
// Add UseGlobal & DoneGlobal Handlers
|
||||
route.Use(api.beginGlobalHandlers...)
|
||||
route.Done(api.doneGlobalHandlers...)
|
||||
|
@ -350,6 +362,32 @@ func (api *APIBuilder) createRoutes(methods []string, relativePath string, handl
|
|||
return routes
|
||||
}
|
||||
|
||||
// https://golang.org/doc/go1.9#callersframes
|
||||
func getCaller() (string, int) {
|
||||
var pcs [32]uintptr
|
||||
n := runtime.Callers(1, pcs[:])
|
||||
frames := runtime.CallersFrames(pcs[:n])
|
||||
wd, _ := os.Getwd()
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
file := frame.File
|
||||
|
||||
if !strings.Contains(file, "/kataras/iris") || strings.Contains(file, "/kataras/iris/_examples") || strings.Contains(file, "iris-contrib/examples") {
|
||||
if relFile, err := filepath.Rel(wd, file); err == nil {
|
||||
file = "./" + relFile
|
||||
}
|
||||
|
||||
return file, frame.Line
|
||||
}
|
||||
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return "???", 0
|
||||
}
|
||||
|
||||
// Handle registers a route to the server's api.
|
||||
// if empty method is passed then handler(s) are being registered to all methods, same as .Any.
|
||||
//
|
||||
|
@ -381,8 +419,7 @@ func (api *APIBuilder) Handle(method string, relativePath string, handlers ...co
|
|||
// app.Handle("GET", "/user/{id:uint64}", userByIDHandler)
|
||||
// app.Handle("GET", "/user/me", userMeHandler)
|
||||
//
|
||||
// This method is used behind the scenes at the `Controller` function
|
||||
// in order to handle more than one paths for the same controller instance.
|
||||
// app.HandleMany("GET POST", "/path", handler)
|
||||
func (api *APIBuilder) HandleMany(methodOrMulti string, relativePathorMulti string, handlers ...context.Handler) (routes []*Route) {
|
||||
// at least slash
|
||||
// a space
|
||||
|
@ -502,7 +539,7 @@ func (api *APIBuilder) Party(relativePath string, handlers ...context.Handler) P
|
|||
errorCodeHandlers: api.errorCodeHandlers,
|
||||
beginGlobalHandlers: api.beginGlobalHandlers,
|
||||
doneGlobalHandlers: api.doneGlobalHandlers,
|
||||
reporter: api.reporter,
|
||||
errors: api.errors,
|
||||
// per-party/children
|
||||
middleware: middleware,
|
||||
doneHandlers: api.doneHandlers[0:],
|
||||
|
@ -542,7 +579,7 @@ func (api *APIBuilder) PartyFunc(relativePath string, partyBuilderFunc func(p Pa
|
|||
func (api *APIBuilder) Subdomain(subdomain string, middleware ...context.Handler) Party {
|
||||
if api.relativePath == SubdomainWildcardIndicator {
|
||||
// cannot concat wildcard subdomain with something else
|
||||
api.reporter.Add("cannot concat parent wildcard subdomain with anything else -> %s , %s",
|
||||
api.errors.Addf("cannot concat parent wildcard subdomain with anything else -> %s , %s",
|
||||
api.relativePath, subdomain)
|
||||
return api
|
||||
}
|
||||
|
@ -562,7 +599,7 @@ func (api *APIBuilder) Subdomain(subdomain string, middleware ...context.Handler
|
|||
func (api *APIBuilder) WildcardSubdomain(middleware ...context.Handler) Party {
|
||||
if hasSubdomain(api.relativePath) {
|
||||
// cannot concat static subdomain with a dynamic one, wildcard should be at the root level
|
||||
api.reporter.Add("cannot concat static subdomain with a dynamic one. Dynamic subdomains should be at the root level -> %s",
|
||||
api.errors.Addf("cannot concat static subdomain with a dynamic one. Dynamic subdomains should be at the root level -> %s",
|
||||
api.relativePath)
|
||||
return api
|
||||
}
|
||||
|
@ -831,7 +868,7 @@ func (api *APIBuilder) Favicon(favPath string, requestPath ...string) *Route {
|
|||
|
||||
f, err := os.Open(favPath)
|
||||
if err != nil {
|
||||
api.reporter.AddErr(errDirectoryFileNotFound.Format(favPath, err.Error()))
|
||||
api.errors.Addf("favicon: file or directory %s not found: %w", favPath, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -848,8 +885,7 @@ func (api *APIBuilder) Favicon(favPath string, requestPath ...string) *Route {
|
|||
// So we could panic but we don't,
|
||||
// we just interrupt with a message
|
||||
// to the (user-defined) logger.
|
||||
api.reporter.AddErr(errDirectoryFileNotFound.
|
||||
Format(favPath, "favicon: couldn't read the data bytes for file: "+err.Error()))
|
||||
api.errors.Addf("favicon: couldn't read the data bytes for %s: %w", favPath, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
/*
|
||||
Relative to deprecation:
|
||||
- party.go#L138-154
|
||||
- deprecated_example_test.go
|
||||
*/
|
||||
|
||||
// https://golang.org/doc/go1.9#callersframes
|
||||
func getCaller() (string, int) {
|
||||
var pcs [32]uintptr
|
||||
n := runtime.Callers(1, pcs[:])
|
||||
frames := runtime.CallersFrames(pcs[:n])
|
||||
wd, _ := os.Getwd()
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
file := frame.File
|
||||
|
||||
if (!strings.Contains(file, "/kataras/iris") ||
|
||||
strings.Contains(file, "/kataras/iris/_examples") ||
|
||||
strings.Contains(file, "/iris-contrib/examples") ||
|
||||
(strings.Contains(file, "/kataras/iris/core/router") && !strings.Contains(file, "deprecated.go"))) &&
|
||||
!strings.HasSuffix(frame.Func.Name(), ".getCaller") && !strings.Contains(file, "/go/src/testing") {
|
||||
|
||||
if relFile, err := filepath.Rel(wd, file); err == nil {
|
||||
file = "./" + relFile
|
||||
}
|
||||
|
||||
return file, frame.Line
|
||||
}
|
||||
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return "?", 0
|
||||
}
|
||||
|
||||
// StaticWeb is DEPRECATED. Use HandleDir(requestPath, directory) instead.
|
||||
func (api *APIBuilder) StaticWeb(requestPath string, directory string) *Route {
|
||||
file, line := getCaller()
|
||||
api.reporter.Add(`StaticWeb is DEPRECATED and it will be removed eventually.
|
||||
Source: %s:%d
|
||||
Use .HandleDir("%s", "%s") instead.`, file, line, requestPath, directory)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StaticHandler is DEPRECATED.
|
||||
// Use iris.FileServer(directory, iris.DirOptions{ShowList: true, Gzip: true}) instead.
|
||||
//
|
||||
// Example https://github.com/kataras/iris/tree/master/_examples/file-server/basic
|
||||
func (api *APIBuilder) StaticHandler(directory string, showList bool, gzip bool) context.Handler {
|
||||
file, line := getCaller()
|
||||
api.reporter.Add(`StaticHandler is DEPRECATED and it will be removed eventually.
|
||||
Source: %s:%d
|
||||
Use iris.FileServer("%s", iris.DirOptions{ShowList: %v, Gzip: %v}) instead.`, file, line, directory, showList, gzip)
|
||||
return FileServer(directory, DirOptions{ShowList: showList, Gzip: gzip})
|
||||
}
|
||||
|
||||
// StaticEmbedded is DEPRECATED.
|
||||
// Use HandleDir(requestPath, directory, iris.DirOptions{Asset: Asset, AssetInfo: AssetInfo, AssetNames: AssetNames}) instead.
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/embedding-files-into-app
|
||||
func (api *APIBuilder) StaticEmbedded(requestPath string, directory string, assetFn func(name string) ([]byte, error), namesFn func() []string) *Route {
|
||||
file, line := getCaller()
|
||||
api.reporter.Add(`StaticEmbedded is DEPRECATED and it will be removed eventually.
|
||||
It is also miss the AssetInfo bindata function, which is required now.
|
||||
Source: %s:%d
|
||||
Use .HandleDir("%s", "%s", iris.DirOptions{Asset: Asset, AssetInfo: AssetInfo, AssetNames: AssetNames}) instead.`, file, line, requestPath, directory)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StaticEmbeddedGzip is DEPRECATED.
|
||||
// Use HandleDir(requestPath, directory, iris.DirOptions{Gzip: true, Asset: Asset, AssetInfo: AssetInfo, AssetNames: AssetNames}) instead.
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/embedding-gziped-files-into-app
|
||||
func (api *APIBuilder) StaticEmbeddedGzip(requestPath string, directory string, assetFn func(name string) ([]byte, error), namesFn func() []string) *Route {
|
||||
file, line := getCaller()
|
||||
api.reporter.Add(`StaticEmbeddedGzip is DEPRECATED and it will be removed eventually.
|
||||
It is also miss the AssetInfo bindata function, which is required now.
|
||||
Source: %s:%d
|
||||
Use .HandleDir("%s", "%s", iris.DirOptions{Gzip: true, Asset: Asset, AssetInfo: AssetInfo, AssetNames: AssetNames}) instead.`, file, line, requestPath, directory)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func ExampleParty_StaticWeb() {
|
||||
api := NewAPIBuilder()
|
||||
api.StaticWeb("/static", "./assets")
|
||||
|
||||
err := api.GetReport()
|
||||
if err == nil {
|
||||
panic("expected report for deprecation")
|
||||
}
|
||||
|
||||
fmt.Print(err)
|
||||
// Output: StaticWeb is DEPRECATED and it will be removed eventually.
|
||||
// Source: ./deprecated_example_test.go:9
|
||||
// Use .HandleDir("/static", "./assets") instead.
|
||||
}
|
||||
|
||||
func ExampleParty_StaticHandler() {
|
||||
api := NewAPIBuilder()
|
||||
api.StaticHandler("./assets", false, true)
|
||||
|
||||
err := api.GetReport()
|
||||
if err == nil {
|
||||
panic("expected report for deprecation")
|
||||
}
|
||||
|
||||
fmt.Print(err)
|
||||
// Output: StaticHandler is DEPRECATED and it will be removed eventually.
|
||||
// Source: ./deprecated_example_test.go:24
|
||||
// Use iris.FileServer("./assets", iris.DirOptions{ShowList: false, Gzip: true}) instead.
|
||||
}
|
||||
|
||||
func ExampleParty_StaticEmbedded() {
|
||||
api := NewAPIBuilder()
|
||||
api.StaticEmbedded("/static", "./assets", nil, nil)
|
||||
|
||||
err := api.GetReport()
|
||||
if err == nil {
|
||||
panic("expected report for deprecation")
|
||||
}
|
||||
|
||||
fmt.Print(err)
|
||||
// Output: StaticEmbedded is DEPRECATED and it will be removed eventually.
|
||||
// It is also miss the AssetInfo bindata function, which is required now.
|
||||
// Source: ./deprecated_example_test.go:39
|
||||
// Use .HandleDir("/static", "./assets", iris.DirOptions{Asset: Asset, AssetInfo: AssetInfo, AssetNames: AssetNames}) instead.
|
||||
}
|
||||
|
||||
func ExampleParty_StaticEmbeddedGzip() {
|
||||
api := NewAPIBuilder()
|
||||
api.StaticEmbeddedGzip("/static", "./assets", nil, nil)
|
||||
|
||||
err := api.GetReport()
|
||||
if err == nil {
|
||||
panic("expected report for deprecation")
|
||||
}
|
||||
|
||||
fmt.Print(err)
|
||||
// Output: StaticEmbeddedGzip is DEPRECATED and it will be removed eventually.
|
||||
// It is also miss the AssetInfo bindata function, which is required now.
|
||||
// Source: ./deprecated_example_test.go:55
|
||||
// Use .HandleDir("/static", "./assets", iris.DirOptions{Gzip: true, Asset: Asset, AssetInfo: AssetInfo, AssetNames: AssetNames}) instead.
|
||||
}
|
|
@ -6,7 +6,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"github.com/kataras/iris/core/errgroup"
|
||||
"github.com/kataras/iris/core/netutil"
|
||||
macroHandler "github.com/kataras/iris/macro/handler"
|
||||
|
||||
|
@ -82,7 +82,7 @@ type RoutesProvider interface { // api builder
|
|||
|
||||
func (h *routerHandler) Build(provider RoutesProvider) error {
|
||||
h.trees = h.trees[0:0] // reset, inneed when rebuilding.
|
||||
rp := errors.NewReporter()
|
||||
rp := errgroup.New("Routes Builder")
|
||||
registeredRoutes := provider.GetRoutes()
|
||||
|
||||
// before sort.
|
||||
|
@ -138,7 +138,7 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
|
|||
// the docs better. Or TODO: add a link here in order to help new users.
|
||||
if err := h.addRoute(r); err != nil {
|
||||
// node errors:
|
||||
rp.Add("%v -> %s", err, r.String())
|
||||
rp.Addf("%s: %w", r.String(), err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
|
|||
golog.Debugf(r.Trace()) // keep log different parameter types in the same path as different routes.
|
||||
}
|
||||
|
||||
return rp.Return()
|
||||
return errgroup.Check(rp)
|
||||
}
|
||||
|
||||
func bindMultiParamTypesHandler(top *Route, r *Route) {
|
||||
|
|
|
@ -2,7 +2,7 @@ package router
|
|||
|
||||
import (
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"github.com/kataras/iris/core/errgroup"
|
||||
"github.com/kataras/iris/macro"
|
||||
)
|
||||
|
||||
|
@ -16,8 +16,8 @@ type Party interface {
|
|||
// if r := app.Party("/users"), then the `r.GetRelPath()` is the "/users".
|
||||
// if r := app.Party("www.") or app.Subdomain("www") then the `r.GetRelPath()` is the "www.".
|
||||
GetRelPath() string
|
||||
// GetReporter returns the reporter for adding errors
|
||||
GetReporter() *errors.Reporter
|
||||
// GetReporter returns the reporter for adding or receiving any errors caused when building the API.
|
||||
GetReporter() *errgroup.Group
|
||||
// Macros returns the macro collection that is responsible
|
||||
// to register custom macros with their own parameter types and their macro functions for all routes.
|
||||
//
|
||||
|
@ -135,23 +135,6 @@ type Party interface {
|
|||
//
|
||||
// Examples can be found at: https://github.com/kataras/iris/tree/master/_examples/file-server
|
||||
HandleDir(requestPath, directory string, opts ...DirOptions) *Route
|
||||
// StaticWeb is DEPRECATED. Use HandleDir(requestPath, directory) instead.
|
||||
StaticWeb(requestPath string, directory string) *Route
|
||||
// StaticHandler is DEPRECATED.
|
||||
// Use iris.FileServer(directory, iris.DirOptions{ShowList: true, Gzip: true}) instead.
|
||||
//
|
||||
// Example https://github.com/kataras/iris/tree/master/_examples/file-server/basic
|
||||
StaticHandler(directory string, showList bool, gzip bool) context.Handler
|
||||
// StaticEmbedded is DEPRECATED.
|
||||
// Use HandleDir(requestPath, directory, iris.DirOptions{Asset: Asset, AssetInfo: AssetInfo, AssetNames: AssetNames}) instead.
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/embedding-files-into-app
|
||||
StaticEmbedded(requestPath string, directory string, assetFn func(name string) ([]byte, error), namesFn func() []string) *Route
|
||||
// StaticEmbeddedGzip is DEPRECATED.
|
||||
// Use HandleDir(requestPath, directory, iris.DirOptions{Gzip: true, Asset: Asset, AssetInfo: AssetInfo, AssetNames: AssetNames}) instead.
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/embedding-gziped-files-into-app
|
||||
StaticEmbeddedGzip(requestPath string, directory string, assetFn func(name string) ([]byte, error), namesFn func() []string) *Route
|
||||
|
||||
// None registers an "offline" route
|
||||
// see context.ExecRoute(routeName) and
|
||||
|
|
|
@ -34,6 +34,10 @@ type Route struct {
|
|||
// used by Application to validate param values of a Route based on its name.
|
||||
FormattedPath string `json:"formattedPath"`
|
||||
|
||||
// the source code's filename:filenumber that this route was created from.
|
||||
SourceFileName string
|
||||
SourceLineNumber int
|
||||
|
||||
// StaticSites if not empty, refers to the system (or virtual if embedded) directory
|
||||
// and sub directories that this "GET" route was registered to serve files and folders
|
||||
// that contain index.html (a site). The index handler may registered by other
|
||||
|
@ -276,7 +280,7 @@ func (r Route) ResolvePath(args ...string) string {
|
|||
// Trace returns some debug infos as a string sentence.
|
||||
// Should be called after Build.
|
||||
func (r Route) Trace() string {
|
||||
printfmt := fmt.Sprintf("%s:", r.Method)
|
||||
printfmt := fmt.Sprintf("[%s:%d] %s:", r.SourceFileName, r.SourceLineNumber, r.Method)
|
||||
if r.Subdomain != "" {
|
||||
printfmt += fmt.Sprintf(" %s", r.Subdomain)
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/core/errors"
|
||||
)
|
||||
|
||||
// Router is the "director".
|
||||
|
|
2
go.mod
2
go.mod
|
@ -20,7 +20,7 @@ require (
|
|||
github.com/iris-contrib/go.uuid v2.0.0+incompatible
|
||||
github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0
|
||||
github.com/json-iterator/go v1.1.6
|
||||
github.com/kataras/golog v0.0.0-20190624001437-99c81de45f40
|
||||
github.com/kataras/golog v0.0.9
|
||||
github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d // indirect
|
||||
github.com/mediocregopher/radix/v3 v3.3.0
|
||||
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38
|
||||
|
|
3
go.sum
3
go.sum
|
@ -16,7 +16,8 @@ github.com/iris-contrib/go.uuid v2.0.0+incompatible h1:XZubAYg61/JwnJNbZilGjf3b3
|
|||
github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
|
||||
github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/kataras/golog v0.0.0-20190624001437-99c81de45f40/go.mod h1:PcaEvfvhGsqwXZ6S3CgCbmjcp+4UDUh2MIfF2ZEul8M=
|
||||
github.com/kataras/golog v0.0.9 h1:J7Dl82843nbKQDrQM/abbNJZvQjS6PfmkkffhOTXEpM=
|
||||
github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk=
|
||||
github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0=
|
||||
github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
|
||||
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38 h1:y0Wmhvml7cGnzPa9nocn/fMraMH/lMDdeG+rkx4VgYY=
|
||||
|
|
34
iris.go
34
iris.go
|
@ -4,6 +4,7 @@ import (
|
|||
// std packages
|
||||
|
||||
stdContext "context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
|
@ -17,8 +18,8 @@ import (
|
|||
|
||||
// context for the handlers
|
||||
"github.com/kataras/iris/context"
|
||||
// core packages, needed to build the application
|
||||
"github.com/kataras/iris/core/errors"
|
||||
// core packages, required to build the application
|
||||
"github.com/kataras/iris/core/errgroup"
|
||||
"github.com/kataras/iris/core/host"
|
||||
"github.com/kataras/iris/core/netutil"
|
||||
"github.com/kataras/iris/core/router"
|
||||
|
@ -804,19 +805,31 @@ func Raw(f func() error) Runner {
|
|||
// Build sets up, once, the framework.
|
||||
// It builds the default router with its default macros
|
||||
// and the template functions that are very-closed to iris.
|
||||
//
|
||||
// If error occured while building the Application, the returns type of error will be an *errgroup.Group
|
||||
// which let the callers to inspect the errors and cause, usage:
|
||||
//
|
||||
// import "github.com/kataras/iris/core/errgroup"
|
||||
//
|
||||
// errgroup.Walk(app.Build(), func(typ interface{}, err error) {
|
||||
// app.Logger().Errorf("%s: %s", typ, err)
|
||||
// })
|
||||
func (app *Application) Build() error {
|
||||
rp := errors.NewReporter()
|
||||
rp := errgroup.New("Application Builder")
|
||||
|
||||
app.once.Do(func() {
|
||||
rp.Describe("api builder: %v", app.APIBuilder.GetReport())
|
||||
rp.Err(app.APIBuilder.GetReporter())
|
||||
|
||||
if !app.Router.Downgraded() {
|
||||
// router
|
||||
// create the request handler, the default routing handler
|
||||
routerHandler := router.NewDefaultHandler()
|
||||
err := app.Router.BuildRouter(app.ContextPool, routerHandler, app.APIBuilder, false)
|
||||
if err != nil {
|
||||
rp.Err(err)
|
||||
}
|
||||
|
||||
rp.Describe("router: %v", app.Router.BuildRouter(app.ContextPool, routerHandler, app.APIBuilder, false))
|
||||
// re-build of the router from outside can be done with;
|
||||
// re-build of the router from outside can be done with
|
||||
// app.RefreshRouter()
|
||||
}
|
||||
|
||||
|
@ -828,11 +841,13 @@ func (app *Application) Build() error {
|
|||
rv := router.NewRoutePathReverser(app.APIBuilder)
|
||||
app.view.AddFunc("urlpath", rv.Path)
|
||||
// app.view.AddFunc("url", rv.URL)
|
||||
rp.Describe("view: %v", app.view.Load())
|
||||
if err := app.view.Load(); err != nil {
|
||||
rp.Group("View Builder").Err(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return rp.Return()
|
||||
return errgroup.Check(rp)
|
||||
}
|
||||
|
||||
// ErrServerClosed is returned by the Server's Serve, ServeTLS, ListenAndServe,
|
||||
|
@ -858,7 +873,8 @@ func (app *Application) Run(serve Runner, withOrWithout ...Configurator) error {
|
|||
// first Build because it doesn't need anything from configuration,
|
||||
// this gives the user the chance to modify the router inside a configurator as well.
|
||||
if err := app.Build(); err != nil {
|
||||
return errors.PrintAndReturnErrors(err, app.logger.Errorf)
|
||||
app.logger.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
app.Configure(withOrWithout...)
|
||||
|
|
|
@ -273,7 +273,11 @@ func (c *ControllerActivator) activate() {
|
|||
}
|
||||
|
||||
func (c *ControllerActivator) addErr(err error) bool {
|
||||
return c.router.GetReporter().AddErr(err)
|
||||
if c.router.GetReporter().Err(err) != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// register all available, exported methods to handlers if possible.
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package sessions
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"github.com/kataras/iris/core/memstore"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
package sessions
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/core/errors"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -78,9 +77,13 @@ func (p *provider) Init(sid string, expires time.Duration) *Session {
|
|||
return newSession
|
||||
}
|
||||
|
||||
// ErrNotFound can be returned when calling `UpdateExpiration` on a non-existing or invalid session entry.
|
||||
// It can be matched directly, i.e: `isErrNotFound := sessions.ErrNotFound.Equal(err)`.
|
||||
var ErrNotFound = errors.New("not found")
|
||||
// ErrNotFound may be returned from `UpdateExpiration` of a non-existing or
|
||||
// invalid session entry from memory storage or databases.
|
||||
// Usage:
|
||||
// if err != nil && err.Is(err, sessions.ErrNotFound) {
|
||||
// [handle error...]
|
||||
// }
|
||||
var ErrNotFound = errors.New("session not found")
|
||||
|
||||
// UpdateExpiration resets the expiration of a session.
|
||||
// if expires > 0 then it will try to update the expiration and destroy task is delayed.
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package sessions
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"github.com/kataras/iris/core/memstore"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -165,7 +166,54 @@ func (s *Session) GetFlashStringDefault(key string, defaultValue string) string
|
|||
return defaultValue
|
||||
}
|
||||
|
||||
var errFindParse = errors.New("Unable to find the %s with key: %s. Found? %#v")
|
||||
// ErrEntryNotFound similar to core/memstore#ErrEntryNotFound but adds
|
||||
// the value (if found) matched to the requested key-value pair of the session's memory storage.
|
||||
type ErrEntryNotFound struct {
|
||||
Err *memstore.ErrEntryNotFound
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
func (e *ErrEntryNotFound) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
// Unwrap method implements the dynamic Unwrap interface of the std errors package.
|
||||
func (e *ErrEntryNotFound) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// As method implements the dynamic As interface of the std errors package.
|
||||
// As should be NOT used directly, use `errors.As` instead.
|
||||
func (e *ErrEntryNotFound) As(target interface{}) bool {
|
||||
if v, ok := target.(*memstore.ErrEntryNotFound); ok && e.Err != nil {
|
||||
return e.Err.As(v)
|
||||
}
|
||||
|
||||
v, ok := target.(*ErrEntryNotFound)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.Value != nil {
|
||||
if v.Value != e.Value {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if v.Err != nil {
|
||||
if e.Err != nil {
|
||||
return e.Err.As(v.Err)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func newErrEntryNotFound(key string, kind reflect.Kind, value interface{}) *ErrEntryNotFound {
|
||||
return &ErrEntryNotFound{Err: &memstore.ErrEntryNotFound{Key: key, Kind: kind}, Value: value}
|
||||
}
|
||||
|
||||
// GetInt same as `Get` but returns its int representation,
|
||||
// if key doesn't exist then it returns -1 and a non-nil error.
|
||||
|
@ -188,7 +236,7 @@ func (s *Session) GetInt(key string) (int, error) {
|
|||
return strconv.Atoi(vstring)
|
||||
}
|
||||
|
||||
return -1, errFindParse.Format("int", key, v)
|
||||
return -1, newErrEntryNotFound(key, reflect.Int, v)
|
||||
}
|
||||
|
||||
// GetIntDefault same as `Get` but returns its int representation,
|
||||
|
@ -241,7 +289,7 @@ func (s *Session) GetInt64(key string) (int64, error) {
|
|||
return strconv.ParseInt(vstring, 10, 64)
|
||||
}
|
||||
|
||||
return -1, errFindParse.Format("int64", key, v)
|
||||
return -1, newErrEntryNotFound(key, reflect.Int64, v)
|
||||
}
|
||||
|
||||
// GetInt64Default same as `Get` but returns its int64 representation,
|
||||
|
@ -283,7 +331,7 @@ func (s *Session) GetFloat32(key string) (float32, error) {
|
|||
return float32(vfloat64), nil
|
||||
}
|
||||
|
||||
return -1, errFindParse.Format("float32", key, v)
|
||||
return -1, newErrEntryNotFound(key, reflect.Float32, v)
|
||||
}
|
||||
|
||||
// GetFloat32Default same as `Get` but returns its float32 representation,
|
||||
|
@ -321,7 +369,7 @@ func (s *Session) GetFloat64(key string) (float64, error) {
|
|||
return strconv.ParseFloat(vstring, 32)
|
||||
}
|
||||
|
||||
return -1, errFindParse.Format("float64", key, v)
|
||||
return -1, newErrEntryNotFound(key, reflect.Float64, v)
|
||||
}
|
||||
|
||||
// GetFloat64Default same as `Get` but returns its float64 representation,
|
||||
|
@ -339,7 +387,7 @@ func (s *Session) GetFloat64Default(key string, defaultValue float64) float64 {
|
|||
func (s *Session) GetBoolean(key string) (bool, error) {
|
||||
v := s.Get(key)
|
||||
if v == nil {
|
||||
return false, errFindParse.Format("bool", key, "nil")
|
||||
return false, newErrEntryNotFound(key, reflect.Bool, nil)
|
||||
}
|
||||
|
||||
// here we could check for "true", "false" and 0 for false and 1 for true
|
||||
|
@ -352,7 +400,7 @@ func (s *Session) GetBoolean(key string) (bool, error) {
|
|||
return strconv.ParseBool(vstring)
|
||||
}
|
||||
|
||||
return false, errFindParse.Format("bool", key, v)
|
||||
return false, newErrEntryNotFound(key, reflect.Bool, v)
|
||||
}
|
||||
|
||||
// GetBooleanDefault same as `Get` but returns its boolean representation,
|
||||
|
|
|
@ -2,12 +2,12 @@ package badger
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"github.com/kataras/iris/sessions"
|
||||
|
||||
"github.com/dgraph-io/badger"
|
||||
|
@ -47,7 +47,7 @@ var _ sessions.Database = (*Database)(nil)
|
|||
// It will remove any old session files.
|
||||
func New(directoryPath string) (*Database, error) {
|
||||
if directoryPath == "" {
|
||||
return nil, errors.New("directoryPath is missing")
|
||||
return nil, errors.New("directoryPath is empty")
|
||||
}
|
||||
|
||||
lindex := directoryPath[len(directoryPath)-1]
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package boltdb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"github.com/kataras/iris/sessions"
|
||||
|
||||
bolt "github.com/etcd-io/bbolt"
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package redis
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"github.com/kataras/iris/sessions"
|
||||
|
||||
"github.com/kataras/golog"
|
||||
|
@ -252,8 +252,13 @@ func closeDB(db *Database) error {
|
|||
}
|
||||
|
||||
var (
|
||||
// ErrRedisClosed an error with message 'Redis is already closed'
|
||||
ErrRedisClosed = errors.New("Redis is already closed")
|
||||
// ErrKeyNotFound an error with message 'Key $thekey doesn't found'
|
||||
ErrKeyNotFound = errors.New("Key '%s' doesn't found")
|
||||
// ErrRedisClosed an error with message 'redis: already closed'
|
||||
ErrRedisClosed = errors.New("redis: already closed")
|
||||
// ErrKeyNotFound a type of error of non-existing redis keys.
|
||||
// The producers(the library) of this error will dynamically wrap this error(fmt.Errorf) with the key name.
|
||||
// Usage:
|
||||
// if err != nil && errors.Is(err, ErrKeyNotFound) {
|
||||
// [...]
|
||||
// }
|
||||
ErrKeyNotFound = errors.New("key not found")
|
||||
)
|
||||
|
|
|
@ -145,7 +145,7 @@ func (r *RadixDriver) Get(key string) (interface{}, error) {
|
|||
return nil, err
|
||||
}
|
||||
if mn.Nil {
|
||||
return nil, ErrKeyNotFound.Format(key)
|
||||
return nil, fmt.Errorf("%s: %w", key, ErrKeyNotFound)
|
||||
}
|
||||
return redisVal, nil
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ func (r *RedigoDriver) Get(key string) (interface{}, error) {
|
|||
return nil, err
|
||||
}
|
||||
if redisVal == nil {
|
||||
return nil, ErrKeyNotFound.Format(key)
|
||||
return nil, fmt.Errorf("%s: %w", key, ErrKeyNotFound)
|
||||
}
|
||||
return redisVal, nil
|
||||
}
|
||||
|
@ -256,7 +256,7 @@ func (r *RedigoDriver) GetBytes(key string) ([]byte, error) {
|
|||
return nil, err
|
||||
}
|
||||
if redisVal == nil {
|
||||
return nil, ErrKeyNotFound.Format(key)
|
||||
return nil, fmt.Errorf("%s: %w", key, ErrKeyNotFound)
|
||||
}
|
||||
|
||||
return redis.Bytes(redisVal, err)
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -91,6 +92,11 @@ func (s *AmberEngine) Load() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
// change the directory field configuration, load happens after directory has been set, so we will not have any problems here.
|
||||
s.directory = dir
|
||||
return s.loadDirectory()
|
||||
|
|
|
@ -209,6 +209,11 @@ func (s *DjangoEngine) Load() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
// change the directory field configuration, load happens after directory has been set, so we will not have any problems here.
|
||||
s.directory = dir
|
||||
return s.loadDirectory()
|
||||
|
|
|
@ -111,6 +111,10 @@ func (s *HandlebarsEngine) Load() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
// change the directory field configuration, load happens after directory has been set, so we will not have any problems here.
|
||||
s.directory = dir
|
||||
return s.loadDirectory()
|
||||
|
|
|
@ -226,6 +226,11 @@ func (s *HTMLEngine) Load() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
// change the directory field configuration, load happens after directory has been set, so we will not have any problems here.
|
||||
s.directory = dir
|
||||
return s.loadDirectory()
|
||||
|
|
|
@ -47,6 +47,10 @@ var jetExtensions = [...]string{
|
|||
|
||||
// Jet creates and returns a new jet view engine.
|
||||
func Jet(directory, extension string) *JetEngine {
|
||||
// if _, err := os.Stat(directory); os.IsNotExist(err) {
|
||||
// panic(err)
|
||||
// }
|
||||
|
||||
extOK := false
|
||||
for _, ext := range jetExtensions {
|
||||
if ext == extension {
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
package view
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/core/errors"
|
||||
)
|
||||
|
||||
// View is responsible to
|
||||
|
@ -37,8 +36,6 @@ func (v *View) Len() int {
|
|||
return len(v.engines)
|
||||
}
|
||||
|
||||
var errNoViewEngineForExt = errors.New("no view engine found for '%s'")
|
||||
|
||||
// ExecuteWriter calls the correct view Engine's ExecuteWriter func
|
||||
func (v *View) ExecuteWriter(w io.Writer, filename string, layout string, bindingData interface{}) error {
|
||||
if len(filename) > 2 {
|
||||
|
@ -49,7 +46,7 @@ func (v *View) ExecuteWriter(w io.Writer, filename string, layout string, bindin
|
|||
|
||||
e := v.Find(filename)
|
||||
if e == nil {
|
||||
return errNoViewEngineForExt.Format(filepath.Ext(filename))
|
||||
return fmt.Errorf("no view engine found for '%s'", filepath.Ext(filename))
|
||||
}
|
||||
|
||||
return e.ExecuteWriter(w, filename, layout, bindingData)
|
||||
|
|
Loading…
Reference in New Issue
Block a user