mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-05 03:17:50 +08:00
Add store package.
This commit is contained in:
parent
27f1a1e85f
commit
868d1c5d1f
2
Makefile
2
Makefile
|
@ -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
24
store/data-dir.go
Normal 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
52
store/dir.go
Normal 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
26
store/dir_test.go
Normal 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
11
store/sql.go
Normal 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
49
store/store.go
Normal 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
31
store/store_test.go
Normal 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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user