mirror of
https://blitiri.com.ar/repos/chasquid
synced 2025-12-18 14:47:03 +00:00
protoio: Add a generic protobuf store
This patch adds a generic protobuf store, where one can put and retreive protobufs, indexed by a string. This will be used in subsequent patches.
This commit is contained in:
@@ -3,7 +3,9 @@ package protoio
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"blitiri.com.ar/go/chasquid/internal/safeio"
|
"blitiri.com.ar/go/chasquid/internal/safeio"
|
||||||
|
|
||||||
@@ -46,3 +48,62 @@ func WriteTextMessage(fname string, pb proto.Message, perm os.FileMode) error {
|
|||||||
out := proto.MarshalTextString(pb)
|
out := proto.MarshalTextString(pb)
|
||||||
return safeio.WriteFile(fname, []byte(out), perm)
|
return safeio.WriteFile(fname, []byte(out), perm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Store represents a persistent protocol buffer message store.
|
||||||
|
type Store struct {
|
||||||
|
// Directory where the store is.
|
||||||
|
dir string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStore returns a new Store instance. It will create dir if needed.
|
||||||
|
func NewStore(dir string) (*Store, error) {
|
||||||
|
s := &Store{dir}
|
||||||
|
err := os.MkdirAll(dir, 0770)
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const storeIDPrefix = "s:"
|
||||||
|
|
||||||
|
// idToFname takes a generic id and returns the corresponding file for it
|
||||||
|
// (which may or may not exist).
|
||||||
|
func (s *Store) idToFname(id string) string {
|
||||||
|
return s.dir + "/" + storeIDPrefix + url.QueryEscape(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) Put(id string, m proto.Message) error {
|
||||||
|
return WriteTextMessage(s.idToFname(id), m, 0660)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) Get(id string, m proto.Message) (bool, error) {
|
||||||
|
err := ReadTextMessage(s.idToFname(id), m)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return err == nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) ListIDs() ([]string, error) {
|
||||||
|
ids := []string{}
|
||||||
|
|
||||||
|
entries, err := ioutil.ReadDir(s.dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, e := range entries {
|
||||||
|
if !strings.HasPrefix(e.Name(), storeIDPrefix) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
id := e.Name()[len(storeIDPrefix):]
|
||||||
|
id, err = url.QueryUnescape(id)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,3 +65,41 @@ func TestText(t *testing.T) {
|
|||||||
os.RemoveAll(dir)
|
os.RemoveAll(dir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStore(t *testing.T) {
|
||||||
|
dir := mustTempDir(t)
|
||||||
|
st, err := NewStore(dir + "/store")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create store: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ids, err := st.ListIDs(); len(ids) != 0 || err != nil {
|
||||||
|
t.Errorf("expected no ids, got %v - %v", ids, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pb := &testpb.M{"hola"}
|
||||||
|
|
||||||
|
if err := st.Put("f", pb); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pb2 := &testpb.M{}
|
||||||
|
if ok, err := st.Get("f", pb2); err != nil || !ok {
|
||||||
|
t.Errorf("Get(f): %v - %v", ok, err)
|
||||||
|
}
|
||||||
|
if pb.Content != pb2.Content {
|
||||||
|
t.Errorf("content mismatch, got %q, expected %q", pb2.Content, pb.Content)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok, err := st.Get("notexists", pb2); err != nil || ok {
|
||||||
|
t.Errorf("Get(notexists): %v - %v", ok, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ids, err := st.ListIDs(); len(ids) != 1 || ids[0] != "f" || err != nil {
|
||||||
|
t.Errorf("expected [f], got %v - %v", ids, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !t.Failed() {
|
||||||
|
os.RemoveAll(dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user