diff --git a/config/components/database/client.go b/config/components/database/client.go index c0040e8..801849b 100644 --- a/config/components/database/client.go +++ b/config/components/database/client.go @@ -29,8 +29,9 @@ package database import ( "context" + "github.com/nabbar/golib/database/gorm" + cfgtps "github.com/nabbar/golib/config/types" - libdbs "github.com/nabbar/golib/database" liberr "github.com/nabbar/golib/errors" libver "github.com/nabbar/golib/version" libvpr "github.com/nabbar/golib/viper" @@ -159,8 +160,8 @@ func (o *componentDatabase) _runCli() liberr.Error { var ( err liberr.Error prt = ErrorComponentReload - dbo libdbs.Database - cfg *libdbs.Config + dbo gorm.Database + cfg *gorm.Config ) if !o.IsStarted() { @@ -171,7 +172,7 @@ func (o *componentDatabase) _runCli() liberr.Error { return prt.Error(err) } - if dbo, err = libdbs.New(cfg); err != nil { + if dbo, err = gorm.New(cfg); err != nil { return prt.Error(err) } diff --git a/config/components/database/config.go b/config/components/database/config.go index e21c0eb..8927141 100644 --- a/config/components/database/config.go +++ b/config/components/database/config.go @@ -29,7 +29,8 @@ package database import ( "time" - libdbs "github.com/nabbar/golib/database" + "github.com/nabbar/golib/database/gorm" + liberr "github.com/nabbar/golib/errors" spfcbr "github.com/spf13/cobra" spfvpr "github.com/spf13/viper" @@ -107,10 +108,10 @@ func (o *componentDatabase) RegisterFlag(Command *spfcbr.Command) error { return nil } -func (o *componentDatabase) _getConfig() (*libdbs.Config, liberr.Error) { +func (o *componentDatabase) _getConfig() (*gorm.Config, liberr.Error) { var ( key string - cfg libdbs.Config + cfg gorm.Config vpr *spfvpr.Viper err liberr.Error ) @@ -129,7 +130,7 @@ func (o *componentDatabase) _getConfig() (*libdbs.Config, liberr.Error) { cfg.RegisterContext(o.x.GetContext) if val := vpr.GetString(key + ".driver"); val != "" { - cfg.Driver = libdbs.DriverFromString(val) + cfg.Driver = gorm.DriverFromString(val) } if val := vpr.GetString(key + ".name"); val != "" { cfg.Name = val diff --git a/config/components/database/interface.go b/config/components/database/interface.go index b54f2c9..10b29fc 100644 --- a/config/components/database/interface.go +++ b/config/components/database/interface.go @@ -30,10 +30,11 @@ import ( "sync" "time" + libdbs "github.com/nabbar/golib/database/gorm" + libcfg "github.com/nabbar/golib/config" cfgtps "github.com/nabbar/golib/config/types" libctx "github.com/nabbar/golib/context" - libdbs "github.com/nabbar/golib/database" ) type ComponentDatabase interface { diff --git a/config/components/database/model.go b/config/components/database/model.go index 0c9b2c7..816b564 100644 --- a/config/components/database/model.go +++ b/config/components/database/model.go @@ -30,8 +30,9 @@ import ( "sync" "time" + libdbs "github.com/nabbar/golib/database/gorm" + libctx "github.com/nabbar/golib/context" - libdbs "github.com/nabbar/golib/database" montps "github.com/nabbar/golib/monitor/types" ) diff --git a/config/components/database/monitor.go b/config/components/database/monitor.go index 7f21330..9ff786b 100644 --- a/config/components/database/monitor.go +++ b/config/components/database/monitor.go @@ -27,7 +27,7 @@ package database import ( - libdbs "github.com/nabbar/golib/database" + libdbs "github.com/nabbar/golib/database/gorm" libmon "github.com/nabbar/golib/monitor" montps "github.com/nabbar/golib/monitor/types" libver "github.com/nabbar/golib/version" diff --git a/database/config.go b/database/gorm/config.go similarity index 99% rename from database/config.go rename to database/gorm/config.go index 4efac49..9a68152 100644 --- a/database/config.go +++ b/database/gorm/config.go @@ -24,7 +24,7 @@ * */ -package database +package gorm import ( "database/sql" diff --git a/database/driver.go b/database/gorm/driver.go similarity index 99% rename from database/driver.go rename to database/gorm/driver.go index 0fa601a..56c6206 100644 --- a/database/driver.go +++ b/database/gorm/driver.go @@ -27,7 +27,7 @@ * */ -package database +package gorm import ( "strings" diff --git a/database/driver_darwin.go b/database/gorm/driver_darwin.go similarity index 99% rename from database/driver_darwin.go rename to database/gorm/driver_darwin.go index a5cd7cb..1b37a62 100644 --- a/database/driver_darwin.go +++ b/database/gorm/driver_darwin.go @@ -27,7 +27,7 @@ * */ -package database +package gorm import ( "strings" diff --git a/database/errors.go b/database/gorm/errors.go similarity index 77% rename from database/errors.go rename to database/gorm/errors.go index b3b5e33..d2231ee 100644 --- a/database/errors.go +++ b/database/gorm/errors.go @@ -24,12 +24,18 @@ * */ -package database +package gorm -import "github.com/nabbar/golib/errors" +import ( + "fmt" + + liberr "github.com/nabbar/golib/errors" +) + +const pkgName = "golib/database/gorm" const ( - ErrorParamsEmpty errors.CodeError = iota + errors.MinPkgDatabase + ErrorParamEmpty liberr.CodeError = iota + liberr.MinPkgDatabaseGorm ErrorDatabaseOpen ErrorDatabaseOpenPool ErrorValidatorError @@ -38,22 +44,18 @@ const ( ErrorDatabasePing ) -var isCodeError = false - -func IsCodeError() bool { - return isCodeError -} - func init() { - isCodeError = errors.ExistInMapMessage(ErrorParamsEmpty) - errors.RegisterIdFctMessage(ErrorParamsEmpty, getMessage) + if liberr.ExistInMapMessage(ErrorParamEmpty) { + panic(fmt.Errorf("error code collision with package %s", pkgName)) + } + liberr.RegisterIdFctMessage(ErrorParamEmpty, getMessage) } -func getMessage(code errors.CodeError) (message string) { +func getMessage(code liberr.CodeError) (message string) { switch code { - case errors.UNK_ERROR: - return "" - case ErrorParamsEmpty: + case liberr.UnknownError: + return liberr.NullMessage + case ErrorParamEmpty: return "given parameters is empty" case ErrorDatabaseOpen: return "database : start connection to dsn" @@ -69,5 +71,5 @@ func getMessage(code errors.CodeError) (message string) { return "database : ping error" } - return "" + return liberr.NullMessage } diff --git a/database/interface.go b/database/gorm/interface.go similarity index 99% rename from database/interface.go rename to database/gorm/interface.go index 2fde62f..e6a45d5 100644 --- a/database/interface.go +++ b/database/gorm/interface.go @@ -24,7 +24,7 @@ * */ -package database +package gorm import ( "context" diff --git a/database/model.go b/database/gorm/model.go similarity index 99% rename from database/model.go rename to database/gorm/model.go index dea68fe..7a49252 100644 --- a/database/model.go +++ b/database/gorm/model.go @@ -24,7 +24,7 @@ * */ -package database +package gorm import ( "context" diff --git a/database/monitor.go b/database/gorm/monitor.go similarity index 99% rename from database/monitor.go rename to database/gorm/monitor.go index a193699..497de76 100644 --- a/database/monitor.go +++ b/database/gorm/monitor.go @@ -24,7 +24,7 @@ * */ -package database +package gorm import ( "context" diff --git a/database/kvdriver/errors.go b/database/kvdriver/errors.go new file mode 100644 index 0000000..3fc963c --- /dev/null +++ b/database/kvdriver/errors.go @@ -0,0 +1,72 @@ +/* + * MIT License + * + * Copyright (c) 2022 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package kvdriver + +import ( + "fmt" + + liberr "github.com/nabbar/golib/errors" +) + +const pkgName = "golib/database/kvdriver" + +const ( + ErrorParamEmpty liberr.CodeError = iota + liberr.MinPkgDatabaseKVDrv + ErrorBadInstance + ErrorGetFunction + ErrorSetFunction + ErrorListFunction + ErrorFunctionParams +) + +func init() { + if liberr.ExistInMapMessage(ErrorParamEmpty) { + panic(fmt.Errorf("error code collision with package %s", pkgName)) + } + liberr.RegisterIdFctMessage(ErrorParamEmpty, getMessage) +} + +func getMessage(code liberr.CodeError) (message string) { + switch code { + case liberr.UnknownError: + return liberr.NullMessage + case ErrorParamEmpty: + return "given parameters is empty" + case ErrorBadInstance: + return "bad instance of " + pkgName + case ErrorGetFunction: + return "missing get function of " + pkgName + case ErrorSetFunction: + return "missing set function of " + pkgName + case ErrorListFunction: + return "missing list function of " + pkgName + case ErrorFunctionParams: + return "missing function params" + } + + return liberr.NullMessage +} diff --git a/database/kvdriver/interface.go b/database/kvdriver/interface.go new file mode 100644 index 0000000..703a41b --- /dev/null +++ b/database/kvdriver/interface.go @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package kvdriver + +type FctWalk[K comparable, M any] func(key K, model M) bool + +type KVDriver[K comparable, M any] interface { + Get(key K, model *M) error + Set(key K, model M) error + List() ([]K, error) + Walk(fct FctWalk[K, M]) error +} + +type FuncGet[K comparable, M any] func(key K) (M, error) +type FuncSet[K comparable, M any] func(key K, model M) error +type FuncList[K comparable, M any] func() ([]K, error) +type FuncWalk[K comparable, M any] func(fct FctWalk[K, M]) error + +type Driver[K comparable, M any] struct { + KVDriver[K, M] + + FctGet FuncGet[K, M] + FctSet FuncSet[K, M] + FctList FuncList[K, M] + FctWalk FuncWalk[K, M] // optional +} diff --git a/database/kvdriver/model.go b/database/kvdriver/model.go new file mode 100644 index 0000000..af5918f --- /dev/null +++ b/database/kvdriver/model.go @@ -0,0 +1,91 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package kvdriver + +func (o *Driver[K, M]) Get(key K, model *M) error { + if o == nil { + return ErrorBadInstance.Error(nil) + } else if o.FctGet == nil { + return ErrorGetFunction.Error(nil) + } else { + m, e := o.FctGet(key) + *model = m + return e + } +} + +func (o *Driver[K, M]) Set(key K, model M) error { + if o == nil { + return ErrorBadInstance.Error(nil) + } else if o.FctSet == nil { + return ErrorSetFunction.Error(nil) + } else { + return o.FctSet(key, model) + } +} + +func (o *Driver[K, M]) List() ([]K, error) { + if o == nil { + return nil, ErrorBadInstance.Error(nil) + } else if o.FctList == nil { + return nil, ErrorListFunction.Error(nil) + } else { + return o.FctList() + } +} + +func (o *Driver[K, M]) Walk(fct FctWalk[K, M]) error { + if o == nil { + return ErrorBadInstance.Error(nil) + } else if fct == nil { + return ErrorFunctionParams.Error(nil) + } else if o.FctWalk == nil { + return o.fakeWalk(fct) + } else { + return o.FctWalk(fct) + } +} + +func (o *Driver[K, M]) fakeWalk(fct FctWalk[K, M]) error { + if l, e := o.List(); e != nil { + return e + } else { + for _, k := range l { + var m = *(new(M)) + + if er := o.Get(k, &m); er != nil { + return er + } + + if !fct(k, m) { + return nil + } + } + } + + return nil +} diff --git a/database/kvitem/errors.go b/database/kvitem/errors.go new file mode 100644 index 0000000..686ef92 --- /dev/null +++ b/database/kvitem/errors.go @@ -0,0 +1,63 @@ +/* + * MIT License + * + * Copyright (c) 2022 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package kvitem + +import ( + "fmt" + + liberr "github.com/nabbar/golib/errors" +) + +const pkgName = "golib/database/kvitem" + +const ( + ErrorParamEmpty liberr.CodeError = iota + liberr.MinPkgDatabaseKVItm + ErrorLoadFunction + ErrorStoreFunction +) + +func init() { + if liberr.ExistInMapMessage(ErrorParamEmpty) { + panic(fmt.Errorf("error code collision with package %s", pkgName)) + } + liberr.RegisterIdFctMessage(ErrorParamEmpty, getMessage) +} + +func getMessage(code liberr.CodeError) (message string) { + switch code { + case liberr.UnknownError: + return liberr.NullMessage + case ErrorParamEmpty: + return "given parameters is empty" + case ErrorLoadFunction: + return "missing load function of " + pkgName + case ErrorStoreFunction: + return "missing store function of " + pkgName + } + + return liberr.NullMessage +} diff --git a/database/kvitem/interface.go b/database/kvitem/interface.go new file mode 100644 index 0000000..1dec9c3 --- /dev/null +++ b/database/kvitem/interface.go @@ -0,0 +1,65 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package kvitem + +import "sync/atomic" + +type FuncLoad[K comparable, M any] func(key K, model *M) error +type FuncStore[K comparable, M any] func(key K, model M) error + +type KVItem[K comparable, M any] interface { + Set(model M) + Get() M + + Load() error + Store(force bool) error + Clean() + + HasChange() bool + + RegisterFctLoad(fct FuncLoad[K, M]) + RegisterFctStore(fct FuncStore[K, M]) +} + +func New[K comparable, M any](key K) KVItem[K, M] { + var ( + ml = new(atomic.Value) + mw = new(atomic.Value) + ) + + ml.Store(nil) + mw.Store(nil) + + return &itm[K, M]{ + k: key, + ml: ml, + ms: mw, + fl: nil, + fs: nil, + } + +} diff --git a/database/kvitem/model.go b/database/kvitem/model.go new file mode 100644 index 0000000..4a8b3f9 --- /dev/null +++ b/database/kvitem/model.go @@ -0,0 +1,203 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package kvitem + +import ( + "reflect" + "sync/atomic" +) + +type itm[K comparable, M any] struct { + k K // key + + ml *atomic.Value // model read + ms *atomic.Value // model write + + fl *atomic.Value + fs *atomic.Value +} + +func (o *itm[K, M]) RegisterFctLoad(fct FuncLoad[K, M]) { + o.fl.Store(fct) +} + +func (o *itm[K, M]) getFctLoad() FuncLoad[K, M] { + if o == nil { + return nil + } + + i := o.fs.Load() + if i == nil { + return nil + } else if f, k := i.(FuncLoad[K, M]); !k { + return nil + } else { + return f + } +} + +func (o *itm[K, M]) RegisterFctStore(fct FuncStore[K, M]) { + o.fs.Store(fct) +} + +func (o *itm[K, M]) getFctStore() FuncStore[K, M] { + if o == nil { + return nil + } + + i := o.fs.Load() + if i == nil { + return nil + } else if f, k := i.(FuncStore[K, M]); !k { + return nil + } else { + return f + } +} + +func (o *itm[K, M]) Set(model M) { + if o == nil { + return + } + + m := o.ml.Load() + + // model not loaded, so store new model + if m == nil { + o.ms.Store(model) + // model loaded and new model given not same, so store new model + } else if !reflect.DeepEqual(m.(M), model) { + o.ms.Store(model) + // model loaded and given model are same, so don't store new model + } else { + o.ms.Store(nil) + } +} + +func (o *itm[K, M]) Get() M { + if o == nil { + return *(new(M)) + } + + // update exist so latest fresh value + m := o.ms.Load() + if m != nil { + if v, k := m.(M); k { + return v + } + } + + // load model exist so return last model load + m = o.ml.Load() + if m != nil { + if v, k := m.(M); k { + return v + } + } + + // nothing load, so return new instance + return *(new(M)) +} + +func (o *itm[K, M]) Load() error { + var fct FuncLoad[K, M] + + if fct = o.getFctLoad(); fct == nil { + return ErrorLoadFunction.Error(nil) + } + + m := *(new(M)) + e := fct(o.k, &m) + + if e == nil { + o.ml.Store(m) + } + + return e +} + +func (o *itm[K, M]) Store(force bool) error { + var fct FuncStore[K, M] + + if fct = o.getFctStore(); fct == nil { + return ErrorStoreFunction.Error(nil) + } + + m := o.ms.Load() + if m != nil { + return fct(o.k, m.(M)) + } else if !force { + return nil + } + + // no update, but force store, so use load model + m = o.ml.Load() + if m != nil { + return fct(o.k, m.(M)) + } + + // no update and no load, but force store, so use new instance of model + m = *(new(M)) + return fct(o.k, m.(M)) +} + +func (o *itm[K, M]) Clean() { + o.ml.Store(nil) + o.ms.Store(nil) +} + +func (o *itm[K, M]) HasChange() bool { + r := o.ml.Load() + w := o.ms.Load() + + if r == nil && w == nil { + // not loaded and not store, so no change + return false + } else if r == nil { + // not loaded but store is set, so has been updated + return true + } else if w == nil { + // loaded and not store, so no change + return false + } + + mr, kr := r.(M) + mw, kw := w.(M) + + if !kr && !kw { + // no valid model, so no change + return false + } else if !kr { + // not valid model for load, but valid for store, so has been updated + return true + } else if !kw { + // valid model for load, but not valid for store, so like no change + return false + } + + return !reflect.DeepEqual(mr, mw) +} diff --git a/database/kvmap/errors.go b/database/kvmap/errors.go new file mode 100644 index 0000000..f8dd084 --- /dev/null +++ b/database/kvmap/errors.go @@ -0,0 +1,72 @@ +/* + * MIT License + * + * Copyright (c) 2022 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package kvmap + +import ( + "fmt" + + liberr "github.com/nabbar/golib/errors" +) + +const pkgName = "golib/database/kvmap" + +const ( + ErrorParamEmpty liberr.CodeError = iota + liberr.MinPkgDatabaseKVMap + ErrorBadInstance + ErrorGetFunction + ErrorSetFunction + ErrorListFunction + ErrorFunctionParams +) + +func init() { + if liberr.ExistInMapMessage(ErrorParamEmpty) { + panic(fmt.Errorf("error code collision with package %s", pkgName)) + } + liberr.RegisterIdFctMessage(ErrorParamEmpty, getMessage) +} + +func getMessage(code liberr.CodeError) (message string) { + switch code { + case liberr.UnknownError: + return liberr.NullMessage + case ErrorParamEmpty: + return "given parameters is empty" + case ErrorBadInstance: + return "bad instance of " + pkgName + case ErrorGetFunction: + return "missing get function of " + pkgName + case ErrorSetFunction: + return "missing set function of " + pkgName + case ErrorListFunction: + return "missing list function of " + pkgName + case ErrorFunctionParams: + return "missing function params" + } + + return liberr.NullMessage +} diff --git a/database/kvmap/interface.go b/database/kvmap/interface.go new file mode 100644 index 0000000..f26b3f0 --- /dev/null +++ b/database/kvmap/interface.go @@ -0,0 +1,43 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package kvmap + +import ( + libkvd "github.com/nabbar/golib/database/kvdriver" +) + +type FuncGet[K comparable, MK comparable] func(key K) (map[MK]any, error) +type FuncSet[K comparable, MK comparable] func(key K, model map[MK]any) error +type FuncList[K comparable, MK comparable] func() ([]K, error) + +type Driver[K comparable, MK comparable, M any] struct { + libkvd.KVDriver[K, M] + + FctGet FuncGet[K, MK] + FctSet FuncSet[K, MK] + FctList FuncList[K, MK] +} diff --git a/database/kvmap/model.go b/database/kvmap/model.go new file mode 100644 index 0000000..e9dcf04 --- /dev/null +++ b/database/kvmap/model.go @@ -0,0 +1,109 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package kvmap + +import ( + "encoding/json" + + libkvd "github.com/nabbar/golib/database/kvdriver" +) + +func (o *Driver[K, MK, M]) serialize(model *M, modelMap *map[MK]any) error { + if p, e := json.Marshal(model); e != nil { + return e + } else { + return json.Unmarshal(p, modelMap) + } +} + +func (o *Driver[K, MK, M]) unSerialize(modelMap *map[MK]any, model *M) error { + if p, e := json.Marshal(modelMap); e != nil { + return e + } else { + return json.Unmarshal(p, model) + } +} + +func (o *Driver[K, MK, M]) Get(key K, model *M) error { + if o == nil { + return ErrorBadInstance.Error(nil) + } else if o.FctGet == nil { + return ErrorGetFunction.Error(nil) + } else if m, e := o.FctGet(key); e != nil { + return e + } else { + return o.unSerialize(&m, model) + } +} + +func (o *Driver[K, MK, M]) Set(key K, model M) error { + var m = make(map[MK]any) + + if o == nil { + return ErrorBadInstance.Error(nil) + } else if o.FctSet == nil { + return ErrorSetFunction.Error(nil) + } else if e := o.serialize(&model, &m); e != nil { + return e + } else { + return o.FctSet(key, m) + } +} + +func (o *Driver[K, MK, M]) List() ([]K, error) { + if o == nil { + return nil, ErrorBadInstance.Error(nil) + } else if o.FctList == nil { + return nil, ErrorListFunction.Error(nil) + } else { + return o.FctList() + } +} + +func (o *Driver[K, MK, M]) Walk(fct libkvd.FctWalk[K, M]) error { + if o == nil { + return ErrorBadInstance.Error(nil) + } else if fct == nil { + return ErrorFunctionParams.Error(nil) + } else if l, e := o.List(); e != nil { + return e + } else { + for _, k := range l { + var m = *(new(M)) + + if er := o.Get(k, &m); er != nil { + return er + } + + if !fct(k, m) { + return nil + } + } + } + + return nil +} diff --git a/database/kvtable/errors.go b/database/kvtable/errors.go new file mode 100644 index 0000000..07b10eb --- /dev/null +++ b/database/kvtable/errors.go @@ -0,0 +1,60 @@ +/* + * MIT License + * + * Copyright (c) 2022 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package kvtable + +import ( + "fmt" + + liberr "github.com/nabbar/golib/errors" +) + +const pkgName = "golib/database/kvtable" + +const ( + ErrorParamEmpty liberr.CodeError = iota + liberr.MinPkgDatabaseKVTbl + ErrorBadDriver +) + +func init() { + if liberr.ExistInMapMessage(ErrorParamEmpty) { + panic(fmt.Errorf("error code collision with package %s", pkgName)) + } + liberr.RegisterIdFctMessage(ErrorParamEmpty, getMessage) +} + +func getMessage(code liberr.CodeError) (message string) { + switch code { + case liberr.UnknownError: + return liberr.NullMessage + case ErrorParamEmpty: + return "given parameters is empty" + case ErrorBadDriver: + return "bad driver of " + pkgName + } + + return liberr.NullMessage +} diff --git a/database/kvtable/interface.go b/database/kvtable/interface.go new file mode 100644 index 0000000..6fc6a46 --- /dev/null +++ b/database/kvtable/interface.go @@ -0,0 +1,51 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package kvtable + +import ( + "sync/atomic" + + libkvd "github.com/nabbar/golib/database/kvdriver" + libkvi "github.com/nabbar/golib/database/kvitem" +) + +type FuncWalk[K comparable, M any] func(kv libkvi.KVItem[K, M]) bool + +type KVTable[K comparable, M any] interface { + Get(key K) (libkvi.KVItem[K, M], error) + List() ([]libkvi.KVItem[K, M], error) + Walk(fct FuncWalk[K, M]) error +} + +func New[K comparable, M any](drv libkvd.KVDriver[K, M]) KVTable[K, M] { + d := new(atomic.Value) + d.Store(drv) + + return &tbl[K, M]{ + d: d, + } +} diff --git a/database/kvtable/model.go b/database/kvtable/model.go new file mode 100644 index 0000000..3a6db8f --- /dev/null +++ b/database/kvtable/model.go @@ -0,0 +1,107 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package kvtable + +import ( + "sync/atomic" + + libkvd "github.com/nabbar/golib/database/kvdriver" + libkvs "github.com/nabbar/golib/database/kvitem" +) + +type tbl[K comparable, M any] struct { + d *atomic.Value +} + +func (o *tbl[K, M]) getDriver() libkvd.KVDriver[K, M] { + if o == nil { + return nil + } + + i := o.d.Load() + if i == nil { + return nil + } else if d, k := i.(libkvd.KVDriver[K, M]); !k { + return nil + } else { + return d + } +} + +func (o *tbl[K, M]) Get(key K) (libkvs.KVItem[K, M], error) { + var kvs = libkvs.New[K, M](key) + + if drv := o.getDriver(); drv == nil { + return nil, ErrorBadDriver.Error(nil) + } else { + kvs.RegisterFctLoad(drv.Get) + kvs.RegisterFctStore(drv.Set) + } + + return kvs, kvs.Load() +} + +func (o *tbl[K, M]) Walk(fct FuncWalk[K, M]) error { + if drv := o.getDriver(); drv == nil { + return ErrorBadDriver.Error(nil) + } else { + return drv.Walk(func(key K, model M) bool { + var kvs = libkvs.New[K, M](key) + + kvs.RegisterFctStore(drv.Set) + kvs.RegisterFctLoad(func(k K, m *M) error { + *m = model + return nil + }) + _ = kvs.Load() + kvs.RegisterFctLoad(drv.Get) + + return fct(kvs) + }) + } +} + +func (o *tbl[K, M]) List() ([]libkvs.KVItem[K, M], error) { + var res = make([]libkvs.KVItem[K, M], 0) + + if drv := o.getDriver(); drv == nil { + return nil, ErrorBadDriver.Error(nil) + } else if l, e := drv.List(); e != nil { + return nil, e + } else { + for _, k := range l { + var kvs = libkvs.New[K, M](k) + + kvs.RegisterFctLoad(drv.Get) + kvs.RegisterFctStore(drv.Set) + + res = append(res, kvs) + } + + return res, nil + } +} diff --git a/errors/modules.go b/errors/modules.go index 05006e8..9887d27 100644 --- a/errors/modules.go +++ b/errors/modules.go @@ -34,7 +34,11 @@ const ( MinPkgConfig = 500 MinPkgConsole = 800 MinPkgCrypt = 900 - MinPkgDatabase = 1000 + MinPkgDatabaseGorm = 1000 + MinPkgDatabaseKVDrv = 1010 + MinPkgDatabaseKVMap = 1020 + MinPkgDatabaseKVTbl = 1030 + MinPkgDatabaseKVItm = 1040 MinPkgFTPClient = 1100 MinPkgHttpCli = 1200 MinPkgHttpServer = 1300