store: minor fixes
[epoint] / pkg / store / store.go
1 package store
2
3 // persistent key-value store
4 // multiple key-value store can be managed by a single db connection
5 // each store has a name, before usage the name of the store must be
6 // ensured to exist
7 //
8 // TODO: this is a toy implementation
9
10 import (
11         "io/ioutil"
12         "os"
13         "path/filepath"
14 )
15
16 type Conn struct {
17         path string
18 }
19
20 type NotFoundError struct {
21         Path string
22 }
23
24 type AlreadyExistsError struct {
25         Path string
26 }
27
28 func (e NotFoundError) Error() string {
29         return "not found: " + e.Path
30 }
31
32 func (e AlreadyExistsError) Error() string {
33         return "already exists: " + e.Path
34 }
35
36 // Open db connection
37 func Open(root string) (c *Conn, err error) {
38         c = new(Conn)
39         c.path, err = filepath.Abs(root)
40         if err != nil {
41                 return
42         }
43         err = os.MkdirAll(c.path, 0755)
44         if err != nil {
45                 return
46         }
47         return
48 }
49
50 // Get the value of k
51 func (c *Conn) Get(name, k string) (v []byte, err error) {
52         v, err = ioutil.ReadFile(filepath.Join(c.path, name, k))
53         if err != nil {
54                 if p, ok := err.(*os.PathError); ok && p.Err == os.ENOENT {
55                         err = NotFoundError{name + "/" + k}
56                 }
57         }
58         return
59 }
60
61 // Ensure k-v store with the given name exists
62 func (c *Conn) Ensure(name string) (err error) {
63         return os.MkdirAll(filepath.Join(c.path, name), 0755)
64 }
65
66 // Set k to v
67 func (c *Conn) Set(name, k string, v []byte) (err error) {
68         fn := filepath.Join(c.path, name, k)
69         f, err := os.OpenFile(fn+".tmp", os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_SYNC, 0666)
70         if err != nil {
71                 return
72         }
73         defer f.Close()
74         _, err = f.Write(v)
75         if err != nil {
76                 return
77         }
78         err = os.Rename(fn+".tmp", fn)
79         return
80 }
81
82 // Set k to v, but fail if k already exists
83 func (c *Conn) Insert(name, k string, v []byte) (err error) {
84         fn := filepath.Join(c.path, name, k)
85         f, err := os.OpenFile(fn, os.O_CREATE|os.O_EXCL|os.O_WRONLY|os.O_SYNC, 0666)
86         if err != nil {
87                 if p, ok := err.(*os.PathError); ok && p.Err == os.EEXIST {
88                         err = AlreadyExistsError{name + "/" + k}
89                 }
90                 return
91         }
92         defer f.Close()
93         _, err = f.Write(v)
94         return
95 }
96
97 // Append v to value of k
98 func (c *Conn) Append(name, k string, v []byte) (err error) {
99         fn := filepath.Join(c.path, name, k)
100         f, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY|os.O_SYNC, 0666)
101         if err != nil {
102                 return
103         }
104         defer f.Close()
105         _, err = f.Write(v)
106         return
107 }
108
109 // Close db connection
110 func (c *Conn) Close() (err error) {
111         return
112 }