From 9564d5656a22daf195fb7f64fc34cc899f33ffb4 Mon Sep 17 00:00:00 2001 From: risotto Date: Sun, 12 Jan 2025 02:51:51 -0700 Subject: [PATCH] pull out of old example --- .gitignore | 1 + LICENSE | 21 +++++++++++++++++++++ add.go | 1 - all.go | 8 ++++++++ count.go | 8 ++++++++ delete.go | 10 ++++++++++ example_test.go | 22 ++++++++++++++++++++++ filter.go | 16 ++++++++++++++++ get.go | 11 +++++++++++ hidden_save.go | 25 +++++++++++++++++++++++++ init.go | 21 +++++++++++++++++++++ load.go | 31 +++++++++++++++++++++++++++++++ remove.go | 1 - save.go | 1 - set.go | 12 ++++++++++++ timer.go | 14 ++++++++++++++ watchflush.go | 28 ++++++++++++++++++++++++++++ 17 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 .gitignore create mode 100644 LICENSE delete mode 100644 add.go create mode 100644 all.go create mode 100644 count.go create mode 100644 delete.go create mode 100644 example_test.go create mode 100644 filter.go create mode 100644 get.go create mode 100644 hidden_save.go create mode 100644 load.go delete mode 100644 remove.go delete mode 100644 save.go create mode 100644 set.go create mode 100644 timer.go create mode 100644 watchflush.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ccc3bb7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +file.gob diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2fdc5c2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © 2024 NAME HERE + +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. diff --git a/add.go b/add.go deleted file mode 100644 index d01fdcf..0000000 --- a/add.go +++ /dev/null @@ -1 +0,0 @@ -package gobstore diff --git a/all.go b/all.go new file mode 100644 index 0000000..090f07f --- /dev/null +++ b/all.go @@ -0,0 +1,8 @@ +package gobstore + +// All returns the entire map +func (j *GobStore[T]) All() map[string]T { + j.Lock() + defer j.Unlock() + return j.values +} diff --git a/count.go b/count.go new file mode 100644 index 0000000..47ab189 --- /dev/null +++ b/count.go @@ -0,0 +1,8 @@ +package gobstore + +// Count returns the length of the dictionary +func (j *GobStore[T]) Count() int { + j.Lock() + defer j.Unlock() + return len(j.values) +} diff --git a/delete.go b/delete.go new file mode 100644 index 0000000..f7f8170 --- /dev/null +++ b/delete.go @@ -0,0 +1,10 @@ +package gobstore + +// Delete removes a key if it exists +func (j *GobStore[T]) Delete(key string) { + j.Lock() + defer j.Unlock() + j.dirty = true + delete(j.values, key) + // j.save() +} diff --git a/example_test.go b/example_test.go new file mode 100644 index 0000000..3ffbcd8 --- /dev/null +++ b/example_test.go @@ -0,0 +1,22 @@ +package gobstore_test + +import ( + "fmt" + + "git.bivouac.wiki/use/gobstore" +) + +func ExampleStore() { + type FooBar struct{ Greeting string } + A := gobstore.NewStore[FooBar]("./", "file.gob") + A.Load() + A.Hidden_save() + //go A.WatchFlush() + A.Set("foobar", FooBar{Greeting: "Hello"}) + resp, ok := A.Get("foobar") + if !ok { + fmt.Println("missing!") + } + fmt.Println(resp.Greeting) + // Output: Hello +} diff --git a/filter.go b/filter.go new file mode 100644 index 0000000..947550d --- /dev/null +++ b/filter.go @@ -0,0 +1,16 @@ +package gobstore + +import "strings" + +// Filter performs a prefix match against all key values. +func (j *GobStore[T]) Filter(prefix string) []T { + j.Lock() + defer j.Unlock() + res := make([]T, 0) + for k, v := range j.values { + if strings.HasPrefix(k, prefix) { + res = append(res, v) + } + } + return res +} diff --git a/get.go b/get.go new file mode 100644 index 0000000..b3c3cea --- /dev/null +++ b/get.go @@ -0,0 +1,11 @@ +package gobstore + +// Get returns a value from the store if it exists +func (j *GobStore[T]) Get(key string) (T, bool) { + j.Lock() + defer j.Unlock() + // res := j.load() + v, ok := j.values[key] + // v, ok := res[key] + return v, ok +} diff --git a/hidden_save.go b/hidden_save.go new file mode 100644 index 0000000..8f5e9b2 --- /dev/null +++ b/hidden_save.go @@ -0,0 +1,25 @@ +package gobstore + +import ( + "encoding/gob" + "fmt" + "os" +) + +func (j *GobStore[T]) Hidden_save() { // exporting.... on close + // no lock + defer timer("GobStore save")() + file, err := os.Create(j.ROOT_DATA_PATH + j.path) + if err != nil { + fmt.Println(err) + } + fmt.Println("save " + j.path) + fmt.Println(len(j.values)) + enc := gob.NewEncoder(file) + err = enc.Encode(j.values) + if err != nil { + fmt.Println(err) + return + } + j.dirty = false +} diff --git a/init.go b/init.go index d01fdcf..ecdd3ae 100644 --- a/init.go +++ b/init.go @@ -1 +1,22 @@ package gobstore + +import ( + "sync" +) + +// parent type +type GobStore[T any] struct { + sync.Mutex + path string + values map[string]T + dirty bool + ROOT_DATA_PATH string +} + +// NewStore creates a new object +func NewStore[T any](ROOT_DATA_PATH string, path string) GobStore[T] { + return GobStore[T]{ + path: path, + ROOT_DATA_PATH: ROOT_DATA_PATH, + } +} diff --git a/load.go b/load.go new file mode 100644 index 0000000..55cd253 --- /dev/null +++ b/load.go @@ -0,0 +1,31 @@ +package gobstore + +import ( + "encoding/gob" + "errors" + "fmt" + "os" +) + +// Load initializes the data from disk +func (j *GobStore[T]) Load() { + // no lock + fmt.Println(j.ROOT_DATA_PATH + j.path) + file, err := os.Open(j.ROOT_DATA_PATH + j.path) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + fmt.Printf("Gob file does not exist: %s\n", j.path) + j.values = map[string]T{} + return + } + fmt.Println(err) + } + var res map[string]T + dec := gob.NewDecoder(file) + dec.Decode(&res) + if err != nil { + fmt.Println(err) + } + j.values = res + j.dirty = false +} diff --git a/remove.go b/remove.go deleted file mode 100644 index d01fdcf..0000000 --- a/remove.go +++ /dev/null @@ -1 +0,0 @@ -package gobstore diff --git a/save.go b/save.go deleted file mode 100644 index d01fdcf..0000000 --- a/save.go +++ /dev/null @@ -1 +0,0 @@ -package gobstore diff --git a/set.go b/set.go new file mode 100644 index 0000000..d75e55e --- /dev/null +++ b/set.go @@ -0,0 +1,12 @@ +package gobstore + +// Set adds or updates a value. +func (j *GobStore[T]) Set(key string, value T) { + j.Lock() + defer j.Unlock() + j.dirty = true + j.values[key] = value + // todo := j.load() + // todo[key] = value + // j.save() +} diff --git a/timer.go b/timer.go new file mode 100644 index 0000000..7dc03fd --- /dev/null +++ b/timer.go @@ -0,0 +1,14 @@ +package gobstore + +import ( + "fmt" + "time" +) + +// timer is a helper function +func timer(name string) func() { + start := time.Now() + return func() { + fmt.Printf("%s took %v\n", name, time.Since(start)) + } +} diff --git a/watchflush.go b/watchflush.go new file mode 100644 index 0000000..32bc00d --- /dev/null +++ b/watchflush.go @@ -0,0 +1,28 @@ +package gobstore + +import ( + "fmt" + "time" +) + +// WatchFlush checks every 5 minutes to save file to disk. +func (j *GobStore[T]) WatchFlush() { + fmt.Println("Watching to flush " + j.path + "...") + defer j.Unlock() // try this if it panics... + ticker := time.NewTicker(time.Minute * 5) + defer ticker.Stop() + for range ticker.C { + j.Lock() + t := timer("flushwatch") + if j.dirty { + fmt.Println("dirty, saving: " + j.path) + j.Hidden_save() + } else { + fmt.Println("clean, skipping: " + j.path) + } + t() + j.Unlock() // force unlock in loop + } + fmt.Println("finished...") + +}