aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Muré <batolettre@gmail.com>2018-09-09 20:21:49 +0200
committerMichael Muré <batolettre@gmail.com>2018-09-09 20:22:27 +0200
commit09e097e1bf32ad153c139e3f6befad9fb059fd6e (patch)
tree422db558f5ead4ec673bca6a2ede413514d49d5b
parent21f9840e991ae569ec1efa404011e9a16ed2ab3b (diff)
downloadgit-bug-09e097e1bf32ad153c139e3f6befad9fb059fd6e.tar.gz
cache: combine sorting and filtering into a query with its micro-DSL
-rw-r--r--cache/bug_excerpt.go22
-rw-r--r--cache/query.go128
-rw-r--r--cache/query_test.go30
-rw-r--r--cache/repo_cache.go75
-rw-r--r--cache/sorting.go55
-rw-r--r--graphql/resolvers/repo.go5
-rw-r--r--termui/bug_table.go11
7 files changed, 249 insertions, 77 deletions
diff --git a/cache/bug_excerpt.go b/cache/bug_excerpt.go
index 94b46ccc..62b5eedb 100644
--- a/cache/bug_excerpt.go
+++ b/cache/bug_excerpt.go
@@ -22,8 +22,8 @@ type BugExcerpt struct {
Labels []bug.Label
}
-func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) BugExcerpt {
- return BugExcerpt{
+func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt {
+ return &BugExcerpt{
Id: b.Id(),
CreateLamportTime: b.CreateLamportTime(),
EditLamportTime: b.EditLamportTime(),
@@ -44,7 +44,21 @@ func init() {
* Sorting
*/
-type BugsByCreationTime []BugExcerpt
+type BugsById []*BugExcerpt
+
+func (b BugsById) Len() int {
+ return len(b)
+}
+
+func (b BugsById) Less(i, j int) bool {
+ return b[i].Id < b[j].Id
+}
+
+func (b BugsById) Swap(i, j int) {
+ b[i], b[j] = b[j], b[i]
+}
+
+type BugsByCreationTime []*BugExcerpt
func (b BugsByCreationTime) Len() int {
return len(b)
@@ -72,7 +86,7 @@ func (b BugsByCreationTime) Swap(i, j int) {
b[i], b[j] = b[j], b[i]
}
-type BugsByEditTime []BugExcerpt
+type BugsByEditTime []*BugExcerpt
func (b BugsByEditTime) Len() int {
return len(b)
diff --git a/cache/query.go b/cache/query.go
new file mode 100644
index 00000000..d8df00fe
--- /dev/null
+++ b/cache/query.go
@@ -0,0 +1,128 @@
+package cache
+
+import (
+ "fmt"
+ "strings"
+)
+
+type Query struct {
+ Filters
+ OrderBy
+ OrderDirection
+}
+
+// ParseQuery parse a query DSL
+//
+// Ex: "status:open author:descartes sort:edit-asc"
+//
+// Supported filter fields are:
+// - status:
+// - author:
+// - label:
+// - no:
+//
+// Sorting is done with:
+// - sort:
+//
+// Todo: write a complete doc
+func ParseQuery(query string) (*Query, error) {
+ fields := strings.Fields(query)
+
+ result := &Query{
+ OrderBy: OrderByCreation,
+ OrderDirection: OrderDescending,
+ }
+
+ sortingDone := false
+
+ for _, field := range fields {
+ split := strings.Split(field, ":")
+ if len(split) != 2 {
+ return nil, fmt.Errorf("can't parse \"%s\"", field)
+ }
+
+ switch split[0] {
+ case "status":
+ f, err := StatusFilter(split[1])
+ if err != nil {
+ return nil, err
+ }
+ result.Status = append(result.Status, f)
+
+ case "author":
+ f := AuthorFilter(split[1])
+ result.Author = append(result.Author, f)
+
+ case "label":
+ f := LabelFilter(split[1])
+ result.Label = append(result.Label, f)
+
+ case "no":
+ err := result.parseNoFilter(split[1])
+ if err != nil {
+ return nil, err
+ }
+
+ case "sort":
+ if sortingDone {
+ return nil, fmt.Errorf("multiple sorting")
+ }
+
+ err := result.parseSorting(split[1])
+ if err != nil {
+ return nil, err
+ }
+
+ sortingDone = true
+
+ default:
+ return nil, fmt.Errorf("unknow query field %s", split[0])
+ }
+ }
+
+ return result, nil
+}
+
+func (q *Query) parseNoFilter(query string) error {
+ switch query {
+ case "label":
+ q.NoFilters = append(q.NoFilters, NoLabelFilter())
+ default:
+ return fmt.Errorf("unknown \"no\" filter")
+ }
+
+ return nil
+}
+
+func (q *Query) parseSorting(query string) error {
+ switch query {
+ // default ASC
+ case "id-desc":
+ q.OrderBy = OrderById
+ q.OrderDirection = OrderDescending
+ case "id", "id-asc":
+ q.OrderBy = OrderById
+ q.OrderDirection = OrderAscending
+
+ // default DESC
+ case "creation", "creation-desc":
+ q.OrderBy = OrderByCreation
+ q.OrderDirection = OrderDescending
+ case "creation-asc":
+ q.OrderBy = OrderByCreation
+ q.OrderDirection = OrderAscending
+
+ // default DESC
+ case "edit", "edit-desc":
+ q.OrderBy = OrderByEdit
+ q.OrderDirection = OrderDescending
+ case "edit-asc":
+ q.OrderBy = OrderByEdit
+ q.OrderDirection = OrderAscending
+
+ default:
+ return fmt.Errorf("unknow sorting %s", query)
+ }
+
+ return nil
+}
diff --git a/cache/query_test.go b/cache/query_test.go
new file mode 100644
index 00000000..24d80035
--- /dev/null
+++ b/cache/query_test.go
@@ -0,0 +1,30 @@
+package cache
+
+import "testing"
+
+func TestQueryParse(t *testing.T) {
+ var tests = []struct {
+ input string
+ ok bool
+ }{
+ {"gibberish", false},
+
+ {"status:", false},
+
+ {"status:open", true},
+ {"status:closed", true},
+ {"status:unknown", false},
+
+ {"author:rene", true},
+ // Todo: fix parsing
+ // {"author:\"Rene Descartes\"", true},
+
+ }
+
+ for _, test := range tests {
+ _, err := ParseQuery(test.input)
+ if (err == nil) != test.ok {
+ t.Fatalf("Unexpected parse result, expected: %v, err: %v", test.ok, err)
+ }
+ }
+}
diff --git a/cache/repo_cache.go b/cache/repo_cache.go
index 2f565935..af9c5070 100644
--- a/cache/repo_cache.go
+++ b/cache/repo_cache.go
@@ -8,6 +8,7 @@ import (
"io/ioutil"
"os"
"path"
+ "sort"
"strconv"
"strings"
@@ -19,7 +20,7 @@ import (
type RepoCache struct {
repo repository.Repo
- excerpts map[string]BugExcerpt
+ excerpts map[string]*BugExcerpt
bugs map[string]*BugCache
}
@@ -101,7 +102,7 @@ func (c *RepoCache) loadExcerpts() error {
decoder := gob.NewDecoder(f)
- var excerpts map[string]BugExcerpt
+ var excerpts map[string]*BugExcerpt
err = decoder.Decode(&excerpts)
if err != nil {
@@ -143,7 +144,9 @@ func repoExcerptsFilePath(repo repository.Repo) string {
}
func (c *RepoCache) buildAllExcerpt() {
- c.excerpts = make(map[string]BugExcerpt)
+ fmt.Printf("Building bug cache... ")
+
+ c.excerpts = make(map[string]*BugExcerpt)
allBugs := bug.ReadAllLocalBugs(c.repo)
@@ -151,18 +154,8 @@ func (c *RepoCache) buildAllExcerpt() {
snap := b.Bug.Compile()
c.excerpts[b.Bug.Id()] = NewBugExcerpt(b.Bug, &snap)
}
-}
-
-func (c *RepoCache) allExcerpt() []BugExcerpt {
- result := make([]BugExcerpt, len(c.excerpts))
- i := 0
- for _, val := range c.excerpts {
- result[i] = val
- i++
- }
-
- return result
+ fmt.Println("Done.")
}
func (c *RepoCache) ResolveBug(id string) (*BugCache, error) {
@@ -215,6 +208,60 @@ func (c *RepoCache) ResolveBugPrefix(prefix string) (*BugCache, error) {
return cached, nil
}
+func (c *RepoCache) QueryBugs(query *Query) []string {
+ if query == nil {
+ return c.AllBugsIds()
+ }
+
+ var filtered []*BugExcerpt
+
+ for _, excerpt := range c.excerpts {
+ if query.Match(excerpt) {
+ filtered = append(filtered, excerpt)
+ }
+ }
+
+ var sorter sort.Interface
+
+ switch query.OrderBy {
+ case OrderById:
+ sorter = BugsById(filtered)
+ case OrderByCreation:
+ sorter = BugsByCreationTime(filtered)
+ case OrderByEdit:
+ sorter = BugsByEditTime(filtered)
+ default:
+ panic("missing sort type")
+ }
+
+ if query.OrderDirection == OrderDescending {
+ sorter = sort.Reverse(sorter)
+ }
+
+ sort.Sort(sorter)
+
+ result := make([]string, len(filtered))
+
+ for i, val := range filtered {
+ result[i] = val.Id
+ }
+
+ return result
+}
+
+// AllBugsIds return all known bug ids
+func (c *RepoCache) AllBugsIds() []string {
+ result := make([]string, len(c.excerpts))
+
+ i := 0
+ for _, excerpt := range c.excerpts {
+ result[i] = excerpt.Id
+ i++
+ }
+
+ return result
+}
+
// ClearAllBugs clear all bugs kept in memory
func (c *RepoCache) ClearAllBugs() {
c.bugs = make(map[string]*BugCache)
diff --git a/cache/sorting.go b/cache/sorting.go
index eeeb315c..19034a9d 100644
--- a/cache/sorting.go
+++ b/cache/sorting.go
@@ -1,7 +1,5 @@
package cache
-import "sort"
-
type OrderBy int
const (
@@ -18,56 +16,3 @@ const (
OrderAscending
OrderDescending
)
-
-func (c *RepoCache) AllBugsId(order OrderBy, direction OrderDirection) []string {
- if order == OrderById {
- return c.orderIds(direction)
- }
-
- excerpts := c.allExcerpt()
-
- var sorter sort.Interface
-
- switch order {
- case OrderByCreation:
- sorter = BugsByCreationTime(excerpts)
- case OrderByEdit:
- sorter = BugsByEditTime(excerpts)
- default:
- panic("missing sort type")
- }
-
- if direction == OrderDescending {
- sorter = sort.Reverse(sorter)
- }
-
- sort.Sort(sorter)
-
- result := make([]string, len(excerpts))
-
- for i, val := range excerpts {
- result[i] = val.Id
- }
-
- return result
-}
-
-func (c *RepoCache) orderIds(direction OrderDirection) []string {
- result := make([]string, len(c.excerpts))
-
- i := 0
- for key := range c.excerpts {
- result[i] = key
- i++
- }
-
- var sorter sort.Interface = sort.StringSlice(result)
-
- if direction == OrderDescending {
- sorter = sort.Reverse(sorter)
- }
-
- sort.Sort(sorter)
-
- return result
-}
diff --git a/graphql/resolvers/repo.go b/graphql/resolvers/repo.go
index 75c1307d..7922256f 100644
--- a/graphql/resolvers/repo.go
+++ b/graphql/resolvers/repo.go
@@ -20,7 +20,10 @@ func (repoResolver) AllBugs(ctx context.Context, obj *models.Repository, after *
}
// Simply pass a []string with the ids to the pagination algorithm
- source := obj.Repo.AllBugsId(cache.OrderByCreation, cache.OrderAscending)
+ source := obj.Repo.QueryBugs(&cache.Query{
+ OrderBy: cache.OrderByCreation,
+ OrderDirection: cache.OrderAscending,
+ })
// The edger create a custom edge holding just the id
edger := func(id string, offset int) connections.Edge {
diff --git a/termui/bug_table.go b/termui/bug_table.go
index e4a2be24..8b50f07b 100644
--- a/termui/bug_table.go
+++ b/termui/bug_table.go
@@ -20,15 +20,20 @@ const remote = "origin"
type bugTable struct {
repo *cache.RepoCache
+ query *cache.Query
allIds []string
bugs []*cache.BugCache
pageCursor int
selectCursor int
}
-func newBugTable(cache *cache.RepoCache) *bugTable {
+func newBugTable(c *cache.RepoCache) *bugTable {
return &bugTable{
- repo: cache,
+ repo: c,
+ query: &cache.Query{
+ OrderBy: cache.OrderByCreation,
+ OrderDirection: cache.OrderAscending,
+ },
pageCursor: 0,
selectCursor: 0,
}
@@ -212,7 +217,7 @@ func (bt *bugTable) disable(g *gocui.Gui) error {
}
func (bt *bugTable) paginate(max int) error {
- bt.allIds = bt.repo.AllBugsId(cache.OrderByCreation, cache.OrderAscending)
+ bt.allIds = bt.repo.QueryBugs(bt.query)
return bt.doPaginate(max)
}