e476a7e1b10a3e42e50cff70a8671e10cd6c231d
[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         if err != nil {
95                 return
96         }
97         return
98 }
99
100 // Append v to value of k
101 func (c *Conn) Append(name, k string, v []byte) (err error) {
102         fn := filepath.Join(c.path, name, k)
103         f, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY|os.O_SYNC, 0666)
104         if err != nil {
105                 return
106         }
107         defer f.Close()
108         _, err = f.Write(v)
109         return
110 }
111
112 // Close db connection
113 func (c *Conn) Close() (err error) {
114         return
115 }