aboutsummaryrefslogtreecommitdiff
path: root/dictionary.go
diff options
context:
space:
mode:
Diffstat (limited to 'dictionary.go')
-rw-r--r--dictionary.go178
1 files changed, 178 insertions, 0 deletions
diff --git a/dictionary.go b/dictionary.go
new file mode 100644
index 0000000..2dd309f
--- /dev/null
+++ b/dictionary.go
@@ -0,0 +1,178 @@
+// Copyright 2020, Shulhan <ms@kilabit.info>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package kamusd
+
+import (
+ "bytes"
+ "encoding/gob"
+ "errors"
+ "io/ioutil"
+ "log"
+ "os"
+ "sync"
+
+ "git.sr.ht/~shulhan/kamusku"
+ libio "github.com/shuLhan/share/lib/io"
+)
+
+const (
+ defStorageName = "kamus.gob"
+)
+
+//
+// dictionary contains cache of words and its definitions.
+//
+type dictionary struct {
+ sync.Mutex
+ cache map[string]*kamusku.Word
+ lastSize int
+ storagePath string
+}
+
+//
+// newDictionary create and initialize the cache for dictionary.
+//
+func newDictionary(storagePath string) (dict *dictionary, err error) {
+ if len(storagePath) == 0 {
+ storagePath = defStorageName
+ }
+
+ dict = &dictionary{
+ cache: make(map[string]*kamusku.Word),
+ storagePath: storagePath,
+ }
+
+ err = dict.load()
+ if err != nil {
+ return nil, err
+ }
+
+ return dict, nil
+}
+
+//
+// lookup the definition of word from cache or nil if not exist.
+//
+func (dict *dictionary) lookup(word string) (kata *kamusku.Word) {
+ dict.Lock()
+ kata = dict.cache[word]
+ dict.Unlock()
+ return kata
+}
+
+//
+// isChanging will return true if the last cache size is not equal with
+// current size.
+//
+func (dict *dictionary) isChanging() bool {
+ dict.Lock()
+ defer dict.Unlock()
+ return dict.lastSize != len(dict.cache)
+}
+
+//
+// load the cached dictionary from storage.
+//
+func (dict *dictionary) load() (err error) {
+ dict.Lock()
+ defer dict.Unlock()
+
+ v, err := ioutil.ReadFile(dict.storagePath)
+ if err != nil {
+ if errors.Is(err, os.ErrNotExist) {
+ return nil
+ }
+ return err
+ }
+
+ r := bytes.NewReader(v)
+
+ dec := gob.NewDecoder(r)
+ err = dec.Decode(&dict.cache)
+ if err != nil {
+ return err
+ }
+
+ // Clean up. Remove all word that contain "→" as definition.
+ for k, kata := range dict.cache {
+ for _, def := range kata.Definition {
+ if def.Value == "→" {
+ delete(dict.cache, k)
+ break
+ }
+ }
+ if len(kata.Definition) == 0 {
+ delete(dict.cache, k)
+ }
+ }
+
+ dict.lastSize = len(dict.cache)
+
+ return nil
+}
+
+//
+// set save the definition of word into cache.
+//
+func (dict *dictionary) set(word string, kata *kamusku.Word) {
+ if len(word) == 0 || kata == nil {
+ return
+ }
+
+ dict.Lock()
+ dict.cache[word] = kata
+ dict.Unlock()
+}
+
+//
+// store the cache to file only if the storage path is set.
+//
+func (dict *dictionary) store() (err error) {
+ if len(dict.storagePath) == 0 {
+ return nil
+ }
+
+ dict.Lock()
+ defer dict.Unlock()
+
+ if len(dict.cache) == 0 {
+ return nil
+ }
+
+ newStorage := dict.storagePath + ".new"
+
+ f, err := os.Create(newStorage)
+ if err != nil {
+ errc := f.Close()
+ if errc != nil {
+ log.Println("dictionary: store: ", err)
+ }
+ return err
+ }
+
+ enc := gob.NewEncoder(f)
+ err = enc.Encode(&dict.cache)
+ if err != nil {
+ errc := f.Close()
+ if errc != nil {
+ log.Println("dictionary: store: ", err)
+ }
+ return err
+ }
+
+ errc := f.Close()
+ if errc != nil {
+ log.Println("dictionary: store: ", err)
+ }
+
+ err = libio.Copy(dict.storagePath, newStorage)
+ if err != nil {
+ return err
+ }
+
+ dict.lastSize = len(dict.cache)
+
+ return nil
+}