package store // persistent key-value store // multiple key-value store can be managed by a single db connection // each store has a name, before usage the name of the store must be // ensured to exist // // TODO: this is a toy implementation import ( "io/ioutil" "os" "path/filepath" ) type Conn struct { path string } type NotFoundError struct { Path string } type AlreadyExistsError struct { Path string } func (e NotFoundError) Error() string { return "not found: " + e.Path } func (e AlreadyExistsError) Error() string { return "already exists: " + e.Path } // Open db connection func Open(root string) (c *Conn, err error) { c = new(Conn) c.path, err = filepath.Abs(root) if err != nil { return } err = os.MkdirAll(c.path, 0755) if err != nil { return } return } // Get the value of k func (c *Conn) Get(name, k string) (v []byte, err error) { v, err = ioutil.ReadFile(filepath.Join(c.path, name, k)) if err != nil { if p, ok := err.(*os.PathError); ok && p.Err == os.ENOENT { err = NotFoundError{name + "/" + k} } } return } // Ensure k-v store with the given name exists func (c *Conn) Ensure(name string) (err error) { return os.MkdirAll(filepath.Join(c.path, name), 0755) } // Set k to v func (c *Conn) Set(name, k string, v []byte) (err error) { fn := filepath.Join(c.path, name, k) f, err := os.OpenFile(fn+".tmp", os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_SYNC, 0666) if err != nil { return } defer f.Close() _, err = f.Write(v) if err != nil { return } err = os.Rename(fn+".tmp", fn) return } // Set k to v, but fail if k already exists func (c *Conn) Insert(name, k string, v []byte) (err error) { fn := filepath.Join(c.path, name, k) f, err := os.OpenFile(fn, os.O_CREATE|os.O_EXCL|os.O_WRONLY|os.O_SYNC, 0666) if err != nil { if p, ok := err.(*os.PathError); ok && p.Err == os.EEXIST { err = AlreadyExistsError{name + "/" + k} } return } defer f.Close() _, err = f.Write(v) return } // Append v to value of k func (c *Conn) Append(name, k string, v []byte) (err error) { fn := filepath.Join(c.path, name, k) f, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY|os.O_SYNC, 0666) if err != nil { return } defer f.Close() _, err = f.Write(v) return } // Close db connection func (c *Conn) Close() (err error) { return }