summaryrefslogtreecommitdiff
path: root/server/object/object.go
diff options
context:
space:
mode:
Diffstat (limited to 'server/object/object.go')
-rw-r--r--server/object/object.go124
1 files changed, 120 insertions, 4 deletions
diff --git a/server/object/object.go b/server/object/object.go
index e0d0239..e05ae0f 100644
--- a/server/object/object.go
+++ b/server/object/object.go
@@ -3,31 +3,143 @@ package object
import (
"citrons.xyz/talk/proto"
"citrons.xyz/talk/server/session"
+ "citrons.xyz/talk/server/validate"
+ "bufio"
+ "bytes"
+ "log"
+ bolt "go.etcd.io/bbolt"
)
type Object interface {
SendRequest(session.Request)
InfoFor(uid string) proto.Object
+ Data() proto.Object
+}
+
+type HasHandle interface {
+ Handle() string
+}
+
+type Kind interface {
+ Undata(o proto.Object) Object
}
type World struct {
+ db *bolt.DB
+ kinds map[string]Kind
objects map[string]Object
}
-func NewWorld() *World {
- return &World {make(map[string]Object)}
+func NewWorld(db *bolt.DB) *World {
+ w := &World {db, make(map[string]Kind), make(map[string]Object)}
+ w.AddObjectKind("gone", TombstoneKind{})
+ return w
+}
+
+func (w *World) AddObjectKind(name string, kind Kind) {
+ w.kinds[name] = kind
+}
+
+func (w *World) getData(id string) proto.Object {
+ var data []byte
+ err := w.db.View(func (tx *bolt.Tx) error {
+ bucket := tx.Bucket([]byte("world"))
+ if bucket != nil {
+ data = bucket.Get([]byte(id))
+ }
+ return nil
+ })
+ if err != nil {
+ log.Fatal("reading database: ", err)
+ }
+ if len(data) == 0 {
+ return proto.Object {}
+ }
+ o, err := proto.ReadObject(bufio.NewReader(bytes.NewReader(data)))
+ if err != nil {
+ panic(err)
+ }
+ return o
+}
+
+func (w *World) setData(id string, o proto.Object) {
+ var buf bytes.Buffer
+ writer := bufio.NewWriter(&buf)
+ proto.WriteObject(writer, o)
+ writer.Flush()
+ err := w.db.Update(func (tx *bolt.Tx) error {
+ bucket, _ := tx.CreateBucketIfNotExists([]byte("world"))
+ return bucket.Put([]byte(id), buf.Bytes())
+ })
+ if err != nil {
+ log.Fatal("updating database: ", err)
+ }
}
func (w *World) GetObject(id string) Object {
+ if w.objects[id] == nil {
+ o := w.getData(id)
+ if o.Kind != "" {
+ w.objects[id] = w.kinds[o.Kind].Undata(o)
+ }
+ }
return w.objects[id]
}
func (w *World) PutObject(id string, o Object) {
w.objects[id] = o
+ if id == "" {
+ return
+ }
+ switch h := o.(type) {
+ case HasHandle:
+ err := w.db.Update(func(tx *bolt.Tx) error {
+ kinds, _ := tx.CreateBucketIfNotExists([]byte("kinds"))
+ kind, _ := kinds.CreateBucketIfNotExists([]byte(o.Data().Kind))
+ byHandle, _ := kind.CreateBucketIfNotExists([]byte("by handle"))
+ byId, _ := kind.CreateBucketIfNotExists([]byte("by id"))
+
+ existing := byId.Get([]byte(id))
+ if len(existing) != 0 {
+ byHandle.Delete(existing)
+ }
+ handle := []byte(validate.Fold(h.Handle()))
+ log.Println(string(handle), o.Data().Kind)
+ byHandle.Put(handle, []byte(id))
+ byId.Put([]byte(id), handle)
+
+ return nil
+ })
+ if err != nil {
+ log.Fatal("updating database: ", err)
+ }
+ }
+ w.setData(id, o.Data())
}
-func (w *World) RemoveObject(id string) {
- w.objects[id] = nil
+func (w *World) Lookup(kind string, handle string) Object {
+ handle = validate.Fold(handle)
+
+ var id string
+ err := w.db.View(func(tx *bolt.Tx) error {
+ kinds := tx.Bucket([]byte("kinds"))
+ if kinds == nil {
+ return nil
+ }
+ kind := kinds.Bucket([]byte(kind))
+ if kind == nil {
+ return nil
+ }
+ id = string(kind.Bucket([]byte("by handle")).Get([]byte(handle)))
+ return nil
+ })
+ if err != nil {
+ log.Fatal("reading database: ", err)
+ }
+ if id != "" {
+ return w.GetObject(id)
+ }
+ return nil
}
func (w *World) NewObject(o Object) string {
@@ -35,3 +147,7 @@ func (w *World) NewObject(o Object) string {
w.PutObject(id, o)
return id
}
+
+func (w *World) DB() *bolt.DB {
+ return w.db
+}