Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
3d413a8605
|
|||
|
46a47e7cd5
|
|||
|
cb32017224
|
|||
|
286ae7f28b
|
|||
|
3f7ef2e0c2
|
|||
|
b820a75f5e
|
|||
|
4e4c43930a
|
|||
|
5b240e0457
|
|||
|
4855e19fb3
|
|||
|
7014f8d0ff
|
|||
|
cb7ea4cf3f
|
|||
|
4ac0a09b85
|
|||
|
7b2c247538
|
|||
|
80fa0d5279
|
|||
|
1fb6970d5a
|
|||
|
e23765a571
|
|||
|
10983035db
|
|||
|
29270855f8
|
|||
|
864953076a
|
|||
|
b5fc0df93b
|
|||
|
1ab0570883
|
@@ -26,7 +26,7 @@ func file_func_line() (string, string, int) {
|
|||||||
func IsNillable(kind reflect.Kind) bool {
|
func IsNillable(kind reflect.Kind) bool {
|
||||||
switch kind {
|
switch kind {
|
||||||
// based on reflect/type.go -> Kind
|
// based on reflect/type.go -> Kind
|
||||||
case reflect.Ptr, reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Slice, reflect.UnsafePointer:
|
case reflect.Pointer, reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Slice, reflect.UnsafePointer:
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -48,6 +48,14 @@ func (logger *ConsoleLoggerImpl) WriteErrRequest(err error, uuid string) (errnum
|
|||||||
return errnum
|
return errnum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (logger *ConsoleLoggerImpl) WriteErrMsgRequest(err error, message string, uuid string) (errnum int) {
|
||||||
|
if err != nil {
|
||||||
|
logger.Write(uuid + " " + message + ": Error: " + err.Error())
|
||||||
|
errnum = 1
|
||||||
|
}
|
||||||
|
return errnum
|
||||||
|
}
|
||||||
|
|
||||||
func (logger *ConsoleLoggerImpl) WriteDebug(message string) {
|
func (logger *ConsoleLoggerImpl) WriteDebug(message string) {
|
||||||
if DEBUG {
|
if DEBUG {
|
||||||
logger.Write(message)
|
logger.Write(message)
|
||||||
|
|||||||
@@ -76,8 +76,8 @@ func (logger *FileLoggerImpl) Write(message string) {
|
|||||||
logger.messages <- time.Now().Format(time.UnixDate) + " : " + message + "\n"
|
logger.messages <- time.Now().Format(time.UnixDate) + " : " + message + "\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *FileLoggerImpl) WriteRequest(message string, request string) {
|
func (logger *FileLoggerImpl) WriteRequest(message string, uuid string) {
|
||||||
logger.Write(request + " : " + message)
|
logger.Write(uuid + " : " + message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *FileLoggerImpl) WriteErr(err error) (errnum int) {
|
func (logger *FileLoggerImpl) WriteErr(err error) (errnum int) {
|
||||||
@@ -96,6 +96,14 @@ func (logger *FileLoggerImpl) WriteErrRequest(err error, uuid string) (errnum in
|
|||||||
return errnum
|
return errnum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (logger *FileLoggerImpl) WriteErrMsgRequest(err error, message string, uuid string) (errnum int) {
|
||||||
|
if err != nil {
|
||||||
|
logger.Write(uuid + " " + message + ": Error: " + err.Error())
|
||||||
|
errnum = 1
|
||||||
|
}
|
||||||
|
return errnum
|
||||||
|
}
|
||||||
|
|
||||||
func (logger *FileLoggerImpl) WriteDebug(message string) {
|
func (logger *FileLoggerImpl) WriteDebug(message string) {
|
||||||
if DEBUG {
|
if DEBUG {
|
||||||
logger.Write(message)
|
logger.Write(message)
|
||||||
@@ -123,3 +131,23 @@ func (logger *FileLoggerImpl) WriteErrRequestDebug(err error, uuid string) (errn
|
|||||||
}
|
}
|
||||||
return errnum
|
return errnum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (logger *FileLoggerImpl) WriteErrMsgRequestDebug(err error, message string, uuid string) (errnum int) {
|
||||||
|
if err != nil {
|
||||||
|
if DEBUG {
|
||||||
|
logger.WriteErrMsgRequest(err, message, uuid)
|
||||||
|
}
|
||||||
|
errnum = 1
|
||||||
|
}
|
||||||
|
return errnum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *ConsoleLoggerImpl) WriteErrMsgRequestDebug(err error, message string, uuid string) (errnum int) {
|
||||||
|
if err != nil {
|
||||||
|
if DEBUG {
|
||||||
|
logger.WriteErrMsgRequest(err, message, uuid)
|
||||||
|
}
|
||||||
|
errnum = 1
|
||||||
|
}
|
||||||
|
return errnum
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ type (
|
|||||||
// If an error that is not nill passed in it logs the error and returns 1, otherwise 0
|
// If an error that is not nill passed in it logs the error and returns 1, otherwise 0
|
||||||
WriteErr(error) int
|
WriteErr(error) int
|
||||||
WriteErrRequest(err error, uuid string) int
|
WriteErrRequest(err error, uuid string) int
|
||||||
|
|
||||||
|
WriteErrMsgRequest(err error, message string, uuid string) int
|
||||||
}
|
}
|
||||||
// Use _DEBUG prints to strip them out of release builds
|
// Use _DEBUG prints to strip them out of release builds
|
||||||
DebugLogger interface {
|
DebugLogger interface {
|
||||||
@@ -38,6 +40,8 @@ type (
|
|||||||
WriteRequestDebug(message string, uuid string)
|
WriteRequestDebug(message string, uuid string)
|
||||||
WriteErrDebug(err error) (errnum int)
|
WriteErrDebug(err error) (errnum int)
|
||||||
WriteErrRequestDebug(err error, uuid string) int
|
WriteErrRequestDebug(err error, uuid string) int
|
||||||
|
|
||||||
|
WriteErrMsgRequestDebug(err error, message string, uuid string) int
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,4 +50,5 @@ var (
|
|||||||
_ Logger = (*NullLoggerImpl)(nil)
|
_ Logger = (*NullLoggerImpl)(nil)
|
||||||
_ Logger = (*ConsoleLoggerImpl)(nil)
|
_ Logger = (*ConsoleLoggerImpl)(nil)
|
||||||
_ Logger = (*FileLoggerImpl)(nil)
|
_ Logger = (*FileLoggerImpl)(nil)
|
||||||
|
_ Logger = (*SlogLoggerImpl)(nil)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ func (logger *NullLoggerImpl) StopLogger() {}
|
|||||||
|
|
||||||
func (logger *NullLoggerImpl) Write(message string) {}
|
func (logger *NullLoggerImpl) Write(message string) {}
|
||||||
|
|
||||||
func (logger *NullLoggerImpl) WriteRequest(message string, request string) {}
|
func (logger *NullLoggerImpl) WriteRequest(message string, uuid string) {}
|
||||||
|
|
||||||
func (logger *NullLoggerImpl) WriteErr(err error) (errnum int) {
|
func (logger *NullLoggerImpl) WriteErr(err error) (errnum int) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -17,7 +17,14 @@ func (logger *NullLoggerImpl) WriteErr(err error) (errnum int) {
|
|||||||
return errnum
|
return errnum
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *NullLoggerImpl) WriteErrRequest(err error, request string) (errnum int) {
|
func (logger *NullLoggerImpl) WriteErrRequest(err error, uuid string) (errnum int) {
|
||||||
|
if err != nil {
|
||||||
|
errnum = 1
|
||||||
|
}
|
||||||
|
return errnum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *NullLoggerImpl) WriteErrMsgRequest(err error, message string, uuid string) (errnum int) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errnum = 1
|
errnum = 1
|
||||||
}
|
}
|
||||||
@@ -41,3 +48,10 @@ func (logger *NullLoggerImpl) WriteErrRequestDebug(err error, uuid string) (errn
|
|||||||
}
|
}
|
||||||
return errnum
|
return errnum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (logger *NullLoggerImpl) WriteErrMsgRequestDebug(err error, message string, uuid string) (errnum int) {
|
||||||
|
if err != nil {
|
||||||
|
errnum = 1
|
||||||
|
}
|
||||||
|
return errnum
|
||||||
|
}
|
||||||
|
|||||||
83
logger/sloglogger.go
Normal file
83
logger/sloglogger.go
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package Logger
|
||||||
|
|
||||||
|
import "log/slog"
|
||||||
|
|
||||||
|
func (lgr *SlogLoggerImpl) init() {}
|
||||||
|
|
||||||
|
func (logger *SlogLoggerImpl) StartLogger() {}
|
||||||
|
|
||||||
|
func (logger *SlogLoggerImpl) StopLogger() {}
|
||||||
|
|
||||||
|
func (logger *SlogLoggerImpl) Write(message string) {
|
||||||
|
slog.Info(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *SlogLoggerImpl) WriteRequest(message string, uuid string) {
|
||||||
|
slog.Info(message, "UUID", uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *SlogLoggerImpl) WriteErr(err error) (errnum int) {
|
||||||
|
if err != nil {
|
||||||
|
slog.Error(err.Error())
|
||||||
|
errnum = 1
|
||||||
|
}
|
||||||
|
return errnum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *SlogLoggerImpl) WriteErrRequest(err error, uuid string) (errnum int) {
|
||||||
|
if err != nil {
|
||||||
|
slog.Error(err.Error(), "UUID", uuid)
|
||||||
|
errnum = 1
|
||||||
|
}
|
||||||
|
return errnum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *SlogLoggerImpl) WriteErrMsgRequest(err error, message string, uuid string) (errnum int) {
|
||||||
|
if err != nil {
|
||||||
|
slog.Error(message+err.Error(), "UUID", uuid)
|
||||||
|
errnum = 1
|
||||||
|
}
|
||||||
|
return errnum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *SlogLoggerImpl) WriteDebug(message string) {
|
||||||
|
if DEBUG {
|
||||||
|
logger.Write(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *SlogLoggerImpl) WriteRequestDebug(message string, uuid string) {
|
||||||
|
if DEBUG {
|
||||||
|
logger.WriteRequest(message, uuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *SlogLoggerImpl) WriteErrDebug(err error) (errnum int) {
|
||||||
|
if err != nil {
|
||||||
|
if DEBUG {
|
||||||
|
logger.WriteErr(err)
|
||||||
|
}
|
||||||
|
errnum = 1
|
||||||
|
}
|
||||||
|
return errnum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *SlogLoggerImpl) WriteErrRequestDebug(err error, uuid string) (errnum int) {
|
||||||
|
if err != nil {
|
||||||
|
if DEBUG {
|
||||||
|
logger.WriteErrRequest(err, uuid)
|
||||||
|
}
|
||||||
|
errnum = 1
|
||||||
|
}
|
||||||
|
return errnum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *SlogLoggerImpl) WriteErrMsgRequestDebug(err error, message string, uuid string) (errnum int) {
|
||||||
|
if err != nil {
|
||||||
|
if DEBUG {
|
||||||
|
logger.WriteErrMsgRequest(err, message, uuid)
|
||||||
|
}
|
||||||
|
errnum = 1
|
||||||
|
}
|
||||||
|
return errnum
|
||||||
|
}
|
||||||
@@ -20,3 +20,5 @@ type FileLoggerImpl struct {
|
|||||||
filepath string
|
filepath string
|
||||||
initfilepath string
|
initfilepath string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SlogLoggerImpl struct{}
|
||||||
|
|||||||
@@ -1,10 +1,20 @@
|
|||||||
package Type
|
package Type
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
// Created to abstract over Is_some and Is_ok
|
// Created to abstract over Is_some and Is_ok
|
||||||
type ValueContainer interface {
|
type ValueContainer interface {
|
||||||
HasValue() bool
|
HasValue() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Recoverable[T any] interface {
|
||||||
|
CatchUnwrap(T)
|
||||||
|
}
|
||||||
|
|
||||||
type Unwrappable[T any] interface {
|
type Unwrappable[T any] interface {
|
||||||
Expect(string) T // panics with a provided custom message
|
Expect(string) T // panics with a provided custom message
|
||||||
Unwrap() T // panics with a generic message
|
Unwrap() T // panics with a generic message
|
||||||
@@ -24,7 +34,8 @@ type Optionaler[T any] interface {
|
|||||||
IsNone() bool
|
IsNone() bool
|
||||||
OkOr(error) Result[T]
|
OkOr(error) Result[T]
|
||||||
OkOrElse(func() error) Result[T]
|
OkOrElse(func() error) Result[T]
|
||||||
Unwrappable[T]
|
Optioner[T]
|
||||||
|
OptionalerMarker
|
||||||
}
|
}
|
||||||
|
|
||||||
type Resulter[T any] interface {
|
type Resulter[T any] interface {
|
||||||
@@ -32,15 +43,36 @@ type Resulter[T any] interface {
|
|||||||
IsErr() bool
|
IsErr() bool
|
||||||
Ok() Optional[T]
|
Ok() Optional[T]
|
||||||
Err() Optional[error]
|
Err() Optional[error]
|
||||||
Unwrappable[T]
|
Optioner[T]
|
||||||
|
ResulterMarker
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Marker interfaces to help type matching
|
||||||
|
type (
|
||||||
|
ResulterMarker interface {
|
||||||
|
Result()
|
||||||
|
}
|
||||||
|
OptionalerMarker interface {
|
||||||
|
Optional()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Ensure compile time the interfaces are implemented
|
// Ensure compile time the interfaces are implemented
|
||||||
var (
|
var (
|
||||||
|
_ OptionalerMarker = (*Optional[any])(nil)
|
||||||
|
_ ResulterMarker = (*Result[any])(nil)
|
||||||
_ Optioner[any] = (*Optional[any])(nil)
|
_ Optioner[any] = (*Optional[any])(nil)
|
||||||
_ Optioner[any] = (*Result[any])(nil)
|
_ Optioner[any] = (*Result[any])(nil)
|
||||||
_ Optionaler[any] = (*Optional[any])(nil)
|
_ Optionaler[any] = (*Optional[any])(nil)
|
||||||
_ Resulter[any] = (*Result[any])(nil)
|
_ Resulter[any] = (*Result[any])(nil)
|
||||||
_ ValueContainer = (*Optional[any])(nil)
|
_ ValueContainer = (*Optional[any])(nil)
|
||||||
_ ValueContainer = (*Result[any])(nil)
|
_ ValueContainer = (*Result[any])(nil)
|
||||||
|
_ sql.Scanner = (*Optional[any])(nil)
|
||||||
|
_ sql.Scanner = (*Result[any])(nil)
|
||||||
|
_ driver.Valuer = (*Optional[any])(nil)
|
||||||
|
_ driver.Valuer = (*Result[any])(nil)
|
||||||
|
_ json.Marshaler = (*Optional[any])(nil)
|
||||||
|
_ json.Marshaler = (*Result[any])(nil)
|
||||||
|
_ json.Unmarshaler = (*Optional[any])(nil)
|
||||||
|
_ json.Unmarshaler = (*Result[any])(nil)
|
||||||
)
|
)
|
||||||
|
|||||||
235
type/optional.go
235
type/optional.go
@@ -1,6 +1,19 @@
|
|||||||
package Type
|
package Type
|
||||||
|
|
||||||
import Assert "github.com/lbatuska/goutils/assert"
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
Assert "github.com/lbatuska/goutils/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Marker interface impl
|
||||||
|
func (opt Optional[T]) Optional() {}
|
||||||
|
|
||||||
// CTORS BEGIN
|
// CTORS BEGIN
|
||||||
func Some[T any](value T) Optional[T] {
|
func Some[T any](value T) Optional[T] {
|
||||||
@@ -40,8 +53,7 @@ func (opt *Optional[T]) HasValue() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UNWRAPPABLE INTERFACE BEGIN
|
// UNWRAPPABLE INTERFACE BEGIN
|
||||||
func (opt *Optional[T]) Expect(msg string) T {
|
func (opt Optional[T]) Expect(msg string) T {
|
||||||
Assert.NotNil(opt)
|
|
||||||
if opt.present {
|
if opt.present {
|
||||||
return opt.value
|
return opt.value
|
||||||
}
|
}
|
||||||
@@ -49,11 +61,13 @@ func (opt *Optional[T]) Expect(msg string) T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (opt *Optional[T]) Unwrap() T {
|
func (opt *Optional[T]) Unwrap() T {
|
||||||
Assert.NotNil(opt)
|
if opt == nil {
|
||||||
|
panic("Tried unwrapping an Optional that did not have a value!")
|
||||||
|
}
|
||||||
if opt.present {
|
if opt.present {
|
||||||
return opt.value
|
return opt.value
|
||||||
}
|
}
|
||||||
panic("Tried unwrapping an Optional that did not have a value!")
|
panic(opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opt *Optional[T]) UnwrapOr(val T) T {
|
func (opt *Optional[T]) UnwrapOr(val T) T {
|
||||||
@@ -106,3 +120,214 @@ func (opt *Optional[T]) OkOrElse(f func() error) Result[T] {
|
|||||||
}
|
}
|
||||||
return Err[T](f())
|
return Err[T](f())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (opt *Optional[T]) Scan(src interface{}) error {
|
||||||
|
Assert.NotNil(opt)
|
||||||
|
opt.present = false
|
||||||
|
// DB had a null value
|
||||||
|
if src == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If T is a scanner (Scan is usually implemented on pointers so we need a pointer)
|
||||||
|
var scanner sql.Scanner = nil
|
||||||
|
var ok bool = false
|
||||||
|
if reflect.TypeOf(opt.value).Kind() == reflect.Ptr {
|
||||||
|
scanner, ok = any(opt.value).(sql.Scanner)
|
||||||
|
} else {
|
||||||
|
scanner, ok = any(&opt.value).(sql.Scanner)
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
if err := scanner.Scan(src); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
opt.present = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if scanres := opt.scanBuiltin(src); scanres.IsSome() {
|
||||||
|
return scanres.Unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Unsupported type %T or differs from Optional[%T], and the type doesn't implement sql.Scanner!", src, opt.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opt *Optional[T]) scanBuiltin(src interface{}) Optional[error] {
|
||||||
|
opt.present = false
|
||||||
|
// First handle the special cases where we allow conversion between types
|
||||||
|
// This is usually just parsing []byte into type
|
||||||
|
if scanres := opt.scanTimeSpecial(src); scanres.IsSome() {
|
||||||
|
return scanres
|
||||||
|
}
|
||||||
|
if scanres := opt.scanStringSpecial(src); scanres.IsSome() {
|
||||||
|
return scanres
|
||||||
|
}
|
||||||
|
|
||||||
|
srcVal := reflect.ValueOf(src)
|
||||||
|
optType := reflect.TypeOf(opt.value)
|
||||||
|
|
||||||
|
optElemType := optType
|
||||||
|
if optElemType.Kind() == reflect.Pointer {
|
||||||
|
optElemType = optElemType.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
srcElemType := srcVal.Type()
|
||||||
|
if srcElemType.Kind() == reflect.Pointer {
|
||||||
|
srcElemType = srcElemType.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if srcElemType != optElemType {
|
||||||
|
return Some(fmt.Errorf("Optional[%T] (aka %T) differs from %T!", opt.value, opt.value, src))
|
||||||
|
}
|
||||||
|
|
||||||
|
if optType.Kind() == reflect.Pointer {
|
||||||
|
if srcVal.Kind() == reflect.Pointer {
|
||||||
|
opt.value = srcVal.Interface().(T)
|
||||||
|
} else {
|
||||||
|
newPtr := reflect.New(optElemType)
|
||||||
|
newPtr.Elem().Set(srcVal)
|
||||||
|
opt.value = newPtr.Interface().(T)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if srcVal.Kind() == reflect.Pointer {
|
||||||
|
opt.value = srcVal.Elem().Interface().(T)
|
||||||
|
} else {
|
||||||
|
opt.value = srcVal.Interface().(T)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opt.present = true
|
||||||
|
return Some[error](nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opt *Optional[T]) scanStringSpecial(src interface{}) Optional[error] {
|
||||||
|
opt.present = false
|
||||||
|
switch v := any(opt.value).(type) {
|
||||||
|
case *string:
|
||||||
|
switch s := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
*v = string(s)
|
||||||
|
goto ok
|
||||||
|
case *[]byte:
|
||||||
|
*v = string(*s)
|
||||||
|
goto ok
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
switch s := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
reflect.ValueOf(&opt.value).Elem().Set(reflect.ValueOf(string(s)))
|
||||||
|
goto ok
|
||||||
|
case *[]byte:
|
||||||
|
reflect.ValueOf(&opt.value).Elem().Set(reflect.ValueOf(string(*s)))
|
||||||
|
goto ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None[error]()
|
||||||
|
ok:
|
||||||
|
opt.present = true
|
||||||
|
return Some[error](nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opt *Optional[T]) scanTimeSpecial(src interface{}) Optional[error] {
|
||||||
|
opt.present = false
|
||||||
|
switch v := any(opt.value).(type) {
|
||||||
|
case *time.Time:
|
||||||
|
switch t := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
parsedTime, err := time.Parse(time.RFC3339, string(t))
|
||||||
|
if err == nil {
|
||||||
|
*v = parsedTime
|
||||||
|
goto ok
|
||||||
|
} else {
|
||||||
|
return Some(err)
|
||||||
|
}
|
||||||
|
case *[]byte:
|
||||||
|
parsedTime, err := time.Parse(time.RFC3339, string(*t))
|
||||||
|
if err == nil {
|
||||||
|
*v = parsedTime
|
||||||
|
goto ok
|
||||||
|
} else {
|
||||||
|
return Some(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case time.Time:
|
||||||
|
switch t := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
parsedTime, err := time.Parse(time.RFC3339, string(t))
|
||||||
|
if err == nil {
|
||||||
|
reflect.ValueOf(&opt.value).Elem().Set(reflect.ValueOf(parsedTime))
|
||||||
|
goto ok
|
||||||
|
} else {
|
||||||
|
return Some(err)
|
||||||
|
}
|
||||||
|
case *[]byte:
|
||||||
|
parsedTime, err := time.Parse(time.RFC3339, string(*t))
|
||||||
|
if err == nil {
|
||||||
|
reflect.ValueOf(&opt.value).Elem().Set(reflect.ValueOf(parsedTime))
|
||||||
|
goto ok
|
||||||
|
} else {
|
||||||
|
return Some(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return None[error]()
|
||||||
|
ok:
|
||||||
|
opt.present = true
|
||||||
|
return Some[error](nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opt Optional[T]) MarshalJSON() ([]byte, error) {
|
||||||
|
if !opt.present {
|
||||||
|
// Return null for `omitempty` compatibility
|
||||||
|
return []byte("null"), nil
|
||||||
|
// panic("Tried to marshal an Optional that did not have a value!")
|
||||||
|
}
|
||||||
|
return json.Marshal(opt.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opt *Optional[T]) UnmarshalJSON(data []byte) error {
|
||||||
|
if string(data) == "null" {
|
||||||
|
opt.present = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var value T
|
||||||
|
if err := json.Unmarshal(data, &value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
opt.value = value
|
||||||
|
opt.present = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opt Optional[T]) Value() (driver.Value, error) {
|
||||||
|
if !opt.present {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if valuer, ok := any(opt.value).(driver.Valuer); ok {
|
||||||
|
return valuer.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := any(opt.Value).(type) {
|
||||||
|
|
||||||
|
case string, bool,
|
||||||
|
int, int8, int16, int32, int64,
|
||||||
|
uint, uint8, uint16, uint32, uint64, uintptr,
|
||||||
|
float32, float64,
|
||||||
|
complex64, complex128:
|
||||||
|
return v, nil
|
||||||
|
|
||||||
|
case *string, *bool,
|
||||||
|
*int, *int8, *int16, *int32, *int64,
|
||||||
|
*uint, *uint8, *uint16, *uint32, *uint64, *uintptr,
|
||||||
|
*float32, *float64,
|
||||||
|
*complex64, *complex128:
|
||||||
|
return v, nil
|
||||||
|
|
||||||
|
case fmt.Stringer:
|
||||||
|
return v.String(), nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unsupported type for Optional")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,6 +18,23 @@ var (
|
|||||||
nilOptional = (*Optional[int])(nil)
|
nilOptional = (*Optional[int])(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func Test_optionalScan(t *testing.T) {
|
||||||
|
a := None[string]()
|
||||||
|
b := None[string]()
|
||||||
|
c := Some("Not C!")
|
||||||
|
d := None[int]()
|
||||||
|
e := Some(Ptr("Not E!"))
|
||||||
|
a.Scan("a")
|
||||||
|
b.Scan("b")
|
||||||
|
c.Scan(Ptr("c"))
|
||||||
|
e.Scan(Ptr("e"))
|
||||||
|
Testing.AssertEqual(t, "a", a.Unwrap())
|
||||||
|
Testing.AssertEqual(t, "b", b.Unwrap())
|
||||||
|
Testing.AssertEqual(t, "c", c.Unwrap())
|
||||||
|
Testing.AssertPanic(t, func() { d.Unwrap() })
|
||||||
|
Testing.AssertEqual(t, "e", *e.Unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
func Test_optionalSome(t *testing.T) {
|
func Test_optionalSome(t *testing.T) {
|
||||||
Testing.AssertEqual(t, x, y)
|
Testing.AssertEqual(t, x, y)
|
||||||
Testing.AssertEqual(t, v, u)
|
Testing.AssertEqual(t, v, u)
|
||||||
|
|||||||
238
type/result.go
238
type/result.go
@@ -1,11 +1,20 @@
|
|||||||
package Type
|
package Type
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
Assert "github.com/lbatuska/goutils/assert"
|
Assert "github.com/lbatuska/goutils/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Marker interface impl
|
||||||
|
func (res Result[T]) Result() {}
|
||||||
|
|
||||||
// CTORS BEGIN
|
// CTORS BEGIN
|
||||||
func Ok[T any](value T) Result[T] {
|
func Ok[T any](value T) Result[T] {
|
||||||
return Result[T]{value: value, err: nil}
|
return Result[T]{value: value, err: nil}
|
||||||
@@ -43,8 +52,7 @@ func (res *Result[T]) HasValue() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UNWRAPPABLE INTERFACE
|
// UNWRAPPABLE INTERFACE
|
||||||
func (res *Result[T]) Expect(msg string) T {
|
func (res Result[T]) Expect(msg string) T {
|
||||||
Assert.NotNil(res)
|
|
||||||
if res.err == nil {
|
if res.err == nil {
|
||||||
return res.value
|
return res.value
|
||||||
}
|
}
|
||||||
@@ -52,11 +60,13 @@ func (res *Result[T]) Expect(msg string) T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (res *Result[T]) Unwrap() T {
|
func (res *Result[T]) Unwrap() T {
|
||||||
Assert.NotNil(res)
|
if res == nil {
|
||||||
|
panic("Tried unwrapping a Result that had an error value!")
|
||||||
|
}
|
||||||
if res.err == nil {
|
if res.err == nil {
|
||||||
return res.value
|
return res.value
|
||||||
}
|
}
|
||||||
panic("Tried unwrapping a Result that had an error value!")
|
panic(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (res *Result[T]) UnwrapOr(val T) T {
|
func (res *Result[T]) UnwrapOr(val T) T {
|
||||||
@@ -134,3 +144,223 @@ func (res *Result[T]) Err() Optional[error] {
|
|||||||
}
|
}
|
||||||
return Optional[error]{present: false}
|
return Optional[error]{present: false}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (res *Result[T]) Scan(src interface{}) error {
|
||||||
|
Assert.NotNil(res)
|
||||||
|
e := fmt.Errorf("Unsupported type %T or differs from Result[%T], and the type doesn't implement sql.Scanner!",
|
||||||
|
src, res.value)
|
||||||
|
res.err = e
|
||||||
|
// DB had a null value
|
||||||
|
if src == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If T is a scanner (Scan is usually implemented on pointers so we need a pointer)
|
||||||
|
var scanner sql.Scanner = nil
|
||||||
|
var ok bool = false
|
||||||
|
if reflect.TypeOf(res.value).Kind() == reflect.Ptr {
|
||||||
|
scanner, ok = any(res.value).(sql.Scanner)
|
||||||
|
} else {
|
||||||
|
scanner, ok = any(&res.value).(sql.Scanner)
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
if err := scanner.Scan(src); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res.err = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if scanres := res.scanBuiltin(src); scanres.IsSome() {
|
||||||
|
return scanres.Unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res *Result[T]) scanBuiltin(src interface{}) Optional[error] {
|
||||||
|
res.err = nil
|
||||||
|
// First handle the special cases where we allow conversion between types
|
||||||
|
// This is usually just parsing []byte into type
|
||||||
|
if scanres := res.scanTimeSpecial(src); scanres.IsSome() {
|
||||||
|
return scanres
|
||||||
|
}
|
||||||
|
if scanres := res.scanStringSpecial(src); scanres.IsSome() {
|
||||||
|
return scanres
|
||||||
|
}
|
||||||
|
|
||||||
|
srcVal := reflect.ValueOf(src)
|
||||||
|
optType := reflect.TypeOf(res.value)
|
||||||
|
|
||||||
|
optElemType := optType
|
||||||
|
if optElemType.Kind() == reflect.Pointer {
|
||||||
|
optElemType = optElemType.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
srcElemType := srcVal.Type()
|
||||||
|
if srcElemType.Kind() == reflect.Pointer {
|
||||||
|
srcElemType = srcElemType.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if srcElemType != optElemType {
|
||||||
|
e := fmt.Errorf("Result[%T] (aka %T) differs from %T!", res.value, res.value, src)
|
||||||
|
res.err = e
|
||||||
|
return Some(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if optType.Kind() == reflect.Pointer {
|
||||||
|
if srcVal.Kind() == reflect.Pointer {
|
||||||
|
res.value = srcVal.Interface().(T)
|
||||||
|
} else {
|
||||||
|
newPtr := reflect.New(optElemType)
|
||||||
|
newPtr.Elem().Set(srcVal)
|
||||||
|
res.value = newPtr.Interface().(T)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if srcVal.Kind() == reflect.Pointer {
|
||||||
|
res.value = srcVal.Elem().Interface().(T)
|
||||||
|
} else {
|
||||||
|
res.value = srcVal.Interface().(T)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.err = nil
|
||||||
|
return Some[error](nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res *Result[T]) scanStringSpecial(src interface{}) Optional[error] {
|
||||||
|
switch v := any(res.value).(type) {
|
||||||
|
case *string:
|
||||||
|
switch s := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
*v = string(s)
|
||||||
|
goto ok
|
||||||
|
case *[]byte:
|
||||||
|
*v = string(*s)
|
||||||
|
goto ok
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
switch s := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
reflect.ValueOf(&res.value).Elem().Set(reflect.ValueOf(string(s)))
|
||||||
|
goto ok
|
||||||
|
case *[]byte:
|
||||||
|
reflect.ValueOf(&res.value).Elem().Set(reflect.ValueOf(string(*s)))
|
||||||
|
goto ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None[error]()
|
||||||
|
ok:
|
||||||
|
res.err = nil
|
||||||
|
return Some[error](nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res *Result[T]) scanTimeSpecial(src interface{}) Optional[error] {
|
||||||
|
switch v := any(res.value).(type) {
|
||||||
|
case *time.Time:
|
||||||
|
switch t := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
parsedTime, err := time.Parse(time.RFC3339, string(t))
|
||||||
|
if err == nil {
|
||||||
|
*v = parsedTime
|
||||||
|
goto ok
|
||||||
|
} else {
|
||||||
|
res.err = err
|
||||||
|
return Some(err)
|
||||||
|
}
|
||||||
|
case *[]byte:
|
||||||
|
parsedTime, err := time.Parse(time.RFC3339, string(*t))
|
||||||
|
if err == nil {
|
||||||
|
*v = parsedTime
|
||||||
|
goto ok
|
||||||
|
} else {
|
||||||
|
res.err = err
|
||||||
|
return Some(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case time.Time:
|
||||||
|
switch t := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
parsedTime, err := time.Parse(time.RFC3339, string(t))
|
||||||
|
if err == nil {
|
||||||
|
reflect.ValueOf(&res.value).Elem().Set(reflect.ValueOf(parsedTime))
|
||||||
|
goto ok
|
||||||
|
} else {
|
||||||
|
res.err = err
|
||||||
|
return Some(err)
|
||||||
|
}
|
||||||
|
case *[]byte:
|
||||||
|
parsedTime, err := time.Parse(time.RFC3339, string(*t))
|
||||||
|
if err == nil {
|
||||||
|
reflect.ValueOf(&res.value).Elem().Set(reflect.ValueOf(parsedTime))
|
||||||
|
goto ok
|
||||||
|
} else {
|
||||||
|
res.err = err
|
||||||
|
return Some(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return None[error]()
|
||||||
|
ok:
|
||||||
|
res.err = nil
|
||||||
|
return Some[error](nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res Result[T]) MarshalJSON() ([]byte, error) {
|
||||||
|
if nil != res.err {
|
||||||
|
// Return null for `omitempty` compatibility
|
||||||
|
return []byte("null"), nil
|
||||||
|
// panic("Tried to marshal a Result that was error!")
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(res.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res *Result[T]) UnmarshalJSON(data []byte) error {
|
||||||
|
var value T
|
||||||
|
res.err = nil
|
||||||
|
if string(data) == "null" {
|
||||||
|
// On null data the best we can do is indicate there was no error and put a default value of T
|
||||||
|
res.value = value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(data, &value); err != nil {
|
||||||
|
res.err = err
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res.value = value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res Result[T]) Value() (driver.Value, error) {
|
||||||
|
if nil != res.err {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if valuer, ok := any(res.value).(driver.Valuer); ok {
|
||||||
|
return valuer.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := any(res.Value).(type) {
|
||||||
|
|
||||||
|
case string, bool,
|
||||||
|
int, int8, int16, int32, int64,
|
||||||
|
uint, uint8, uint16, uint32, uint64, uintptr,
|
||||||
|
float32, float64,
|
||||||
|
complex64, complex128:
|
||||||
|
return v, nil
|
||||||
|
|
||||||
|
case *string, *bool,
|
||||||
|
*int, *int8, *int16, *int32, *int64,
|
||||||
|
*uint, *uint8, *uint16, *uint32, *uint64, *uintptr,
|
||||||
|
*float32, *float64,
|
||||||
|
*complex64, *complex128:
|
||||||
|
return v, nil
|
||||||
|
|
||||||
|
case fmt.Stringer:
|
||||||
|
return v.String(), nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unsupported type for Result")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,6 +15,21 @@ var (
|
|||||||
nilResult = (*Result[int])(nil)
|
nilResult = (*Result[int])(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func Test_resultScan(t *testing.T) {
|
||||||
|
a := Ok("Not A!")
|
||||||
|
b := Ok(0)
|
||||||
|
c := Ok(0)
|
||||||
|
d := Err[*string](errors.New(""))
|
||||||
|
a.Scan("a")
|
||||||
|
b.Scan(1)
|
||||||
|
c.Scan("c")
|
||||||
|
d.Scan(Ptr("d"))
|
||||||
|
Testing.AssertEqual(t, a.Unwrap(), "a")
|
||||||
|
Testing.AssertEqual(t, b.Unwrap(), 1)
|
||||||
|
Testing.AssertError(t, c.UnwrapErr())
|
||||||
|
Testing.AssertEqual(t, *d.Unwrap(), "d")
|
||||||
|
}
|
||||||
|
|
||||||
func Test_resultConstructors(t *testing.T) {
|
func Test_resultConstructors(t *testing.T) {
|
||||||
err := errors.New("some error")
|
err := errors.New("some error")
|
||||||
w := Err[int](err)
|
w := Err[int](err)
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
package Type
|
package Type
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
func Expect[T any](val Unwrappable[T], msg string) T {
|
func Expect[T any](val Unwrappable[T], msg string) T {
|
||||||
return val.Expect(msg)
|
return val.Expect(msg)
|
||||||
}
|
}
|
||||||
@@ -42,3 +48,92 @@ func ResultWrapb[T any](err error, val T) Result[T] {
|
|||||||
func Ptr[T any](v T) *T {
|
func Ptr[T any](v T) *T {
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// meant to be used as defer Type.CatchUnwrap(Type.Ptr(&res)) or Type.CatchUnwrap(&res) if res is already a pointer
|
||||||
|
// where res is a pointer to an Option or Result returned by a function (initialized to not be nil)
|
||||||
|
// func X() (res *Optional[int]) {
|
||||||
|
// res = None[int]()
|
||||||
|
// defer CatchUnwrap(&res)
|
||||||
|
//
|
||||||
|
// // Some possibly unsafe unwrapping of values
|
||||||
|
// return res
|
||||||
|
// }
|
||||||
|
// === OR ===
|
||||||
|
// func X() (res Optional[int]) {
|
||||||
|
// res = None[int]()
|
||||||
|
// defer Type.CatchUnwrap(Type.Ptr(&res))
|
||||||
|
//
|
||||||
|
// // Some possibly unsafe unwrapping of values
|
||||||
|
// return res
|
||||||
|
// }
|
||||||
|
|
||||||
|
func CatchUnwrap(ret interface{}) {
|
||||||
|
r := recover()
|
||||||
|
if r == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vp := reflect.ValueOf(r)
|
||||||
|
if vp.Kind() != reflect.Pointer || vp.IsNil() {
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := r.(OptionalerMarker); ok {
|
||||||
|
if setOptionalNone(ret) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := r.(ResulterMarker); ok {
|
||||||
|
if setResultError(ret, fmt.Errorf("Tried to unwrap a failed result!")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setOptionalNone(ret interface{}) bool {
|
||||||
|
v := reflect.ValueOf(ret)
|
||||||
|
if v.Kind() != reflect.Ptr || v.IsNil() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
elem := v.Elem().Elem()
|
||||||
|
if elem.Kind() != reflect.Struct {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
presentField := elem.FieldByName("present")
|
||||||
|
if !presentField.IsValid() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if presentField.Kind() == reflect.Bool {
|
||||||
|
presentField = reflect.NewAt(presentField.Type(),
|
||||||
|
unsafe.Pointer(presentField.UnsafeAddr())).Elem()
|
||||||
|
presentField.SetBool(false) // Setting it to false (None)
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func setResultError(ret interface{}, err error) bool {
|
||||||
|
v := reflect.ValueOf(ret)
|
||||||
|
if v.Kind() != reflect.Ptr || v.IsNil() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
elem := v.Elem().Elem()
|
||||||
|
if elem.Kind() != reflect.Struct {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
errField := elem.FieldByName("err")
|
||||||
|
if !errField.IsValid() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if errField.Kind() == reflect.Interface {
|
||||||
|
errField = reflect.NewAt(errField.Type(),
|
||||||
|
unsafe.Pointer(errField.UnsafeAddr())).Elem()
|
||||||
|
errField.Set(reflect.ValueOf(err)) // Setting the error
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
@@ -66,9 +66,9 @@ func (rg *RouteGroup) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
rg.mux.ServeHTTP(w, r)
|
rg.mux.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
|
|
||||||
mwlen := len(rg.global_middlewares) - 1
|
mwlen := len(*rg.global_middlewares) - 1
|
||||||
for i := range rg.global_middlewares {
|
for i := range *rg.global_middlewares {
|
||||||
handler = rg.global_middlewares[mwlen-i](handler)
|
handler = (*rg.global_middlewares)[mwlen-i](handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.ServeHTTP(w, r)
|
handler.ServeHTTP(w, r)
|
||||||
@@ -81,7 +81,7 @@ func (rg *RouteGroup) Handler(r *http.Request) (h http.Handler, pattern string)
|
|||||||
|
|
||||||
// Creates a new SimpleRouter with the basePath of "/"
|
// Creates a new SimpleRouter with the basePath of "/"
|
||||||
func SimpleRouter() *RouteGroup {
|
func SimpleRouter() *RouteGroup {
|
||||||
return &RouteGroup{mux: http.NewServeMux(), basePath: "/"}
|
return &RouteGroup{mux: http.NewServeMux(), basePath: "/", global_middlewares: &[]Middleware{}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Puts middlewares on top of existing ones in order
|
// Puts middlewares on top of existing ones in order
|
||||||
@@ -105,20 +105,21 @@ func (rg *RouteGroup) PopMiddleware() Middleware {
|
|||||||
return lastMiddleware
|
return lastMiddleware
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this vs PushMiddleware ? what's the diff?
|
||||||
func (rg *RouteGroup) PushGlobalMiddleware(first Middleware, others ...Middleware) *RouteGroup {
|
func (rg *RouteGroup) PushGlobalMiddleware(first Middleware, others ...Middleware) *RouteGroup {
|
||||||
rg.global_middlewares = append(rg.global_middlewares, first)
|
*rg.global_middlewares = append(*rg.global_middlewares, first)
|
||||||
rg.global_middlewares = append(rg.global_middlewares, others...)
|
*rg.global_middlewares = append(*rg.global_middlewares, others...)
|
||||||
return rg
|
return rg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rg *RouteGroup) PopGlobalMiddleware() Middleware {
|
func (rg *RouteGroup) PopGlobalMiddleware() Middleware {
|
||||||
if len(rg.global_middlewares) == 0 {
|
if len(*rg.global_middlewares) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
lastIndex := len(rg.global_middlewares) - 1
|
lastIndex := len(*rg.global_middlewares) - 1
|
||||||
lastMiddleware := rg.global_middlewares[lastIndex]
|
lastMiddleware := (*rg.global_middlewares)[lastIndex]
|
||||||
rg.global_middlewares = rg.global_middlewares[:lastIndex]
|
*rg.global_middlewares = (*rg.global_middlewares)[:lastIndex]
|
||||||
|
|
||||||
return lastMiddleware
|
return lastMiddleware
|
||||||
}
|
}
|
||||||
@@ -148,6 +149,7 @@ func (rg *RouteGroup) SubPath(path string) *RouteGroup {
|
|||||||
rgc := &RouteGroup{
|
rgc := &RouteGroup{
|
||||||
mux: rg.mux,
|
mux: rg.mux,
|
||||||
basePath: rg.basePath + path + "/",
|
basePath: rg.basePath + path + "/",
|
||||||
|
global_middlewares: rg.global_middlewares,
|
||||||
}
|
}
|
||||||
middlewares := make([]Middleware, len(rg.middlewares))
|
middlewares := make([]Middleware, len(rg.middlewares))
|
||||||
copy(middlewares, rg.middlewares)
|
copy(middlewares, rg.middlewares)
|
||||||
|
|||||||
@@ -192,12 +192,24 @@ func Test_globalMiddlewares(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
simpleRouter := SimpleRouter()
|
simpleRouter := SimpleRouter()
|
||||||
|
simpleRouter2 := simpleRouter.SubPath("2")
|
||||||
|
|
||||||
simpleRouter.PushGlobalMiddleware(gmw1)
|
simpleRouter.PushGlobalMiddleware(gmw1)
|
||||||
simpleRouter.PushGlobalMiddleware(gmw2)
|
simpleRouter.PushGlobalMiddleware(gmw2)
|
||||||
simpleRouter.PushMiddleware(mw1, mw2)
|
simpleRouter.PushMiddleware(mw1, mw2)
|
||||||
simpleRouter.GET("test", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
simpleRouter.GET("test", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||||
simpleRouter.GET("test2", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
simpleRouter.GET("test2", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||||
|
|
||||||
|
// Subpath
|
||||||
|
simpleRouter2.GET("test", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||||
|
_ts := httptest.NewServer(simpleRouter2)
|
||||||
|
_req, _ := http.NewRequest("GET", _ts.URL+"/2/test", nil)
|
||||||
|
_, _err := http.DefaultClient.Do(_req)
|
||||||
|
Testing.AssertNotError(t, _err)
|
||||||
|
Testing.AssertEqual(t, 12, counter)
|
||||||
|
counter = 0
|
||||||
|
// Subpath
|
||||||
|
|
||||||
ts := httptest.NewServer(simpleRouter)
|
ts := httptest.NewServer(simpleRouter)
|
||||||
req, _ := http.NewRequest("GET", ts.URL+"/test", nil)
|
req, _ := http.NewRequest("GET", ts.URL+"/test", nil)
|
||||||
_, err := http.DefaultClient.Do(req)
|
_, err := http.DefaultClient.Do(req)
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ type RouteGroup struct {
|
|||||||
mux *http.ServeMux // A ponter to the underlying ServeMux, this allows us to call ListenAndServe on any instance
|
mux *http.ServeMux // A ponter to the underlying ServeMux, this allows us to call ListenAndServe on any instance
|
||||||
basePath string // The current path we are defining handlers on / appending to
|
basePath string // The current path we are defining handlers on / appending to
|
||||||
middlewares []Middleware // Stack of middlewares that will be applied on a handler in order
|
middlewares []Middleware // Stack of middlewares that will be applied on a handler in order
|
||||||
global_middlewares []Middleware
|
global_middlewares *[]Middleware
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user