query: no need for an ast package

Michael Muré created

Change summary

cache/filter.go           |  6 +-
cache/repo_cache.go       | 22 +++++-----
commands/ls.go            | 76 +++++++++++++++-------------------------
graphql/resolvers/repo.go |  5 +-
query/parser.go           | 35 +++++++++---------
query/parser_test.go      | 59 +++++++++++++++----------------
query/query.go            |  6 ++
termui/bug_table.go       |  3 -
8 files changed, 96 insertions(+), 116 deletions(-)

Detailed changes

cache/filter.go 🔗

@@ -5,7 +5,7 @@ import (
 
 	"github.com/MichaelMure/git-bug/bug"
 	"github.com/MichaelMure/git-bug/entity"
-	"github.com/MichaelMure/git-bug/query/ast"
+	"github.com/MichaelMure/git-bug/query"
 )
 
 // resolver has the resolving functions needed by filters.
@@ -123,9 +123,9 @@ type Matcher struct {
 	NoFilters   []Filter
 }
 
-// compileMatcher transform an ast.Filters into a specialized matcher
+// compileMatcher transform a query.Filters into a specialized matcher
 // for the cache.
-func compileMatcher(filters ast.Filters) *Matcher {
+func compileMatcher(filters query.Filters) *Matcher {
 	result := &Matcher{}
 
 	for _, value := range filters.Status {

cache/repo_cache.go 🔗

@@ -18,7 +18,7 @@ import (
 	"github.com/MichaelMure/git-bug/bug"
 	"github.com/MichaelMure/git-bug/entity"
 	"github.com/MichaelMure/git-bug/identity"
-	"github.com/MichaelMure/git-bug/query/ast"
+	"github.com/MichaelMure/git-bug/query"
 	"github.com/MichaelMure/git-bug/repository"
 	"github.com/MichaelMure/git-bug/util/git"
 	"github.com/MichaelMure/git-bug/util/process"
@@ -526,15 +526,15 @@ func (c *RepoCache) resolveBugMatcher(f func(*BugExcerpt) bool) (entity.Id, erro
 }
 
 // QueryBugs return the id of all Bug matching the given Query
-func (c *RepoCache) QueryBugs(query *ast.Query) []entity.Id {
+func (c *RepoCache) QueryBugs(q *query.Query) []entity.Id {
 	c.muBug.RLock()
 	defer c.muBug.RUnlock()
 
-	if query == nil {
+	if q == nil {
 		return c.AllBugsIds()
 	}
 
-	matcher := compileMatcher(query.Filters)
+	matcher := compileMatcher(q.Filters)
 
 	var filtered []*BugExcerpt
 
@@ -546,21 +546,21 @@ func (c *RepoCache) QueryBugs(query *ast.Query) []entity.Id {
 
 	var sorter sort.Interface
 
-	switch query.OrderBy {
-	case ast.OrderById:
+	switch q.OrderBy {
+	case query.OrderById:
 		sorter = BugsById(filtered)
-	case ast.OrderByCreation:
+	case query.OrderByCreation:
 		sorter = BugsByCreationTime(filtered)
-	case ast.OrderByEdit:
+	case query.OrderByEdit:
 		sorter = BugsByEditTime(filtered)
 	default:
 		panic("missing sort type")
 	}
 
-	switch query.OrderDirection {
-	case ast.OrderAscending:
+	switch q.OrderDirection {
+	case query.OrderAscending:
 		// Nothing to do
-	case ast.OrderDescending:
+	case query.OrderDescending:
 		sorter = sort.Reverse(sorter)
 	default:
 		panic("missing sort direction")

commands/ls.go 🔗

@@ -10,21 +10,17 @@ import (
 	"github.com/MichaelMure/git-bug/bug"
 	"github.com/MichaelMure/git-bug/cache"
 	"github.com/MichaelMure/git-bug/query"
-	"github.com/MichaelMure/git-bug/query/ast"
 	"github.com/MichaelMure/git-bug/util/colors"
 	"github.com/MichaelMure/git-bug/util/interrupt"
 )
 
 var (
-	lsStatusQuery      []string
-	lsAuthorQuery      []string
-	lsParticipantQuery []string
-	lsLabelQuery       []string
-	lsTitleQuery       []string
-	lsActorQuery       []string
-	lsNoQuery          []string
-	lsSortBy           string
-	lsSortDirection    string
+	lsQuery query.Query
+
+	lsStatusQuery   []string
+	lsNoQuery       []string
+	lsSortBy        string
+	lsSortDirection string
 )
 
 func runLsBug(cmd *cobra.Command, args []string) error {
@@ -35,7 +31,7 @@ func runLsBug(cmd *cobra.Command, args []string) error {
 	defer backend.Close()
 	interrupt.RegisterCleaner(backend.Close)
 
-	var q *ast.Query
+	var q *query.Query
 	if len(args) >= 1 {
 		q, err = query.Parse(strings.Join(args, " "))
 
@@ -43,10 +39,11 @@ func runLsBug(cmd *cobra.Command, args []string) error {
 			return err
 		}
 	} else {
-		q, err = lsQueryFromFlags()
+		err = completeQuery()
 		if err != nil {
 			return err
 		}
+		q = &lsQuery
 	}
 
 	allIds := backend.QueryBugs(q)
@@ -99,63 +96,46 @@ func runLsBug(cmd *cobra.Command, args []string) error {
 	return nil
 }
 
-// Transform the command flags into an ast.Query
-func lsQueryFromFlags() (*ast.Query, error) {
-	q := ast.NewQuery()
-
+// Finish the command flags transformation into the query.Query
+func completeQuery() error {
 	for _, str := range lsStatusQuery {
 		status, err := bug.StatusFromString(str)
 		if err != nil {
-			return nil, err
+			return err
 		}
-		q.Status = append(q.Status, status)
-	}
-	for _, title := range lsTitleQuery {
-		q.Title = append(q.Title, title)
-	}
-	for _, author := range lsAuthorQuery {
-		q.Author = append(q.Author, author)
-	}
-	for _, actor := range lsActorQuery {
-		q.Actor = append(q.Actor, actor)
-	}
-	for _, participant := range lsParticipantQuery {
-		q.Participant = append(q.Participant, participant)
-	}
-	for _, label := range lsLabelQuery {
-		q.Label = append(q.Label, label)
+		lsQuery.Status = append(lsQuery.Status, status)
 	}
 
 	for _, no := range lsNoQuery {
 		switch no {
 		case "label":
-			q.NoLabel = true
+			lsQuery.NoLabel = true
 		default:
-			return nil, fmt.Errorf("unknown \"no\" filter %s", no)
+			return fmt.Errorf("unknown \"no\" filter %s", no)
 		}
 	}
 
 	switch lsSortBy {
 	case "id":
-		q.OrderBy = ast.OrderById
+		lsQuery.OrderBy = query.OrderById
 	case "creation":
-		q.OrderBy = ast.OrderByCreation
+		lsQuery.OrderBy = query.OrderByCreation
 	case "edit":
-		q.OrderBy = ast.OrderByEdit
+		lsQuery.OrderBy = query.OrderByEdit
 	default:
-		return nil, fmt.Errorf("unknown sort flag %s", lsSortBy)
+		return fmt.Errorf("unknown sort flag %s", lsSortBy)
 	}
 
 	switch lsSortDirection {
 	case "asc":
-		q.OrderDirection = ast.OrderAscending
+		lsQuery.OrderDirection = query.OrderAscending
 	case "desc":
-		q.OrderDirection = ast.OrderDescending
+		lsQuery.OrderDirection = query.OrderDescending
 	default:
-		return nil, fmt.Errorf("unknown sort direction %s", lsSortDirection)
+		return fmt.Errorf("unknown sort direction %s", lsSortDirection)
 	}
 
-	return q, nil
+	return nil
 }
 
 var lsCmd = &cobra.Command{
@@ -181,15 +161,15 @@ func init() {
 
 	lsCmd.Flags().StringSliceVarP(&lsStatusQuery, "status", "s", nil,
 		"Filter by status. Valid values are [open,closed]")
-	lsCmd.Flags().StringSliceVarP(&lsAuthorQuery, "author", "a", nil,
+	lsCmd.Flags().StringSliceVarP(&lsQuery.Author, "author", "a", nil,
 		"Filter by author")
-	lsCmd.Flags().StringSliceVarP(&lsParticipantQuery, "participant", "p", nil,
+	lsCmd.Flags().StringSliceVarP(&lsQuery.Participant, "participant", "p", nil,
 		"Filter by participant")
-	lsCmd.Flags().StringSliceVarP(&lsActorQuery, "actor", "A", nil,
+	lsCmd.Flags().StringSliceVarP(&lsQuery.Actor, "actor", "A", nil,
 		"Filter by actor")
-	lsCmd.Flags().StringSliceVarP(&lsLabelQuery, "label", "l", nil,
+	lsCmd.Flags().StringSliceVarP(&lsQuery.Label, "label", "l", nil,
 		"Filter by label")
-	lsCmd.Flags().StringSliceVarP(&lsTitleQuery, "title", "t", nil,
+	lsCmd.Flags().StringSliceVarP(&lsQuery.Title, "title", "t", nil,
 		"Filter by title")
 	lsCmd.Flags().StringSliceVarP(&lsNoQuery, "no", "n", nil,
 		"Filter by absence of something. Valid values are [label]")

graphql/resolvers/repo.go 🔗

@@ -9,7 +9,6 @@ import (
 	"github.com/MichaelMure/git-bug/graphql/graph"
 	"github.com/MichaelMure/git-bug/graphql/models"
 	"github.com/MichaelMure/git-bug/query"
-	"github.com/MichaelMure/git-bug/query/ast"
 )
 
 var _ graph.RepositoryResolver = &repoResolver{}
@@ -29,7 +28,7 @@ func (repoResolver) AllBugs(_ context.Context, obj *models.Repository, after *st
 		Last:   last,
 	}
 
-	var q *ast.Query
+	var q *query.Query
 	if queryStr != nil {
 		query2, err := query.Parse(*queryStr)
 		if err != nil {
@@ -37,7 +36,7 @@ func (repoResolver) AllBugs(_ context.Context, obj *models.Repository, after *st
 		}
 		q = query2
 	} else {
-		q = ast.NewQuery()
+		q = query.NewQuery()
 	}
 
 	// Simply pass a []string with the ids to the pagination algorithm

query/parser.go 🔗

@@ -4,7 +4,6 @@ import (
 	"fmt"
 
 	"github.com/MichaelMure/git-bug/bug"
-	"github.com/MichaelMure/git-bug/query/ast"
 )
 
 // Parse parse a query DSL
@@ -12,15 +11,15 @@ import (
 // Ex: "status:open author:descartes sort:edit-asc"
 //
 // Supported filter qualifiers and syntax are described in docs/queries.md
-func Parse(query string) (*ast.Query, error) {
+func Parse(query string) (*Query, error) {
 	tokens, err := tokenize(query)
 	if err != nil {
 		return nil, err
 	}
 
-	q := &ast.Query{
-		OrderBy:        ast.OrderByCreation,
-		OrderDirection: ast.OrderDescending,
+	q := &Query{
+		OrderBy:        OrderByCreation,
+		OrderDirection: OrderDescending,
 	}
 	sortingDone := false
 
@@ -66,31 +65,31 @@ func Parse(query string) (*ast.Query, error) {
 	return q, nil
 }
 
-func parseSorting(q *ast.Query, value string) error {
+func parseSorting(q *Query, value string) error {
 	switch value {
 	// default ASC
 	case "id-desc":
-		q.OrderBy = ast.OrderById
-		q.OrderDirection = ast.OrderDescending
+		q.OrderBy = OrderById
+		q.OrderDirection = OrderDescending
 	case "id", "id-asc":
-		q.OrderBy = ast.OrderById
-		q.OrderDirection = ast.OrderAscending
+		q.OrderBy = OrderById
+		q.OrderDirection = OrderAscending
 
 	// default DESC
 	case "creation", "creation-desc":
-		q.OrderBy = ast.OrderByCreation
-		q.OrderDirection = ast.OrderDescending
+		q.OrderBy = OrderByCreation
+		q.OrderDirection = OrderDescending
 	case "creation-asc":
-		q.OrderBy = ast.OrderByCreation
-		q.OrderDirection = ast.OrderAscending
+		q.OrderBy = OrderByCreation
+		q.OrderDirection = OrderAscending
 
 	// default DESC
 	case "edit", "edit-desc":
-		q.OrderBy = ast.OrderByEdit
-		q.OrderDirection = ast.OrderDescending
+		q.OrderBy = OrderByEdit
+		q.OrderDirection = OrderDescending
 	case "edit-asc":
-		q.OrderBy = ast.OrderByEdit
-		q.OrderDirection = ast.OrderAscending
+		q.OrderBy = OrderByEdit
+		q.OrderDirection = OrderAscending
 
 	default:
 		return fmt.Errorf("unknown sorting %s", value)

query/parser_test.go 🔗

@@ -6,73 +6,72 @@ import (
 	"github.com/stretchr/testify/assert"
 
 	"github.com/MichaelMure/git-bug/bug"
-	"github.com/MichaelMure/git-bug/query/ast"
 )
 
 func TestParse(t *testing.T) {
 	var tests = []struct {
 		input  string
-		output *ast.Query
+		output *Query
 	}{
 		{"gibberish", nil},
 		{"status:", nil},
 		{":value", nil},
 
-		{"status:open", &ast.Query{
-			Filters: ast.Filters{Status: []bug.Status{bug.OpenStatus}},
+		{"status:open", &Query{
+			Filters: Filters{Status: []bug.Status{bug.OpenStatus}},
 		}},
-		{"status:closed", &ast.Query{
-			Filters: ast.Filters{Status: []bug.Status{bug.ClosedStatus}},
+		{"status:closed", &Query{
+			Filters: Filters{Status: []bug.Status{bug.ClosedStatus}},
 		}},
 		{"status:unknown", nil},
 
-		{"author:rene", &ast.Query{
-			Filters: ast.Filters{Author: []string{"rene"}},
+		{"author:rene", &Query{
+			Filters: Filters{Author: []string{"rene"}},
 		}},
-		{`author:"René Descartes"`, &ast.Query{
-			Filters: ast.Filters{Author: []string{"René Descartes"}},
+		{`author:"René Descartes"`, &Query{
+			Filters: Filters{Author: []string{"René Descartes"}},
 		}},
 
-		{"actor:bernhard", &ast.Query{
-			Filters: ast.Filters{Actor: []string{"bernhard"}},
+		{"actor:bernhard", &Query{
+			Filters: Filters{Actor: []string{"bernhard"}},
 		}},
-		{"participant:leonhard", &ast.Query{
-			Filters: ast.Filters{Participant: []string{"leonhard"}},
+		{"participant:leonhard", &Query{
+			Filters: Filters{Participant: []string{"leonhard"}},
 		}},
 
-		{"label:hello", &ast.Query{
-			Filters: ast.Filters{Label: []string{"hello"}},
+		{"label:hello", &Query{
+			Filters: Filters{Label: []string{"hello"}},
 		}},
-		{`label:"Good first issue"`, &ast.Query{
-			Filters: ast.Filters{Label: []string{"Good first issue"}},
+		{`label:"Good first issue"`, &Query{
+			Filters: Filters{Label: []string{"Good first issue"}},
 		}},
 
-		{"title:titleOne", &ast.Query{
-			Filters: ast.Filters{Title: []string{"titleOne"}},
+		{"title:titleOne", &Query{
+			Filters: Filters{Title: []string{"titleOne"}},
 		}},
-		{`title:"Bug titleTwo"`, &ast.Query{
-			Filters: ast.Filters{Title: []string{"Bug titleTwo"}},
+		{`title:"Bug titleTwo"`, &Query{
+			Filters: Filters{Title: []string{"Bug titleTwo"}},
 		}},
 
-		{"no:label", &ast.Query{
-			Filters: ast.Filters{NoLabel: true},
+		{"no:label", &Query{
+			Filters: Filters{NoLabel: true},
 		}},
 
-		{"sort:edit", &ast.Query{
-			OrderBy: ast.OrderByEdit,
+		{"sort:edit", &Query{
+			OrderBy: OrderByEdit,
 		}},
 		{"sort:unknown", nil},
 
 		{`status:open author:"René Descartes" participant:leonhard label:hello label:"Good first issue" sort:edit-desc`,
-			&ast.Query{
-				Filters: ast.Filters{
+			&Query{
+				Filters: Filters{
 					Status:      []bug.Status{bug.OpenStatus},
 					Author:      []string{"René Descartes"},
 					Participant: []string{"leonhard"},
 					Label:       []string{"hello", "Good first issue"},
 				},
-				OrderBy:        ast.OrderByEdit,
-				OrderDirection: ast.OrderDescending,
+				OrderBy:        OrderByEdit,
+				OrderDirection: OrderDescending,
 			},
 		},
 	}

query/ast/ast.go → query/query.go 🔗

@@ -1,7 +1,11 @@
-package ast
+package query
 
 import "github.com/MichaelMure/git-bug/bug"
 
+// Query is the intermediary representation of a Bug's query. It is either
+// produced by parsing a query string (ex: "status:open author:rene") or created
+// manually. This query doesn't do anything by itself and need to be interpreted
+// for the specific domain of application.
 type Query struct {
 	Filters
 	OrderBy

termui/bug_table.go 🔗

@@ -13,7 +13,6 @@ import (
 	"github.com/MichaelMure/git-bug/cache"
 	"github.com/MichaelMure/git-bug/entity"
 	"github.com/MichaelMure/git-bug/query"
-	"github.com/MichaelMure/git-bug/query/ast"
 	"github.com/MichaelMure/git-bug/util/colors"
 )
 
@@ -28,7 +27,7 @@ const defaultQuery = "status:open"
 type bugTable struct {
 	repo         *cache.RepoCache
 	queryStr     string
-	query        *ast.Query
+	query        *query.Query
 	allIds       []entity.Id
 	excerpts     []*cache.BugExcerpt
 	pageCursor   int