diff --git a/acl/go.mod b/acl/go.mod index 4a2525b..96decde 100644 --- a/acl/go.mod +++ b/acl/go.mod @@ -9,5 +9,5 @@ replace ( require ( go.osspkg.com/goppy/errors v0.1.0 - go.osspkg.com/goppy/xtest v0.1.2 + go.osspkg.com/goppy/xtest v0.1.3 ) diff --git a/app/application.go b/app/application.go index 7dfbbad..4cd29f4 100644 --- a/app/application.go +++ b/app/application.go @@ -7,6 +7,7 @@ package app import ( "go.osspkg.com/goppy/console" + "go.osspkg.com/goppy/iofile" "go.osspkg.com/goppy/syscall" "go.osspkg.com/goppy/xc" "go.osspkg.com/goppy/xlog" @@ -31,8 +32,8 @@ type ( pidfile string configs Modules modules Modules - sources Sources - packages *_dic + sources iofile.FileCodec + packages Container logout *_log log xlog.Logger ctx xc.Context @@ -46,7 +47,7 @@ func New() App { return &_app{ modules: Modules{}, configs: Modules{}, - packages: newDic(ctx), + packages: NewContainer(ctx), ctx: ctx, exitFunc: func(_ int) {}, } @@ -104,7 +105,7 @@ func (a *_app) Run() { }, { Message: "Running dependencies", - Call: func() error { return a.packages.Build() }, + Call: func() error { return a.packages.Start() }, }, }, func(er bool) { @@ -118,7 +119,7 @@ func (a *_app) Run() { []step{ { Message: "Stop dependencies", - Call: func() error { return a.packages.Down() }, + Call: func() error { return a.packages.Stop() }, }, }, ) @@ -138,6 +139,9 @@ func (a *_app) Invoke(call interface{}) { { Call: func() error { return a.packages.Register(a.modules...) }, }, + { + Call: func() error { return a.packages.Start() }, + }, { Call: func() error { return a.packages.Invoke(call) }, }, @@ -145,7 +149,7 @@ func (a *_app) Invoke(call interface{}) { func(_ bool) {}, []step{ { - Call: func() error { return a.packages.Down() }, + Call: func() error { return a.packages.Stop() }, }, }, ) @@ -170,7 +174,7 @@ func (a *_app) prepareConfig(interactive bool) { } if len(a.cfile) > 0 { // read config file - a.sources = Sources(a.cfile) + a.sources = iofile.FileCodec(a.cfile) // init logger config := &Config{} @@ -191,7 +195,7 @@ func (a *_app) prepareConfig(interactive bool) { ) // decode all configs var configs []interface{} - configs, err = typingRefPtr(a.configs, func(i interface{}) error { + configs, err = typingReflectPtr(a.configs, func(i interface{}) error { return a.sources.Decode(i) }) if err != nil { diff --git a/app/container.go b/app/container.go index 02e12aa..98f4d1c 100644 --- a/app/container.go +++ b/app/container.go @@ -8,409 +8,271 @@ package app import ( "fmt" "reflect" - "sync" "go.osspkg.com/algorithms/graph/kahn" "go.osspkg.com/goppy/errors" + "go.osspkg.com/goppy/iosync" "go.osspkg.com/goppy/xc" ) -type _dic struct { - kahn *kahn.Graph - srv *_serv - list *dicMap -} +type ( + container struct { + kahn *kahn.Graph + srv *serviceTree + store *objectStorage + status iosync.Switch + } + + Container interface { + Start() error + Register(items ...interface{}) error + Invoke(item interface{}) error + Stop() error + } +) -func newDic(ctx xc.Context) *_dic { - return &_dic{ - kahn: kahn.New(), - srv: newService(ctx), - list: newDicMap(), +func NewContainer(ctx xc.Context) Container { + return &container{ + kahn: kahn.New(), + srv: newServiceTree(ctx), + store: newObjectStorage(), + status: iosync.NewSwitch(), } } -// Down - stop all services in dependencies -func (v *_dic) Down() error { +// Stop - stop all services in dependencies +func (v *container) Stop() error { + if !v.status.Off() { + return nil + } return v.srv.Down() } -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Start - initialize dependencies and start +func (v *container) Start() error { + if !v.status.On() { + return errDepAlreadyRunned + } + if err := v.srv.MakeAsUp(); err != nil { + return err + } + if err := v.prepare(); err != nil { + return err + } + if err := v.kahn.Build(); err != nil { + return errors.Wrapf(err, "dependency graph calculation") + } + return v.run() +} -// Register - register a new dependency -func (v *_dic) Register(items ...interface{}) error { - if v.srv.IsUp() { - return errDepBuilderNotRunning +func (v *container) Register(items ...interface{}) error { + if v.srv.IsOn() { + return errDepAlreadyRunned } for _, item := range items { ref := reflect.TypeOf(item) + rt := asTypeExist switch ref.Kind() { + case reflect.Func, reflect.Struct: + rt = asTypeNew + default: + } + if err := v.store.Add(ref, item, rt); err != nil { + return err + } + } + return nil +} - case reflect.Struct: - if err := v.list.Add(item, item, typeExist); err != nil { - return err - } +var root = "ROOT" - case reflect.Func: - for i := 0; i < ref.NumIn(); i++ { - in := ref.In(i) - if in.Kind() == reflect.Struct { - if err := v.list.Add(in, reflect.New(in).Elem().Interface(), typeNewIfNotExist); err != nil { - return err - } - } +func (v *container) prepare() error { + return v.store.Each(func(item *objectStorageItem) error { + + switch item.Kind { + case reflect.Func: + if item.ReflectType.NumIn() == 0 { + v.kahn.Add(root, item.Address) + break } - if ref.NumOut() == 0 { - if err := v.list.Add(ref, item, typeNew); err != nil { - return err - } - continue + for i := 0; i < item.ReflectType.NumIn(); i++ { + inRefType := item.ReflectType.In(i) + inAddress, _ := getReflectAddress(inRefType, nil) + v.kahn.Add(inAddress, item.Address) } - for i := 0; i < ref.NumOut(); i++ { - if err := v.list.Add(ref.Out(i), item, typeNew); err != nil { - return err - } + + case reflect.Struct: + if item.ReflectType.NumField() == 0 { + v.kahn.Add(root, item.Address) + break + } + for i := 0; i < item.ReflectType.NumField(); i++ { + inRefType := item.ReflectType.Field(i).Type + inAddress, _ := getReflectAddress(inRefType, nil) + v.kahn.Add(inAddress, item.Address) } default: - if err := v.list.Add(item, item, typeExist); err != nil { - return err - } + v.kahn.Add(root, item.Address) } - } - return nil + return nil + }) } -// Build - initialize dependencies -func (v *_dic) Build() error { - if err := v.srv.MakeAsUp(); err != nil { - return err +func (v *container) Invoke(obj interface{}) error { + if v.srv.IsOff() { + return errDepNotRunning } - - err := v.list.foreach(v.calcFunc, v.calcStruct, v.calcOther) + item, err := v.toStoreItem(obj) if err != nil { - return errors.Wrapf(err, "building dependency graph") - } - - if err = v.kahn.Build(); err != nil { - return errors.Wrapf(err, "dependency graph calculation") - } - - return v.exec() -} - -// Inject - obtained dependence -func (v *_dic) Inject(item interface{}) error { - _, err := v.callArgs(item) - return err -} - -// Invoke - obtained dependence -func (v *_dic) Invoke(item interface{}) error { - ref := reflect.TypeOf(item) - addr, ok := getRefAddr(ref) - if !ok { - return fmt.Errorf("resolve invoke reference") - } - - if err := v.Register(item); err != nil { - return err - } - - if err := v.srv.MakeAsUp(); err != nil { return err } - - err := v.list.foreach(v.calcFunc, v.calcStruct, v.calcOther) + _, err = v.callArgs(item) if err != nil { - return errors.Wrapf(err, "building dependency graph") - } - - v.kahn.BreakPoint(addr) - - if err = v.kahn.Build(); err != nil { - return errors.Wrapf(err, "dependency graph calculation") - } - - return v.exec() -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -var empty = "EMPTY" - -func (v *_dic) calcFunc(outAddr string, outRef reflect.Type) error { - if outRef.NumIn() == 0 { - if err := v.kahn.Add(empty, outAddr); err != nil { - return errors.Wrapf(err, "cant add [->%s] to graph", outAddr) - } + return err } - - for i := 0; i < outRef.NumIn(); i++ { - inRef := outRef.In(i) - inAddr, _ := getRefAddr(inRef) - - //TODO: need? - //if _, err := v.list.Get(inAddr); err != nil { - // return errors.Wrapf(err, "cant add [%s->%s] to graph", inAddr, outAddr) - //} - if err := v.kahn.Add(inAddr, outAddr); err != nil { - return errors.Wrapf(err, "cant add [%s->%s] to graph", inAddr, outAddr) + if item.Service == itDownService { + if err = v.srv.AddAndUp(item.Value); err != nil { + return err } } - return nil } -func (v *_dic) calcStruct(outAddr string, outRef reflect.Type) error { - if outRef.NumField() == 0 { - if err := v.kahn.Add(empty, outAddr); err != nil { - return errors.Wrapf(err, "cant add [->%s] to graph", outAddr) - } - return nil +func (v *container) toStoreItem(obj interface{}) (*objectStorageItem, error) { + item, ok := obj.(*objectStorageItem) + if ok { + return item, nil } - for i := 0; i < outRef.NumField(); i++ { - inRef := outRef.Field(i).Type - inAddr, _ := getRefAddr(inRef) - - //TODO: need? - //if _, err := v.list.Get(inAddr); err != nil { - // return errors.Wrapf(err, "cant add [%s->%s] to graph", inAddr, outAddr) - //} - if err := v.kahn.Add(inAddr, outAddr); err != nil { - return errors.Wrapf(err, "cant add [%s->%s] to graph", inAddr, outAddr) - } + ref := reflect.TypeOf(obj) + address, ok := getReflectAddress(ref, obj) + if !ok { + return nil, fmt.Errorf("dependency [%s] is not supported", address) } - return nil -} - -func (v *_dic) calcOther(_ string, _ reflect.Type) error { - return nil -} - -func (v *_dic) callFunc(item interface{}) ([]reflect.Value, error) { - ref := reflect.TypeOf(item) - args := make([]reflect.Value, 0, ref.NumIn()) - - for i := 0; i < ref.NumIn(); i++ { - inRef := ref.In(i) - inAddr, _ := getRefAddr(inRef) - vv, err := v.list.Get(inAddr) - if err != nil { - return nil, err - } - args = append(args, reflect.ValueOf(vv)) + serviceStatus := itNotService + if isService(obj) { + serviceStatus = itDownService } - - args = reflect.ValueOf(item).Call(args) - for _, arg := range args { - if err, ok := arg.Interface().(error); ok && err != nil { - return nil, err - } + item = &objectStorageItem{ + Address: address, + RelationType: 0, + ReflectType: ref, + Kind: ref.Kind(), + Value: obj, + Service: serviceStatus, } - - return args, nil + return item, nil } -func (v *_dic) callStruct(item interface{}) ([]reflect.Value, error) { - ref := reflect.TypeOf(item) - value := reflect.New(ref) - args := make([]reflect.Value, 0, ref.NumField()) - - for i := 0; i < ref.NumField(); i++ { - inRef := ref.Field(i) - inAddr, _ := getRefAddr(inRef.Type) - vv, err := v.list.Get(inAddr) - if err != nil { - return nil, err - } - value.Elem().FieldByName(inRef.Name).Set(reflect.ValueOf(vv)) +func (v *container) callArgs(obj interface{}) ([]reflect.Value, error) { + item, err := v.toStoreItem(obj) + if err != nil { + return nil, err } - return append(args, value.Elem()), nil -} - -func (v *_dic) callArgs(item interface{}) ([]reflect.Value, error) { - ref := reflect.TypeOf(item) + switch item.Kind { - switch ref.Kind() { case reflect.Func: - return v.callFunc(item) + args := make([]reflect.Value, 0, item.ReflectType.NumIn()) + for i := 0; i < item.ReflectType.NumIn(); i++ { + inRefType := item.ReflectType.In(i) + inAddress, ok := getReflectAddress(inRefType, item.Value) + if !ok { + return nil, fmt.Errorf("dependency [%s] is not supported", inAddress) + } + dep, err := v.store.Get(inAddress) + if err != nil { + return nil, err + } + args = append(args, reflect.ValueOf(dep.Value)) + } + args = reflect.ValueOf(item.Value).Call(args) + for _, arg := range args { + if err, ok := arg.Interface().(error); ok && err != nil { + return nil, err + } + } + return args, nil + case reflect.Struct: - return v.callStruct(item) + value := reflect.New(item.ReflectType) + args := make([]reflect.Value, 0, 1) + for i := 0; i < item.ReflectType.NumField(); i++ { + inRefType := item.ReflectType.Field(i) + inAddress, ok := getReflectAddress(inRefType.Type, nil) + if !ok { + return nil, fmt.Errorf("dependency [%s] is not supported", inAddress) + } + dep, err := v.store.Get(inAddress) + if err != nil { + return nil, err + } + value.Elem().FieldByName(inRefType.Name).Set(reflect.ValueOf(dep.Value)) + } + return append(args, value.Elem()), nil + default: - return []reflect.Value{reflect.ValueOf(item)}, nil } + + return []reflect.Value{reflect.ValueOf(item.Value)}, nil } -func (v *_dic) exec() error { +// nolint: gocyclo +func (v *container) run() error { names := make(map[string]struct{}) for _, name := range v.kahn.Result() { - if name == empty { + if name == root { continue } names[name] = struct{}{} } for _, name := range v.kahn.Result() { - if _, ok := names[name]; !ok { - continue - } - if v.list.HasType(name, typeExist) { + if name == root { continue } - - item, err := v.list.Get(name) + item, err := v.store.Get(name) if err != nil { return err } - + if item.RelationType == asTypeExist { + if item.Service == itDownService { + if err = v.srv.AddAndUp(item.Value); err != nil { + return errors.Wrapf(err, "service initialization error [%s]", item.Address) + } + item.Service = itUpedService + } + delete(names, name) + continue + } args, err := v.callArgs(item) if err != nil { return errors.Wrapf(err, "initialize error [%s]", name) } - for _, arg := range args { - addr, _ := getRefAddr(arg.Type()) - if vv, ok := asService(arg); ok { - if err = v.srv.AddAndRun(vv); err != nil { - return errors.Wrapf(err, "service initialization error [%s]", addr) + address, ok := getReflectAddress(arg.Type(), arg.Interface()) + if !ok { + if address == "error" { + continue } + return fmt.Errorf("dependency [%s] is not supported form [%s]", address, item.Address) } - if vv, ok := asServiceContext(arg); ok { - if err = v.srv.AddAndRun(vv); err != nil { - return errors.Wrapf(err, "service initialization error [%s]", addr) + if isService(arg.Interface()) { + if err = v.srv.AddAndUp(arg.Interface()); err != nil { + return errors.Wrapf(err, "service initialization error [%s]", address) } } - delete(names, addr) - if arg.Type().String() == "error" { - continue - } - if err = v.list.Add(arg.Type(), arg.Interface(), typeExist); err != nil { - return errors.Wrapf(err, "initialize error [%s]", addr) + delete(names, address) + if err = v.store.Add(arg.Type(), arg.Interface(), asTypeExist); err != nil { + return errors.Wrapf(err, "initialize error [%s]", address) } } delete(names, name) } v.srv.IterateOver() - - return nil -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -const ( - typeNew int = iota - typeNewIfNotExist - typeExist -) - -type ( - dicMapItem struct { - Value interface{} - Type int - } - dicMap struct { - data map[string]*dicMapItem - mux sync.RWMutex - } -) - -func newDicMap() *dicMap { - return &dicMap{ - data: make(map[string]*dicMapItem), - } -} - -func (v *dicMap) Add(place, value interface{}, t int) error { - v.mux.Lock() - defer v.mux.Unlock() - - ref, ok := place.(reflect.Type) - if !ok { - ref = reflect.TypeOf(place) - } - - addr, ok := getRefAddr(ref) - if !ok { - if addr != "error" { - return fmt.Errorf("dependency [%s] is not supported", addr) - } - //return nil - } - - if vv, ok := v.data[addr]; ok { - if t == typeNewIfNotExist { - return nil - } - if vv.Type == typeExist { - return fmt.Errorf("dependency [%s] already initiated", addr) - } - } - v.data[addr] = &dicMapItem{ - Value: value, - Type: t, - } - - return nil -} - -func (v *dicMap) Get(addr string) (interface{}, error) { - v.mux.RLock() - defer v.mux.RUnlock() - - if vv, ok := v.data[addr]; ok { - return vv.Value, nil - } - return nil, fmt.Errorf("dependency [%s] not initiated", addr) -} - -func (v *dicMap) HasType(addr string, t int) bool { - v.mux.RLock() - defer v.mux.RUnlock() - - if vv, ok := v.data[addr]; ok { - return vv.Type == t - } - return false -} - -func (v *dicMap) Step(addr string) (int, error) { - v.mux.RLock() - defer v.mux.RUnlock() - - if vv, ok := v.data[addr]; ok { - return vv.Type, nil - } - return 0, fmt.Errorf("dependency [%s] not initiated", addr) -} - -func (v *dicMap) foreach(kFunc, kStruct, kOther func(addr string, ref reflect.Type) error) error { - v.mux.RLock() - defer v.mux.RUnlock() - - for addr, item := range v.data { - if item.Type == typeExist { - continue - } - - ref := reflect.TypeOf(item.Value) - var err error - switch ref.Kind() { - case reflect.Func: - err = kFunc(addr, ref) - case reflect.Struct: - err = kStruct(addr, ref) - default: - err = kOther(addr, ref) - } - - if err != nil { - return err - } - } return nil } diff --git a/app/container_test.go b/app/container_test.go index a590824..0f4eccd 100644 --- a/app/container_test.go +++ b/app/container_test.go @@ -3,210 +3,344 @@ * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ -package app +package app_test import ( + "context" "fmt" "testing" + "go.osspkg.com/goppy/app" + "go.osspkg.com/goppy/errors" "go.osspkg.com/goppy/xc" "go.osspkg.com/goppy/xtest" ) -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -type t0 struct{} - -func newT0() *t0 { return &t0{} } -func (t0 *t0) Up() error { return nil } -func (t0 *t0) Down() error { return nil } -func (t0 *t0) V() string { return "t0V" } - -type t1 struct { - t0 *t0 +func TestUnit_EmptyDI(t *testing.T) { + c := app.NewContainer(xc.New()) + xtest.NoError(t, c.Start()) + xtest.NoError(t, c.Stop()) } -func newT1(t0 *t0) *t1 { return &t1{t0: t0} } -func (t1 *t1) Up() error { return nil } -func (t1 *t1) Down() error { return nil } -func (t1 *t1) V() string { return "t1V" } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -type t2 struct { - t0 *t0 - t1 *t1 +type SimpleDI1_A struct { + A string } -func newT2(t1 *t1, t0 *t0) *t2 { return &t2{t0: t0, t1: t1} } -func (t2 *t2) Up() error { return nil } -func (t2 *t2) Down() error { return nil } -func (t2 *t2) V() (string, string, string) { return "t2V", t2.t1.V(), t2.t0.V() } - -type t4 struct { - T0 *t0 - T1 *t1 - T2 *t2 - T7 *t7 - T44 t44 +func SimpleDI1_func1(a *SimpleDI1_A) { + fmt.Println("[func]", a.A) } -type t44 struct { - Env string +func SimpleDI1_func2(a, b *SimpleDI1_A) { + fmt.Println("[func]", a.A, b.A) } -type t5 struct{} - -func newT5() *t5 { return &t5{} } -func (t5 *t5) V() string { return "t5V" } - -type t6 struct{ T4 t4 } - -func newT6(t4 t4) *t6 { return &t6{T4: t4} } -func (t6 *t6) V() string { return "t6V" } - -type t7 struct{} - -func newT7() *t7 { return &t7{} } -func (t7 *t7) V() string { return "t7V" } - -type t8 struct{} - -func newT8() (*t8, error) { return &t8{}, nil } -func (t8 *t8) V() string { return "t8V" } +type SimpleDI1_Err struct { + ER string +} -type hello string +func (v *SimpleDI1_Err) Error() string { + return v.ER +} -var AA = hello("hhhh") +type SimpleDI1_StructEmpty struct { +} -type ii interface { - V() string +type SimpleDI1_StructUnsupported struct { + A float32 } -func newT7i(_ hello) ii { - return &t7{} +type SimpleDI1_Struct struct { + AA *SimpleDI1_A } -func TestUnit_Dependencies(t *testing.T) { - dep := newDic(xc.New()) +type SimpleDI1_ServiceEmpty struct{} - xtest.NoError(t, dep.Register([]interface{}{ - newT1, newT2, newT5, newT6, newT7(), newT8, - AA, newT7i, newT0, t44{Env: "aaa"}, - func(b *t6) { - t.Log("anonymous function") - }, - }...)) +func (v *SimpleDI1_ServiceEmpty) Up() error { return nil } +func (v *SimpleDI1_ServiceEmpty) Down() error { return nil } - xtest.NoError(t, dep.Build()) - xtest.Error(t, dep.Build()) +type SimpleDI1_ServiceEmptyXC struct{} - xtest.NoError(t, dep.Inject(func( - v1 *t1, v2 *t2, v3 *t5, v4 *t6, v5 *t6, - v6 *t7, v7 *t8, v8 hello, - v9 ii, v10 *t0, v11 t44, - ) { - xtest.Equal(t, "t1V", v1.V()) - vv2, _, _ := v2.V() - xtest.Equal(t, "t2V", vv2) - xtest.Equal(t, "t5V", v3.V()) - xtest.Equal(t, "t6V", v4.V()) - xtest.Equal(t, "t6V", v5.V()) - xtest.Equal(t, "t7V", v6.V()) - xtest.Equal(t, hello("hhhh"), v8) - xtest.Equal(t, "t7V", v9.V()) - xtest.Equal(t, "t0V", v10.V()) - xtest.Equal(t, "aaa", v11.Env) - })) +func (v *SimpleDI1_ServiceEmptyXC) Up(_ xc.Context) error { return nil } +func (v *SimpleDI1_ServiceEmptyXC) Down() error { return nil } - xtest.Error(t, dep.Inject(func(a string, b int, c bool) { +type SimpleDI1_ServiceEmptyCtx struct{} - })) +func (v *SimpleDI1_ServiceEmptyCtx) Up(_ context.Context) error { return nil } +func (v *SimpleDI1_ServiceEmptyCtx) Down() error { return nil } - xtest.NoError(t, dep.Down()) - xtest.Error(t, dep.Down()) +type SimpleDI1_Service struct { + ErrUp string + ErrDown string } -type demo1 struct{} -type demo2 struct{} -type demo3 struct{} - -func newDemo() (*demo1, *demo2, *demo3) { return &demo1{}, &demo2{}, &demo3{} } -func (d *demo1) Up() error { - fmt.Println("demo1 up") - return nil -} -func (d *demo1) Down() error { - fmt.Println("demo1 down") +func (v *SimpleDI1_Service) Up() error { + if len(v.ErrUp) > 0 { + return fmt.Errorf(v.ErrUp) + } return nil } -func TestUnit_Dependencies2(t *testing.T) { - dep := newDic(xc.New()) - xtest.NoError(t, dep.Register([]interface{}{ - newDemo, - }...)) - xtest.NoError(t, dep.Build()) - xtest.Error(t, dep.Build()) - xtest.NoError(t, dep.Down()) - xtest.Error(t, dep.Down()) +func (v *SimpleDI1_Service) Down() error { + if len(v.ErrDown) > 0 { + return fmt.Errorf(v.ErrDown) + } + return nil } -type demo4 struct{} - -func newDemo4() (*demo4, error) { return nil, fmt.Errorf("fail init constructor demo4") } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -func TestUnit_Dependencies3(t *testing.T) { - dep := newDic(xc.New()) - xtest.NoError(t, dep.Register([]interface{}{ - newDemo4, - }...)) - err := dep.Build() - xtest.Error(t, err) - fmt.Println(err.Error()) - xtest.Contains(t, err.Error(), "fail init constructor demo4") +func TestUnit_SimpleDI1(t *testing.T) { + c := app.NewContainer(xc.New()) + xtest.NoError(t, c.Register( + &SimpleDI1_A{A: "field A of struct"}, + func(a *SimpleDI1_A) { fmt.Println(1, a.A) }, + func(a *SimpleDI1_A) { fmt.Println(2, a.A) }, + func() { fmt.Println("empty 1") }, + func() { fmt.Println("empty 2") }, + SimpleDI1_func1, + )) + xtest.NoError(t, c.Start()) + xtest.Error(t, c.Start()) + xtest.NoError(t, c.Stop()) + xtest.NoError(t, c.Stop()) } -func newDemo5() error { return fmt.Errorf("fail init constructor newDemo5") } - -func TestUnit_Dependencies4(t *testing.T) { - dep := newDic(xc.New()) - xtest.NoError(t, dep.Register(newDemo5)) - err := dep.Build() - xtest.Error(t, err) - fmt.Println(err.Error()) - xtest.Contains(t, err.Error(), "fail init constructor newDemo5") +func TestUnit_SimpleDI2(t *testing.T) { + c := app.NewContainer(xc.New()) + xtest.NoError(t, c.Register(&SimpleDI1_A{A: "field A of struct"})) + xtest.ErrorContains(t, c.Invoke(func(a *SimpleDI1_A) { + fmt.Println("Invoke", a.A) + }), "dependencies are not running yet") + xtest.NoError(t, c.Start()) + xtest.NoError(t, c.Stop()) } -type demo6 struct{} - -func newDemo6() *demo6 { return &demo6{} } -func (d *demo6) Up() error { - fmt.Println("demo6 up") - return nil -} -func (d *demo6) Down() error { - fmt.Println("demo6 down") - return nil -} -func (d *demo6) Name() string { - return "DEMO 6" -} - -func TestUnit_DicInvoke1(t *testing.T) { - dep := newDic(xc.New()) - xtest.NoError(t, dep.Register(newDemo6)) - xtest.NoError(t, dep.Invoke(func(d *demo6) { - fmt.Println("Invoke", d.Name()) - })) - xtest.NoError(t, dep.Down()) - xtest.Error(t, dep.Down()) +func TestUnit_SimpleDI3(t *testing.T) { + c := app.NewContainer(xc.New()) + xtest.NoError(t, c.Start()) + xtest.ErrorContains(t, c.Invoke(func(a *SimpleDI1_A) { + fmt.Println("Invoke", a.A) + }), "_test.SimpleDI1_A] not initiated") + xtest.NoError(t, c.Stop()) } -func TestUnit_DicInvoke2(t *testing.T) { - dep := newDic(xc.New()) - xtest.NoError(t, dep.Register(newDemo6)) - xtest.NoError(t, dep.Invoke(func() { - fmt.Println("Invoke") - })) - xtest.NoError(t, dep.Down()) - xtest.Error(t, dep.Down()) +func TestUnit_DI_Default(t *testing.T) { + tests := []struct { + name string + register []interface{} + invoke interface{} + wantErr bool + wantErrString string + }{ + { + name: "Case1", + register: []interface{}{ + &SimpleDI1_A{A: "field A of struct"}, + &SimpleDI1_A{A: "field A of struct"}, + }, + wantErr: true, + wantErrString: "_test.SimpleDI1_A] already initiated", + }, + { + name: "Case2", + register: []interface{}{ + &SimpleDI1_Err{ER: "111"}, + }, + wantErr: false, + wantErrString: "", + }, + { + name: "Case3", + register: []interface{}{ + 123, + }, + wantErr: true, + wantErrString: "dependency [int] is not supported", + }, + { + name: "Case4", + register: []interface{}{ + &SimpleDI1_A{A: "field A of struct"}, + SimpleDI1_func2, + }, + wantErr: false, + wantErrString: "", + }, + { + name: "Case5", + register: []interface{}{ + &SimpleDI1_A{A: "field A of struct"}, + SimpleDI1_StructEmpty{}, + SimpleDI1_Struct{}, + }, + wantErr: false, + wantErrString: "", + }, + { + name: "Case6", + register: []interface{}{ + func() *SimpleDI1_A { return &SimpleDI1_A{A: "field A of struct 1"} }, + func() *SimpleDI1_A { return &SimpleDI1_A{A: "field A of struct 2"} }, + }, + wantErr: true, + wantErrString: "_test.SimpleDI1_A] already initiated", + }, + { + name: "Case7", + register: []interface{}{ + SimpleDI1_func2, + }, + wantErr: true, + wantErrString: "_test.SimpleDI1_A] not initiated", + }, + { + name: "Case8", + register: []interface{}{ + SimpleDI1_Struct{}, + }, + wantErr: true, + wantErrString: "_test.SimpleDI1_A] not initiated", + }, + { + name: "Case9", + register: []interface{}{}, + invoke: func(a int) {}, + wantErr: true, + wantErrString: "dependency [int] is not supported", + }, + { + name: "Case10", + register: []interface{}{}, + invoke: SimpleDI1_StructUnsupported{}, + wantErr: true, + wantErrString: "dependency [float32] is not supported", + }, + { + name: "Case11", + register: []interface{}{}, + invoke: SimpleDI1_Struct{}, + wantErr: true, + wantErrString: "_test.SimpleDI1_A] not initiated", + }, + { + name: "Case12", + register: []interface{}{}, + invoke: &SimpleDI1_A{}, + wantErr: false, + wantErrString: "", + }, + { + name: "Case13", + register: []interface{}{}, + invoke: func() error { return fmt.Errorf("start fail") }, + wantErr: true, + wantErrString: "start fail", + }, + { + name: "Case14", + register: []interface{}{ + &SimpleDI1_Service{}, + }, + wantErr: false, + wantErrString: "", + }, + { + name: "Case15", + register: []interface{}{ + &SimpleDI1_Service{ErrUp: "is up err"}, + }, + wantErr: true, + wantErrString: "is up err", + }, + { + name: "Case15", + register: []interface{}{ + &SimpleDI1_Service{ErrDown: "is down err"}, + }, + wantErr: true, + wantErrString: "is down err", + }, + { + name: "Case16", + register: []interface{}{ + func() *SimpleDI1_Service { + return &SimpleDI1_Service{ErrUp: "is up err"} + }, + }, + wantErr: true, + wantErrString: "is up err", + }, + { + name: "Case17", + register: []interface{}{ + func() (int, error) { + return 0, nil + }, + }, + wantErr: true, + wantErrString: "dependency [int] is not supported form [", + }, + { + name: "Case17", + register: []interface{}{ + func() (error, int) { + return nil, 0 + }, + }, + wantErr: true, + wantErrString: "dependency [int] is not supported form [", + }, + { + name: "Case18", + register: []interface{}{}, + invoke: func() (int, error) { + return 0, nil + }, + wantErr: false, + wantErrString: "", + }, + { + name: "Case19", + register: []interface{}{}, + invoke: func(_ error) int { + return 0 + }, + wantErr: true, + wantErrString: "dependency [error] is not supported", + }, + { + name: "Case20", + register: []interface{}{ + &SimpleDI1_ServiceEmpty{}, + &SimpleDI1_ServiceEmptyXC{}, + &SimpleDI1_ServiceEmptyCtx{}, + &SimpleDI1_Service{}, + }, + wantErr: false, + wantErrString: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := app.NewContainer(xc.New()) + errs := errors.Wrap( + c.Register(tt.register...), + c.Start(), + func() error { + if tt.invoke != nil { + return c.Invoke(tt.invoke) + } + return nil + }(), + c.Stop(), + ) + if tt.wantErr { + xtest.ErrorContains(t, errs, tt.wantErrString) + } else { + xtest.NoError(t, errs) + } + }) + } } diff --git a/app/errors.go b/app/errors.go index 8428f8a..815b865 100644 --- a/app/errors.go +++ b/app/errors.go @@ -8,8 +8,7 @@ package app import "go.osspkg.com/goppy/errors" var ( - errDepBuilderNotRunning = errors.New("dependencies builder is not running") - errDepNotRunning = errors.New("dependencies are not running yet") - errServiceUnknown = errors.New("unknown service") - errBadFileFormat = errors.New("is not a supported file format") + errDepAlreadyRunned = errors.New("dependencies are already running") + errDepNotRunning = errors.New("dependencies are not running yet") + errServiceUnknown = errors.New("unknown service") ) diff --git a/app/go.mod b/app/go.mod index bf81c12..6f04e24 100644 --- a/app/go.mod +++ b/app/go.mod @@ -5,6 +5,7 @@ go 1.18 replace ( go.osspkg.com/goppy/console => ../console go.osspkg.com/goppy/errors => ../errors + go.osspkg.com/goppy/iofile => ../iofile go.osspkg.com/goppy/iosync => ../iosync go.osspkg.com/goppy/syscall => ../syscall go.osspkg.com/goppy/xc => ../xc @@ -13,21 +14,20 @@ replace ( ) require ( - go.osspkg.com/algorithms v1.3.0 + go.osspkg.com/algorithms v1.3.1 go.osspkg.com/goppy/console v0.1.0 go.osspkg.com/goppy/errors v0.1.0 + go.osspkg.com/goppy/iofile v0.1.4 + go.osspkg.com/goppy/iosync v0.1.4 go.osspkg.com/goppy/syscall v0.1.1 go.osspkg.com/goppy/xc v0.1.0 - go.osspkg.com/goppy/xlog v0.1.5 - go.osspkg.com/goppy/xtest v0.1.2 - gopkg.in/yaml.v3 v3.0.1 + go.osspkg.com/goppy/xlog v0.1.6 + go.osspkg.com/goppy/xtest v0.1.3 ) require ( github.com/josharian/intern v1.0.0 // indirect - github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/rogpeppe/go-internal v1.11.0 // indirect - go.osspkg.com/goppy/iosync v0.1.3 // indirect - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/app/go.sum b/app/go.sum index 25b9a5b..8eb5406 100644 --- a/app/go.sum +++ b/app/go.sum @@ -3,21 +3,16 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -go.osspkg.com/algorithms v1.3.0 h1:rrKO440aNTofYJwGnOVsW00w0VRtZtu+BQrgXMXw7j4= -go.osspkg.com/algorithms v1.3.0/go.mod h1:J2SXxHdqBK9ALGYJomA9XGvTOhIwMK0fvVw+KusYyoo= +go.osspkg.com/algorithms v1.3.1 h1:Weh0Z4dMzHFRJTfMva/mU6Uk95wSRGwt1Saj2sfz3Wc= +go.osspkg.com/algorithms v1.3.1/go.mod h1:kj+oVK7UDQlXMKleMTYzbEEpUAXC2tzRBtki7F5EbXc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/app/reflect.go b/app/reflect.go index 732a9fb..2ed48e6 100644 --- a/app/reflect.go +++ b/app/reflect.go @@ -12,9 +12,9 @@ import ( var errType = reflect.TypeOf(new(error)).Elem() -func getRefAddr(t reflect.Type) (string, bool) { +func getReflectAddress(t reflect.Type, v interface{}) (string, bool) { if len(t.PkgPath()) > 0 { - return t.PkgPath() + "." + t.Name(), true + return fmt.Sprintf("%s.%s", t.PkgPath(), t.Name()), true } switch t.Kind() { case reflect.Array, reflect.Chan, reflect.Map, reflect.Ptr, reflect.Slice: @@ -22,17 +22,19 @@ func getRefAddr(t reflect.Type) (string, bool) { return "error", false } if len(t.Elem().PkgPath()) > 0 { - return t.Elem().PkgPath() + "." + t.Elem().Name(), true + return fmt.Sprintf("%s.%s", t.Elem().PkgPath(), t.Elem().Name()), true } case reflect.Func: - // TODO: fix for anonymous function - // random.String(30) + "." + t.String(), true - return t.String(), true + if v == nil { + return t.String(), false + } + p := reflect.ValueOf(v).Pointer() + return fmt.Sprintf("0x%x.%s", p, t.String()), true } return t.String(), false } -func typingRefPtr(vv []interface{}, call func(interface{}) error) ([]interface{}, error) { +func typingReflectPtr(vv []interface{}, call func(interface{}) error) ([]interface{}, error) { result := make([]interface{}, 0, len(vv)) for _, v := range vv { ref := reflect.TypeOf(v) diff --git a/app/reflect_test.go b/app/reflect_test.go index 077c3ac..7143adb 100644 --- a/app/reflect_test.go +++ b/app/reflect_test.go @@ -6,14 +6,13 @@ package app import ( + "errors" "reflect" "strings" "testing" - - "go.osspkg.com/goppy/errors" ) -func TestUnit_getRefAddr(t *testing.T) { +func TestUnit_getReflectAddress(t *testing.T) { type ( aa string bb struct{} @@ -35,28 +34,30 @@ func TestUnit_getRefAddr(t *testing.T) { tests := []struct { name string args reflect.Type + obj interface{} want string ok bool }{ - {name: "Case1", args: reflect.TypeOf(a), want: "int"}, - {name: "Case2", args: reflect.TypeOf(b), want: "string"}, - {name: "Case3", args: reflect.TypeOf(c), want: "bool"}, - {name: "Case4", args: reflect.TypeOf(d), want: "go.osspkg.com/goppy/app.aa", ok: true}, - {name: "Case5", args: reflect.TypeOf(e), want: "go.osspkg.com/goppy/app.ff", ok: true}, - {name: "Case6", args: reflect.TypeOf(f), want: "func(string) bool", ok: true}, - {name: "Case7", args: reflect.TypeOf(g), want: "error"}, - {name: "Case8", args: reflect.TypeOf(h), want: "[]string"}, - {name: "Case9", args: reflect.TypeOf(j), want: "go.osspkg.com/goppy/app.bb", ok: true}, - {name: "Case10", args: reflect.TypeOf(k), want: "struct {}"}, + {name: "Case1", args: reflect.TypeOf(a), obj: a, want: "int"}, + {name: "Case2", args: reflect.TypeOf(b), obj: b, want: "string"}, + {name: "Case3", args: reflect.TypeOf(c), obj: c, want: "bool"}, + {name: "Case4", args: reflect.TypeOf(d), obj: d, want: "go.osspkg.com/goppy/app.aa", ok: true}, + {name: "Case5", args: reflect.TypeOf(e), obj: e, want: "go.osspkg.com/goppy/app.ff", ok: true}, + {name: "Case6", args: reflect.TypeOf(f), obj: f, want: "func(string) bool", ok: true}, + {name: "Case6", args: reflect.TypeOf(f), obj: nil, want: "func(string) bool", ok: false}, + {name: "Case7", args: reflect.TypeOf(g), obj: g, want: "error"}, + {name: "Case8", args: reflect.TypeOf(h), obj: h, want: "[]string"}, + {name: "Case9", args: reflect.TypeOf(j), obj: j, want: "go.osspkg.com/goppy/app.bb", ok: true}, + {name: "Case10", args: reflect.TypeOf(k), obj: k, want: "struct {}"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, ok := getRefAddr(tt.args) + got, ok := getReflectAddress(tt.args, tt.obj) if !strings.Contains(got, tt.want) { - t.Errorf("getRefAddr() = %v, want %v", got, tt.want) + t.Errorf("getReflectAddress() = %v, want %v", got, tt.want) } if ok != tt.ok { - t.Errorf("getRefAddr() = %v, want %v", ok, tt.ok) + t.Errorf("getReflectAddress() = %v, want %v", ok, tt.ok) } }) } diff --git a/app/services.go b/app/services.go index f4d2b0f..51ffb21 100644 --- a/app/services.go +++ b/app/services.go @@ -6,44 +6,27 @@ package app import ( - "reflect" - "sync/atomic" + "context" "go.osspkg.com/goppy/errors" + "go.osspkg.com/goppy/iosync" "go.osspkg.com/goppy/xc" ) type ( - // ServiceInterface interface for services ServiceInterface interface { Up() error Down() error } - //ServiceContextInterface interface for services with context - ServiceContextInterface interface { + ServiceXContextInterface interface { Up(ctx xc.Context) error Down() error } -) - -var ( - srvType = reflect.TypeOf(new(ServiceInterface)).Elem() - srvTypeCtx = reflect.TypeOf(new(ServiceContextInterface)).Elem() -) - -func asService(v reflect.Value) (ServiceInterface, bool) { - if v.Type().AssignableTo(srvType) { - return v.Interface().(ServiceInterface), true - } - return nil, false -} - -func asServiceContext(v reflect.Value) (ServiceContextInterface, bool) { - if v.Type().AssignableTo(srvTypeCtx) { - return v.Interface().(ServiceContextInterface), true + ServiceContextInterface interface { + Up(ctx context.Context) error + Down() error } - return nil, false -} +) func isService(v interface{}) bool { if _, ok := v.(ServiceInterface); ok { @@ -52,46 +35,96 @@ func isService(v interface{}) bool { if _, ok := v.(ServiceContextInterface); ok { return true } + if _, ok := v.(ServiceXContextInterface); ok { + return true + } return false } -/**********************************************************************************************************************/ +func serviceCallUp(v interface{}, c xc.Context) error { + if vv, ok := v.(ServiceContextInterface); ok { + return vv.Up(c.Context()) + } + if vv, ok := v.(ServiceXContextInterface); ok { + return vv.Up(c) + } + if vv, ok := v.(ServiceInterface); ok { + return vv.Up() + } + return errors.Wrapf(errServiceUnknown, "service [%T]", v) +} -const ( - statusUp uint32 = 1 - statusDown uint32 = 0 -) +func serviceCallDown(v interface{}) error { + if vv, ok := v.(ServiceContextInterface); ok { + return vv.Down() + } + if vv, ok := v.(ServiceXContextInterface); ok { + return vv.Down() + } + if vv, ok := v.(ServiceInterface); ok { + return vv.Down() + } + return errors.Wrapf(errServiceUnknown, "service [%T]", v) +} + +/**********************************************************************************************************************/ type ( - _serv struct { - tree *treeItem - status uint32 - ctx xc.Context - } treeItem struct { Previous *treeItem Current interface{} Next *treeItem } + serviceTree struct { + tree *treeItem + status iosync.Switch + ctx xc.Context + } ) -func newService(ctx xc.Context) *_serv { - return &_serv{ +func newServiceTree(ctx xc.Context) *serviceTree { + return &serviceTree{ tree: nil, - status: statusDown, ctx: ctx, + status: iosync.NewSwitch(), } } -// IsUp - mark that all services have started -func (s *_serv) IsUp() bool { - return atomic.LoadUint32(&s.status) == statusUp +func (s *serviceTree) IsOn() bool { + return s.status.IsOn() +} + +func (s *serviceTree) IsOff() bool { + return s.status.IsOff() } -// AddAndRun - add new service by interface -func (s *_serv) AddAndRun(v interface{}) error { - if !s.IsUp() { - return errDepBuilderNotRunning +func (s *serviceTree) MakeAsUp() error { + if !s.status.On() { + return errDepAlreadyRunned + } + return nil +} + +func (s *serviceTree) IterateOver() { + if s.tree == nil { + return + } + for s.tree.Previous != nil { + s.tree = s.tree.Previous + } + for { + if s.tree.Next == nil { + break + } + s.tree = s.tree.Next + } + return +} + +// AddAndUp - add new service and call up +func (s *serviceTree) AddAndUp(v interface{}) error { + if s.IsOff() { + return errDepNotRunning } if !isService(v) { @@ -114,67 +147,23 @@ func (s *_serv) AddAndRun(v interface{}) error { s.tree = n } - if vv, ok := v.(ServiceContextInterface); ok { - if err := vv.Up(s.ctx); err != nil { - return err - } - } - if vv, ok := v.(ServiceInterface); ok { - if err := vv.Up(); err != nil { - return err - } - } - - return nil -} - -func (s *_serv) MakeAsUp() error { - if !atomic.CompareAndSwapUint32(&s.status, statusDown, statusUp) { - return errDepBuilderNotRunning - } - return nil -} - -func (s *_serv) IterateOver() { - if s.tree == nil { - return - } - for s.tree.Previous != nil { - s.tree = s.tree.Previous - } - for { - if s.tree.Next == nil { - break - } - s.tree = s.tree.Next - } - return + return serviceCallUp(v, s.ctx) } // Down - stop all services -func (s *_serv) Down() error { +func (s *serviceTree) Down() error { var err0 error - if !atomic.CompareAndSwapUint32(&s.status, statusUp, statusDown) { + if !s.status.Off() { return errDepNotRunning } if s.tree == nil { return nil } for { - if vv, ok := s.tree.Current.(ServiceContextInterface); ok { - if err := vv.Down(); err != nil { - err0 = errors.Wrap(err0, - errors.Wrapf(err, "down [%T] service error", s.tree.Current), - ) - } - } else if vv, ok := s.tree.Current.(ServiceInterface); ok { - if err := vv.Down(); err != nil { - err0 = errors.Wrap(err0, - errors.Wrapf(err, "down [%T] service error", s.tree.Current), - ) - } - } else { - return errors.Wrapf(errServiceUnknown, "service [%T]", s.tree.Current) + if err := serviceCallDown(s.tree.Current); err != nil { + err0 = errors.Wrap(err0, + errors.Wrapf(err, "down [%T] service error", s.tree.Current), + ) } if s.tree.Previous == nil { break diff --git a/app/sources.go b/app/sources.go deleted file mode 100644 index f73bbc7..0000000 --- a/app/sources.go +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2022-2023 Mikhail Knyazhev . All rights reserved. - * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. - */ - -package app - -import ( - "encoding/json" - "os" - "path/filepath" - - "go.osspkg.com/goppy/errors" - "gopkg.in/yaml.v3" -) - -// Sources model -type Sources string - -// Decode unmarshal file to model -func (v Sources) Decode(configs ...interface{}) error { - data, err := os.ReadFile(string(v)) - if err != nil { - return err - } - ext := filepath.Ext(string(v)) - switch ext { - case ".yml", ".yaml": - return v.unmarshal("yaml unmarshal", data, yaml.Unmarshal, configs...) - case ".json": - return v.unmarshal("json unmarshal", data, json.Unmarshal, configs...) - } - return errBadFileFormat -} - -func (v Sources) unmarshal( - title string, data []byte, call func([]byte, interface{}, - ) error, configs ...interface{}) error { - for _, conf := range configs { - if err := call(data, conf); err != nil { - return errors.Wrapf(err, title) - } - } - return nil -} diff --git a/app/storage.go b/app/storage.go new file mode 100644 index 0000000..b71be41 --- /dev/null +++ b/app/storage.go @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2022-2023 Mikhail Knyazhev . All rights reserved. + * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. + */ + +package app + +import ( + "fmt" + "reflect" + "sync" +) + +type ( + objectRelationType uint + objectRelationService uint +) + +const ( + asTypeNew objectRelationType = 1 + asTypeExist objectRelationType = 2 + + itNotService objectRelationService = 0 + itDownService objectRelationService = 1 + itUpedService objectRelationService = 2 +) + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +type ( + objectStorageItem struct { + Address string + RelationType objectRelationType + ReflectType reflect.Type + Kind reflect.Kind + Value interface{} + Service objectRelationService + } + objectStorage struct { + data map[string]*objectStorageItem + mux sync.RWMutex + } +) + +func newObjectStorage() *objectStorage { + return &objectStorage{ + data: make(map[string]*objectStorageItem), + } +} + +func (v *objectStorage) Get(address string) (*objectStorageItem, error) { + v.mux.RLock() + defer v.mux.RUnlock() + + if item, ok := v.data[address]; ok { + return item, nil + } + return nil, fmt.Errorf("dependency [%s] not initiated", address) +} + +func (v *objectStorage) Add(ref reflect.Type, obj interface{}, relationType objectRelationType) error { + v.mux.Lock() + defer v.mux.Unlock() + + address, ok := getReflectAddress(ref, obj) + if !ok { + if address != "error" { + return fmt.Errorf("dependency [%s] is not supported", address) + } + return nil + } + if item, ok := v.data[address]; ok { + if item.RelationType == asTypeExist { + return fmt.Errorf("dependency [%s] already initiated", address) + } + } + serviceStatus := itNotService + if isService(obj) { + serviceStatus = itDownService + } + v.data[address] = &objectStorageItem{ + Address: address, + Value: obj, + ReflectType: ref, + RelationType: relationType, + Kind: ref.Kind(), + Service: serviceStatus, + } + return nil +} + +func (v *objectStorage) Each(call func(item *objectStorageItem) error) error { + v.mux.RLock() + defer v.mux.RUnlock() + + for _, item := range v.data { + if err := call(item); err != nil { + return err + } + } + return nil +} diff --git a/auth/go.mod b/auth/go.mod index 3eac62d..bf8a76b 100644 --- a/auth/go.mod +++ b/auth/go.mod @@ -18,13 +18,13 @@ replace ( require ( github.com/mailru/easyjson v0.7.7 - go.osspkg.com/goppy/encryption v0.1.3 + go.osspkg.com/goppy/encryption v0.1.4 go.osspkg.com/goppy/errors v0.1.0 go.osspkg.com/goppy/ioutil v0.1.1 go.osspkg.com/goppy/plugins v0.1.1 go.osspkg.com/goppy/random v0.1.1 - go.osspkg.com/goppy/web v0.1.7 - go.osspkg.com/goppy/xtest v0.1.2 + go.osspkg.com/goppy/web v0.1.8 + go.osspkg.com/goppy/xtest v0.1.3 golang.org/x/oauth2 v0.13.0 ) @@ -33,9 +33,9 @@ require ( cloud.google.com/go/compute/metadata v0.2.3 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/josharian/intern v1.0.0 // indirect - go.osspkg.com/goppy/iosync v0.1.3 // indirect + go.osspkg.com/goppy/iosync v0.1.4 // indirect go.osspkg.com/goppy/xc v0.1.0 // indirect - go.osspkg.com/goppy/xlog v0.1.5 // indirect + go.osspkg.com/goppy/xlog v0.1.6 // indirect go.osspkg.com/goppy/xnet v0.1.1 // indirect go.osspkg.com/static v1.4.0 // indirect golang.org/x/net v0.17.0 // indirect diff --git a/encryption/go.mod b/encryption/go.mod index 56af60f..8e2903f 100644 --- a/encryption/go.mod +++ b/encryption/go.mod @@ -11,6 +11,6 @@ replace ( require ( go.osspkg.com/goppy/errors v0.1.0 go.osspkg.com/goppy/random v0.1.1 - go.osspkg.com/goppy/xtest v0.1.2 + go.osspkg.com/goppy/xtest v0.1.3 golang.org/x/crypto v0.17.0 ) diff --git a/epoll/go.mod b/epoll/go.mod index ba8efac..5a76db6 100644 --- a/epoll/go.mod +++ b/epoll/go.mod @@ -13,9 +13,9 @@ replace ( require ( go.osspkg.com/goppy/errors v0.1.0 - go.osspkg.com/goppy/iosync v0.1.3 + go.osspkg.com/goppy/iosync v0.1.4 go.osspkg.com/goppy/xc v0.1.0 - go.osspkg.com/goppy/xlog v0.1.5 + go.osspkg.com/goppy/xlog v0.1.6 go.osspkg.com/goppy/xnet v0.1.1 golang.org/x/sys v0.15.0 ) diff --git a/examples/go.mod b/examples/go.mod index 6a08e89..211603f 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -36,25 +36,25 @@ replace ( require ( go.osspkg.com/goppy v0.15.4 - go.osspkg.com/goppy/app v0.1.5 + go.osspkg.com/goppy/app v0.1.6 go.osspkg.com/goppy/auth v0.1.0 go.osspkg.com/goppy/console v0.1.0 go.osspkg.com/goppy/geoip v0.1.0 - go.osspkg.com/goppy/iosync v0.1.2 + go.osspkg.com/goppy/iosync v0.1.3 go.osspkg.com/goppy/ormmysql v0.1.0 go.osspkg.com/goppy/ormpgsql v0.1.0 go.osspkg.com/goppy/ormsqlite v0.1.0 go.osspkg.com/goppy/plugins v0.1.1 - go.osspkg.com/goppy/routine v0.1.2 + go.osspkg.com/goppy/routine v0.1.3 go.osspkg.com/goppy/syscall v0.1.1 go.osspkg.com/goppy/tcp v0.0.0-00010101000000-000000000000 go.osspkg.com/goppy/udp v0.0.2 go.osspkg.com/goppy/unixsocket v0.1.0 - go.osspkg.com/goppy/web v0.1.6 + go.osspkg.com/goppy/web v0.1.7 go.osspkg.com/goppy/ws v0.1.0 go.osspkg.com/goppy/xc v0.1.0 go.osspkg.com/goppy/xdns v0.1.0 - go.osspkg.com/goppy/xlog v0.1.4 + go.osspkg.com/goppy/xlog v0.1.5 ) require ( @@ -70,14 +70,14 @@ require ( github.com/miekg/dns v1.1.57 // indirect github.com/oschwald/geoip2-golang v1.9.0 // indirect github.com/oschwald/maxminddb-golang v1.11.0 // indirect - go.osspkg.com/algorithms v1.3.0 // indirect + go.osspkg.com/algorithms v1.3.1 // indirect go.osspkg.com/goppy/encryption v0.1.3 // indirect go.osspkg.com/goppy/errors v0.1.0 // indirect go.osspkg.com/goppy/iofile v0.1.3 // indirect go.osspkg.com/goppy/ioutil v0.1.1 // indirect - go.osspkg.com/goppy/orm v0.1.4 // indirect + go.osspkg.com/goppy/orm v0.1.5 // indirect go.osspkg.com/goppy/random v0.1.1 // indirect - go.osspkg.com/goppy/sqlcommon v0.1.4 // indirect + go.osspkg.com/goppy/sqlcommon v0.1.5 // indirect go.osspkg.com/goppy/xnet v0.1.1 // indirect go.osspkg.com/static v1.4.0 // indirect golang.org/x/mod v0.12.0 // indirect diff --git a/examples/go.sum b/examples/go.sum index cb437c4..9cde89b 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -32,8 +32,8 @@ github.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfgl github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -go.osspkg.com/algorithms v1.3.0 h1:rrKO440aNTofYJwGnOVsW00w0VRtZtu+BQrgXMXw7j4= -go.osspkg.com/algorithms v1.3.0/go.mod h1:J2SXxHdqBK9ALGYJomA9XGvTOhIwMK0fvVw+KusYyoo= +go.osspkg.com/algorithms v1.3.1 h1:Weh0Z4dMzHFRJTfMva/mU6Uk95wSRGwt1Saj2sfz3Wc= +go.osspkg.com/algorithms v1.3.1/go.mod h1:kj+oVK7UDQlXMKleMTYzbEEpUAXC2tzRBtki7F5EbXc= go.osspkg.com/static v1.4.0 h1:2uy4/11c0QP+QLMucKQZbAU+e6lhVHKw5dWJPTk/DBg= go.osspkg.com/static v1.4.0/go.mod h1:94YydVU3qUtb1J534486lpm+qg6CviQjqtxKlkpSppM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/geoip/go.mod b/geoip/go.mod index dafd9b3..20848f7 100644 --- a/geoip/go.mod +++ b/geoip/go.mod @@ -17,7 +17,7 @@ replace ( require ( github.com/oschwald/geoip2-golang v1.9.0 go.osspkg.com/goppy/plugins v0.1.1 - go.osspkg.com/goppy/web v0.1.7 + go.osspkg.com/goppy/web v0.1.8 ) require ( @@ -25,10 +25,10 @@ require ( github.com/mailru/easyjson v0.7.7 // indirect github.com/oschwald/maxminddb-golang v1.11.0 // indirect go.osspkg.com/goppy/errors v0.1.0 // indirect - go.osspkg.com/goppy/iosync v0.1.3 // indirect + go.osspkg.com/goppy/iosync v0.1.4 // indirect go.osspkg.com/goppy/ioutil v0.1.1 // indirect go.osspkg.com/goppy/xc v0.1.0 // indirect - go.osspkg.com/goppy/xlog v0.1.5 // indirect + go.osspkg.com/goppy/xlog v0.1.6 // indirect go.osspkg.com/goppy/xnet v0.1.1 // indirect go.osspkg.com/static v1.4.0 // indirect golang.org/x/sys v0.15.0 // indirect diff --git a/go.mod b/go.mod index 7d3dcee..82845c5 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ replace ( go.osspkg.com/goppy/app => ./app go.osspkg.com/goppy/console => ./console go.osspkg.com/goppy/errors => ./errors + go.osspkg.com/goppy/iofile => ./iofile go.osspkg.com/goppy/iosync => ./iosync go.osspkg.com/goppy/plugins => ./plugins go.osspkg.com/goppy/syscall => ./syscall @@ -15,20 +16,20 @@ replace ( ) require ( - go.osspkg.com/goppy/app v0.1.6 + go.osspkg.com/goppy/app v0.1.7 go.osspkg.com/goppy/console v0.1.0 go.osspkg.com/goppy/errors v0.1.0 + go.osspkg.com/goppy/iofile v0.1.4 go.osspkg.com/goppy/plugins v0.1.1 - go.osspkg.com/goppy/xlog v0.1.5 + go.osspkg.com/goppy/xlog v0.1.6 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/josharian/intern v1.0.0 // indirect - github.com/kr/text v0.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - go.osspkg.com/algorithms v1.3.0 // indirect - go.osspkg.com/goppy/iosync v0.1.3 // indirect + go.osspkg.com/algorithms v1.3.1 // indirect + go.osspkg.com/goppy/iosync v0.1.4 // indirect go.osspkg.com/goppy/syscall v0.1.1 // indirect go.osspkg.com/goppy/xc v0.1.0 // indirect ) diff --git a/go.sum b/go.sum index 595b805..10da452 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,15 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -go.osspkg.com/algorithms v1.3.0 h1:rrKO440aNTofYJwGnOVsW00w0VRtZtu+BQrgXMXw7j4= -go.osspkg.com/algorithms v1.3.0/go.mod h1:J2SXxHdqBK9ALGYJomA9XGvTOhIwMK0fvVw+KusYyoo= +go.osspkg.com/algorithms v1.3.1 h1:Weh0Z4dMzHFRJTfMva/mU6Uk95wSRGwt1Saj2sfz3Wc= +go.osspkg.com/algorithms v1.3.1/go.mod h1:kj+oVK7UDQlXMKleMTYzbEEpUAXC2tzRBtki7F5EbXc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/go.work.sum b/go.work.sum index bf94841..26318b7 100644 --- a/go.work.sum +++ b/go.work.sum @@ -14,6 +14,7 @@ github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5i github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= diff --git a/goppy.go b/goppy.go index 9a53222..2032eac 100644 --- a/goppy.go +++ b/goppy.go @@ -13,6 +13,7 @@ import ( "go.osspkg.com/goppy/app" "go.osspkg.com/goppy/console" "go.osspkg.com/goppy/errors" + "go.osspkg.com/goppy/iofile" "go.osspkg.com/goppy/plugins" "go.osspkg.com/goppy/xlog" "gopkg.in/yaml.v3" @@ -142,7 +143,7 @@ func validateConfig(filename string, configs ...interface{}) error { defType := reflect.TypeOf(new(plugins.Validator)).Elem() for _, cfg := range configs { if reflect.TypeOf(cfg).AssignableTo(defType) { - if err = app.Sources(filename).Decode(cfg); err != nil { + if err = iofile.FileCodec(filename).Decode(cfg); err != nil { return fmt.Errorf("decode config %T error: %w", cfg, err) } vv, ok := cfg.(plugins.Validator) diff --git a/iofile/encdec.go b/iofile/encdec.go index 7dd2b93..508590d 100644 --- a/iofile/encdec.go +++ b/iofile/encdec.go @@ -9,9 +9,9 @@ import ( "encoding/json" "os" "path/filepath" - "sync" "go.osspkg.com/goppy/errors" + "go.osspkg.com/goppy/iosync" "gopkg.in/yaml.v3" ) @@ -29,14 +29,14 @@ var ( type codec struct { enc map[string]func(v interface{}) ([]byte, error) dec map[string]func([]byte, interface{}) error - mux sync.RWMutex + mux iosync.Lock } func newCodec() *codec { return &codec{ enc: make(map[string]func(v interface{}) ([]byte, error), 10), dec: make(map[string]func([]byte, interface{}) error, 10), - mux: sync.RWMutex{}, + mux: iosync.NewLock(), } } @@ -45,25 +45,25 @@ func AddFileCodec(ext string, enc func(v interface{}) ([]byte, error), dec func( } func (v *codec) Add(ext string, enc func(v interface{}) ([]byte, error), dec func([]byte, interface{}) error) *codec { - v.mux.Lock() - defer v.mux.Unlock() - v.enc[ext] = enc - v.dec[ext] = dec + v.mux.Lock(func() { + v.enc[ext] = enc + v.dec[ext] = dec + }) return v } -func (v *codec) GetEnc(ext string) (func(v interface{}) ([]byte, error), bool) { - v.mux.RLock() - defer v.mux.RUnlock() - fn, ok := v.enc[ext] - return fn, ok +func (v *codec) GetEnc(ext string) (fn func(v interface{}) ([]byte, error), ok bool) { + v.mux.RLock(func() { + fn, ok = v.enc[ext] + }) + return } -func (v *codec) GetDec(ext string) (func([]byte, interface{}) error, bool) { - v.mux.RLock() - defer v.mux.RUnlock() - fn, ok := v.dec[ext] - return fn, ok +func (v *codec) GetDec(ext string) (fn func([]byte, interface{}) error, ok bool) { + v.mux.RLock(func() { + fn, ok = v.dec[ext] + }) + return } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/iofile/go.mod b/iofile/go.mod index 368b383..475f47a 100644 --- a/iofile/go.mod +++ b/iofile/go.mod @@ -4,12 +4,14 @@ go 1.18 replace ( go.osspkg.com/goppy/errors => ../errors + go.osspkg.com/goppy/iosync => ../iosync go.osspkg.com/goppy/xtest => ../xtest ) require ( go.osspkg.com/goppy/errors v0.1.0 - go.osspkg.com/goppy/xtest v0.1.2 + go.osspkg.com/goppy/iosync v0.1.4 + go.osspkg.com/goppy/xtest v0.1.3 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/iosync/go.mod b/iosync/go.mod index 374e997..3427463 100644 --- a/iosync/go.mod +++ b/iosync/go.mod @@ -4,4 +4,4 @@ go 1.18 replace go.osspkg.com/goppy/xtest => ../xtest -require go.osspkg.com/goppy/xtest v0.1.2 +require go.osspkg.com/goppy/xtest v0.1.3 diff --git a/orm/go.mod b/orm/go.mod index 5b35f09..5b10b5a 100644 --- a/orm/go.mod +++ b/orm/go.mod @@ -14,16 +14,16 @@ replace ( require ( go.osspkg.com/goppy/errors v0.1.0 - go.osspkg.com/goppy/iofile v0.1.3 - go.osspkg.com/goppy/sqlcommon v0.1.5 + go.osspkg.com/goppy/iofile v0.1.4 + go.osspkg.com/goppy/sqlcommon v0.1.6 go.osspkg.com/goppy/xc v0.1.0 - go.osspkg.com/goppy/xlog v0.1.5 + go.osspkg.com/goppy/xlog v0.1.6 ) require ( github.com/josharian/intern v1.0.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - go.osspkg.com/goppy/iosync v0.1.3 // indirect + go.osspkg.com/goppy/iosync v0.1.4 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/ormmysql/go.mod b/ormmysql/go.mod index c5ec981..f42d3ca 100644 --- a/ormmysql/go.mod +++ b/ormmysql/go.mod @@ -18,18 +18,18 @@ replace ( require ( github.com/go-sql-driver/mysql v1.7.1 go.osspkg.com/goppy/errors v0.1.0 - go.osspkg.com/goppy/orm v0.1.5 + go.osspkg.com/goppy/orm v0.1.6 go.osspkg.com/goppy/plugins v0.1.1 - go.osspkg.com/goppy/routine v0.1.3 - go.osspkg.com/goppy/sqlcommon v0.1.5 + go.osspkg.com/goppy/routine v0.1.4 + go.osspkg.com/goppy/sqlcommon v0.1.6 go.osspkg.com/goppy/xc v0.1.0 - go.osspkg.com/goppy/xlog v0.1.5 + go.osspkg.com/goppy/xlog v0.1.6 ) require ( github.com/josharian/intern v1.0.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - go.osspkg.com/goppy/iofile v0.1.3 // indirect - go.osspkg.com/goppy/iosync v0.1.3 // indirect + go.osspkg.com/goppy/iofile v0.1.4 // indirect + go.osspkg.com/goppy/iosync v0.1.4 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/ormpgsql/go.mod b/ormpgsql/go.mod index aad7160..079004c 100644 --- a/ormpgsql/go.mod +++ b/ormpgsql/go.mod @@ -18,18 +18,18 @@ replace ( require ( github.com/lib/pq v1.10.9 go.osspkg.com/goppy/errors v0.1.0 - go.osspkg.com/goppy/orm v0.1.5 + go.osspkg.com/goppy/orm v0.1.6 go.osspkg.com/goppy/plugins v0.1.1 - go.osspkg.com/goppy/routine v0.1.3 - go.osspkg.com/goppy/sqlcommon v0.1.5 + go.osspkg.com/goppy/routine v0.1.4 + go.osspkg.com/goppy/sqlcommon v0.1.6 go.osspkg.com/goppy/xc v0.1.0 - go.osspkg.com/goppy/xlog v0.1.5 + go.osspkg.com/goppy/xlog v0.1.6 ) require ( github.com/josharian/intern v1.0.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - go.osspkg.com/goppy/iofile v0.1.3 // indirect - go.osspkg.com/goppy/iosync v0.1.3 // indirect + go.osspkg.com/goppy/iofile v0.1.4 // indirect + go.osspkg.com/goppy/iosync v0.1.4 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/ormsqlite/go.mod b/ormsqlite/go.mod index af500bf..b180ee1 100644 --- a/ormsqlite/go.mod +++ b/ormsqlite/go.mod @@ -18,18 +18,18 @@ replace ( require ( github.com/mattn/go-sqlite3 v1.14.19 go.osspkg.com/goppy/errors v0.1.0 - go.osspkg.com/goppy/iofile v0.1.3 - go.osspkg.com/goppy/orm v0.1.5 + go.osspkg.com/goppy/iofile v0.1.4 + go.osspkg.com/goppy/orm v0.1.6 go.osspkg.com/goppy/plugins v0.1.1 - go.osspkg.com/goppy/routine v0.1.3 - go.osspkg.com/goppy/sqlcommon v0.1.5 + go.osspkg.com/goppy/routine v0.1.4 + go.osspkg.com/goppy/sqlcommon v0.1.6 go.osspkg.com/goppy/xc v0.1.0 - go.osspkg.com/goppy/xlog v0.1.5 + go.osspkg.com/goppy/xlog v0.1.6 ) require ( github.com/josharian/intern v1.0.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - go.osspkg.com/goppy/iosync v0.1.3 // indirect + go.osspkg.com/goppy/iosync v0.1.4 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/routine/go.mod b/routine/go.mod index 1246fbf..eb6b629 100644 --- a/routine/go.mod +++ b/routine/go.mod @@ -10,5 +10,5 @@ replace ( require ( go.osspkg.com/goppy/errors v0.1.0 - go.osspkg.com/goppy/iosync v0.1.3 + go.osspkg.com/goppy/iosync v0.1.4 ) diff --git a/sqlcommon/go.mod b/sqlcommon/go.mod index 20da678..cd27ed2 100644 --- a/sqlcommon/go.mod +++ b/sqlcommon/go.mod @@ -11,12 +11,12 @@ replace ( require ( go.osspkg.com/goppy/errors v0.1.0 - go.osspkg.com/goppy/xlog v0.1.5 - go.osspkg.com/goppy/xtest v0.1.2 + go.osspkg.com/goppy/xlog v0.1.6 + go.osspkg.com/goppy/xtest v0.1.3 ) require ( github.com/josharian/intern v1.0.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - go.osspkg.com/goppy/iosync v0.1.3 // indirect + go.osspkg.com/goppy/iosync v0.1.4 // indirect ) diff --git a/tcp/go.mod b/tcp/go.mod index d429287..75dad03 100644 --- a/tcp/go.mod +++ b/tcp/go.mod @@ -13,10 +13,10 @@ replace ( require ( go.osspkg.com/goppy/errors v0.1.0 - go.osspkg.com/goppy/iosync v0.1.3 + go.osspkg.com/goppy/iosync v0.1.4 go.osspkg.com/goppy/plugins v0.1.1 go.osspkg.com/goppy/xc v0.1.0 - go.osspkg.com/goppy/xlog v0.1.5 + go.osspkg.com/goppy/xlog v0.1.6 go.osspkg.com/goppy/xnet v0.1.1 ) diff --git a/udp/go.mod b/udp/go.mod index 09f3824..5068441 100644 --- a/udp/go.mod +++ b/udp/go.mod @@ -12,9 +12,9 @@ replace ( ) require ( - go.osspkg.com/goppy/iosync v0.1.3 + go.osspkg.com/goppy/iosync v0.1.4 go.osspkg.com/goppy/xc v0.1.0 - go.osspkg.com/goppy/xlog v0.1.5 + go.osspkg.com/goppy/xlog v0.1.6 go.osspkg.com/goppy/xnet v0.1.1 ) diff --git a/unixsocket/go.mod b/unixsocket/go.mod index e79b6c0..36d1598 100644 --- a/unixsocket/go.mod +++ b/unixsocket/go.mod @@ -14,11 +14,11 @@ replace ( require ( go.osspkg.com/goppy/errors v0.1.0 - go.osspkg.com/goppy/iosync v0.1.3 + go.osspkg.com/goppy/iosync v0.1.4 go.osspkg.com/goppy/ioutil v0.1.1 go.osspkg.com/goppy/plugins v0.1.1 go.osspkg.com/goppy/xc v0.1.0 - go.osspkg.com/goppy/xlog v0.1.5 + go.osspkg.com/goppy/xlog v0.1.6 ) require ( diff --git a/web/go.mod b/web/go.mod index 9591159..8fb2cb5 100644 --- a/web/go.mod +++ b/web/go.mod @@ -16,13 +16,13 @@ replace ( require ( github.com/mailru/easyjson v0.7.7 go.osspkg.com/goppy/errors v0.1.0 - go.osspkg.com/goppy/iosync v0.1.3 + go.osspkg.com/goppy/iosync v0.1.4 go.osspkg.com/goppy/ioutil v0.1.1 go.osspkg.com/goppy/plugins v0.1.1 go.osspkg.com/goppy/xc v0.1.0 - go.osspkg.com/goppy/xlog v0.1.5 + go.osspkg.com/goppy/xlog v0.1.6 go.osspkg.com/goppy/xnet v0.1.1 - go.osspkg.com/goppy/xtest v0.1.2 + go.osspkg.com/goppy/xtest v0.1.3 go.osspkg.com/static v1.4.0 ) diff --git a/ws/go.mod b/ws/go.mod index f18109b..73b5e3d 100644 --- a/ws/go.mod +++ b/ws/go.mod @@ -15,11 +15,11 @@ require ( github.com/gorilla/websocket v1.5.1 github.com/mailru/easyjson v0.7.7 go.osspkg.com/goppy/errors v0.1.0 - go.osspkg.com/goppy/iosync v0.1.3 + go.osspkg.com/goppy/iosync v0.1.4 go.osspkg.com/goppy/plugins v0.1.1 go.osspkg.com/goppy/xc v0.1.0 - go.osspkg.com/goppy/xlog v0.1.5 - go.osspkg.com/goppy/xtest v0.1.2 + go.osspkg.com/goppy/xlog v0.1.6 + go.osspkg.com/goppy/xtest v0.1.3 ) require ( diff --git a/xdns/go.mod b/xdns/go.mod index 4d48f4e..0410829 100644 --- a/xdns/go.mod +++ b/xdns/go.mod @@ -14,10 +14,10 @@ replace ( require ( github.com/miekg/dns v1.1.57 go.osspkg.com/goppy/errors v0.1.0 - go.osspkg.com/goppy/iosync v0.1.3 + go.osspkg.com/goppy/iosync v0.1.4 go.osspkg.com/goppy/plugins v0.1.1 go.osspkg.com/goppy/xc v0.1.0 - go.osspkg.com/goppy/xlog v0.1.5 + go.osspkg.com/goppy/xlog v0.1.6 go.osspkg.com/goppy/xnet v0.1.1 ) diff --git a/xlog/go.mod b/xlog/go.mod index e9cb89b..60508d8 100644 --- a/xlog/go.mod +++ b/xlog/go.mod @@ -9,8 +9,8 @@ replace ( require ( github.com/mailru/easyjson v0.7.7 - go.osspkg.com/goppy/iosync v0.1.3 - go.osspkg.com/goppy/xtest v0.1.2 + go.osspkg.com/goppy/iosync v0.1.4 + go.osspkg.com/goppy/xtest v0.1.3 ) require github.com/josharian/intern v1.0.0 // indirect diff --git a/xtest/errors.go b/xtest/errors.go index c976910..2875e54 100644 --- a/xtest/errors.go +++ b/xtest/errors.go @@ -5,6 +5,8 @@ package xtest +import "strings" + func NoError(t IUnitTest, err error, args ...interface{}) { if err == nil { return @@ -22,3 +24,19 @@ func Error(t IUnitTest, err error, args ...interface{}) { t.Errorf(errorMessage(args, "Want error, but got ")) t.FailNow() } + +func ErrorContains(t IUnitTest, err error, need string, args ...interface{}) { + if err == nil { + t.Helper() + t.Errorf(errorMessage(args, "Want error, but got ")) + t.FailNow() + return + } + + if strings.Contains(err.Error(), need) { + return + } + t.Helper() + t.Errorf(errorMessage(args, "Not found\nSearchData: %+v\nNeed: %+v", err.Error(), need)) + t.FailNow() +}