1package cache
  2
  3import (
  4	"fmt"
  5	"strings"
  6	"unicode"
  7)
  8
  9type Query struct {
 10	Filters
 11	OrderBy
 12	OrderDirection
 13}
 14
 15// Return an identity query with default sorting (creation-desc)
 16func NewQuery() *Query {
 17	return &Query{
 18		OrderBy:        OrderByCreation,
 19		OrderDirection: OrderDescending,
 20	}
 21}
 22
 23// ParseQuery parse a query DSL
 24//
 25// Ex: "status:open author:descartes sort:edit-asc"
 26//
 27// Supported filter qualifiers and syntax are described in docs/queries.md
 28func ParseQuery(query string) (*Query, error) {
 29	fields := splitQuery(query)
 30
 31	result := &Query{
 32		OrderBy:        OrderByCreation,
 33		OrderDirection: OrderDescending,
 34	}
 35
 36	sortingDone := false
 37
 38	for _, field := range fields {
 39		split := strings.Split(field, ":")
 40		if len(split) != 2 {
 41			return nil, fmt.Errorf("can't parse \"%s\"", field)
 42		}
 43
 44		qualifierName := split[0]
 45		qualifierQuery := removeQuote(split[1])
 46
 47		switch qualifierName {
 48		case "status", "state":
 49			f, err := StatusFilter(qualifierQuery)
 50			if err != nil {
 51				return nil, err
 52			}
 53			result.Status = append(result.Status, f)
 54
 55		case "author":
 56			f := AuthorFilter(qualifierQuery)
 57			result.Author = append(result.Author, f)
 58
 59		case "actor":
 60			f := ActorFilter(qualifierQuery)
 61			result.Actor = append(result.Actor, f)
 62
 63		case "participant":
 64			f := ParticipantFilter(qualifierQuery)
 65			result.Participant = append(result.Participant, f)
 66
 67		case "label":
 68			f := LabelFilter(qualifierQuery)
 69			result.Label = append(result.Label, f)
 70
 71		case "title":
 72			f := TitleFilter(qualifierQuery)
 73			result.Title = append(result.Title, f)
 74
 75		case "no":
 76			err := result.parseNoFilter(qualifierQuery)
 77			if err != nil {
 78				return nil, err
 79			}
 80
 81		case "sort":
 82			if sortingDone {
 83				return nil, fmt.Errorf("multiple sorting")
 84			}
 85
 86			err := result.parseSorting(qualifierQuery)
 87			if err != nil {
 88				return nil, err
 89			}
 90
 91			sortingDone = true
 92
 93		default:
 94			return nil, fmt.Errorf("unknow qualifier name %s", qualifierName)
 95		}
 96	}
 97
 98	return result, nil
 99}
100
101func splitQuery(query string) []string {
102	lastQuote := rune(0)
103	f := func(c rune) bool {
104		switch {
105		case c == lastQuote:
106			lastQuote = rune(0)
107			return false
108		case lastQuote != rune(0):
109			return false
110		case unicode.In(c, unicode.Quotation_Mark):
111			lastQuote = c
112			return false
113		default:
114			return unicode.IsSpace(c)
115		}
116	}
117
118	return strings.FieldsFunc(query, f)
119}
120
121func removeQuote(field string) string {
122	if len(field) >= 2 {
123		if field[0] == '"' && field[len(field)-1] == '"' {
124			return field[1 : len(field)-1]
125		}
126	}
127	return field
128}
129
130func (q *Query) parseNoFilter(query string) error {
131	switch query {
132	case "label":
133		q.NoFilters = append(q.NoFilters, NoLabelFilter())
134	default:
135		return fmt.Errorf("unknown \"no\" filter %s", query)
136	}
137
138	return nil
139}
140
141func (q *Query) parseSorting(query string) error {
142	switch query {
143	// default ASC
144	case "id-desc":
145		q.OrderBy = OrderById
146		q.OrderDirection = OrderDescending
147	case "id", "id-asc":
148		q.OrderBy = OrderById
149		q.OrderDirection = OrderAscending
150
151	// default DESC
152	case "creation", "creation-desc":
153		q.OrderBy = OrderByCreation
154		q.OrderDirection = OrderDescending
155	case "creation-asc":
156		q.OrderBy = OrderByCreation
157		q.OrderDirection = OrderAscending
158
159	// default DESC
160	case "edit", "edit-desc":
161		q.OrderBy = OrderByEdit
162		q.OrderDirection = OrderDescending
163	case "edit-asc":
164		q.OrderBy = OrderByEdit
165		q.OrderDirection = OrderAscending
166
167	default:
168		return fmt.Errorf("unknow sorting %s", query)
169	}
170
171	return nil
172}