diff options
-rw-r--r-- | cache/bug_excerpt.go | 22 | ||||
-rw-r--r-- | cache/query.go | 128 | ||||
-rw-r--r-- | cache/query_test.go | 30 | ||||
-rw-r--r-- | cache/repo_cache.go | 75 | ||||
-rw-r--r-- | cache/sorting.go | 55 | ||||
-rw-r--r-- | graphql/resolvers/repo.go | 5 | ||||
-rw-r--r-- | termui/bug_table.go | 11 |
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) } |