Add store package.

This commit is contained in:
Cheer Xiao 2014-10-01 00:57:38 +02:00
parent 27f1a1e85f
commit 868d1c5d1f
7 changed files with 194 additions and 1 deletions

View File

@ -1,5 +1,5 @@
EXE := elvish
PKGS := edit eval parse util sys
PKGS := edit eval parse util sys store
PKG_PATHS := $(addprefix ./,$(PKGS)) # go tools want an explicit ./
PKG_COVERS := $(addprefix cover/,$(PKGS))

24
store/data-dir.go Normal file
View File

@ -0,0 +1,24 @@
package store
import (
"errors"
"os"
"strings"
)
var (
errEmptyHOME = errors.New("Environment variable HOME is empty")
)
// ensureDataDir ensures Elvish's data directory exists, creating it if
// necessary. It returns the path to the data directory (never with a
// trailing slash) and possible error.
func ensureDataDir() (string, error) {
home := os.Getenv("HOME")
if home == "" {
return "", errEmptyHOME
}
home = strings.TrimRight(home, "/")
ddir := home + "/.elvish"
return ddir, os.MkdirAll(ddir, 0700)
}

52
store/dir.go Normal file
View File

@ -0,0 +1,52 @@
package store
import (
"database/sql"
"github.com/coopernurse/gorp"
)
type Dir struct {
Path string
Score float64
}
const (
InitScore = 10
ScoreIncrement = 10
)
func init() {
tableAdders = append(tableAdders, func(dm *gorp.DbMap) {
t := dm.AddTable(Dir{}).SetKeys(false, "Path")
t.ColMap("Path").SetUnique(true)
})
}
func (s *Store) AddDir(d string) error {
tx, err := s.dm.Begin()
if err != nil {
return err
}
defer tx.Commit()
dir := Dir{}
err = tx.SelectOne(&dir, "select * from Dir where Path=?", d)
if err == sql.ErrNoRows {
dir = Dir{Path: d, Score: InitScore}
return tx.Insert(&dir)
} else {
dir.Score += ScoreIncrement
_, err = tx.Update(&dir)
return err
}
}
func (s *Store) FindDirs(p string) ([]Dir, error) {
var dirs []Dir
_, err := s.dm.Select(
&dirs,
"select * from Dir where Path glob ? order by score desc",
"*"+EscapeGlob(p)+"*")
return dirs, err
}

26
store/dir_test.go Normal file
View File

@ -0,0 +1,26 @@
package store
import (
"reflect"
"testing"
)
var (
dirsToAdd = []string{"/usr", "/usr/bin", "/usr"}
wantedDirs = []Dir{Dir{"/usr", 20}, Dir{"/usr/bin", 10}}
)
func TestDir(t *testing.T) {
for _, path := range dirsToAdd {
err := tStore.AddDir(path)
if err != nil {
t.Errorf("tStore.AddDir(%q) => %v, want <nil>", path, err)
}
}
dirs, err := tStore.FindDirs("usr")
if err != nil || !reflect.DeepEqual(dirs, wantedDirs) {
t.Errorf(`tStore.FindDirs("usr") => (%v, %v), want (%v, <nil>)`,
dirs, err, wantedDirs)
}
}

11
store/sql.go Normal file
View File

@ -0,0 +1,11 @@
package store
import "strings"
var globEscaper = strings.NewReplacer("\\", "\\\\", "?", "\\?", "*", "\\*")
// EscapeGlob escapes s to be suitable as an argument to SQLite's GLOB
// operator.
func EscapeGlob(s string) string {
return globEscaper.Replace(s)
}

49
store/store.go Normal file
View File

@ -0,0 +1,49 @@
package store
import (
"database/sql"
"net/url"
"github.com/coopernurse/gorp"
_ "github.com/mattn/go-sqlite3"
)
type Store struct {
dm *gorp.DbMap
}
var tableAdders []func(*gorp.DbMap)
// DefaultDB returns the default database for storage.
func DefaultDB() (*sql.DB, error) {
ddir, err := ensureDataDir()
if err != nil {
return nil, err
}
uri := "file:" + url.QueryEscape(ddir+"/db") +
"?mode=rwc&cache=shared&vfs=unix-dotfile"
return sql.Open("sqlite3", uri)
}
// NewStore creates a new Store with the default database.
func NewStore() (*Store, error) {
db, err := DefaultDB()
if err != nil {
return nil, err
}
return NewStoreDB(db)
}
// NewStoreDB creates a new Store with a custom database. The database must be
// a SQLite database.
func NewStoreDB(db *sql.DB) (*Store, error) {
dbmap := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}
for _, ta := range tableAdders {
ta(dbmap)
}
err := dbmap.CreateTablesIfNotExists()
if err != nil {
return nil, err
}
return &Store{dbmap}, nil
}

31
store/store_test.go Normal file
View File

@ -0,0 +1,31 @@
package store
// This file also sets up the test fixture.
import (
"database/sql"
"fmt"
"testing"
_ "github.com/mattn/go-sqlite3"
)
var tStore *Store
func init() {
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
panic(fmt.Sprintf("Failed to create in-memory SQLite3 DB: %v", err))
}
tStore, err = NewStoreDB(db)
if err != nil {
panic(fmt.Sprintf("Failed to create Store instance: %v", err))
}
}
func TestNewStore(t *testing.T) {
_, err := NewStore()
if err != nil {
t.Errorf("NewStore() -> (*, %v), want (*, <nil>)", err)
}
}