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 "label":
60 f := LabelFilter(qualifierQuery)
61 result.Label = append(result.Label, f)
62
63 case "title":
64 f := TitleFilter(qualifierQuery)
65 result.Label = append(result.Title, f)
66
67 case "no":
68 err := result.parseNoFilter(qualifierQuery)
69 if err != nil {
70 return nil, err
71 }
72
73 case "sort":
74 if sortingDone {
75 return nil, fmt.Errorf("multiple sorting")
76 }
77
78 err := result.parseSorting(qualifierQuery)
79 if err != nil {
80 return nil, err
81 }
82
83 sortingDone = true
84
85 default:
86 return nil, fmt.Errorf("unknow qualifier name %s", qualifierName)
87 }
88 }
89
90 return result, nil
91}
92
93func splitQuery(query string) []string {
94 lastQuote := rune(0)
95 f := func(c rune) bool {
96 switch {
97 case c == lastQuote:
98 lastQuote = rune(0)
99 return false
100 case lastQuote != rune(0):
101 return false
102 case unicode.In(c, unicode.Quotation_Mark):
103 lastQuote = c
104 return false
105 default:
106 return unicode.IsSpace(c)
107 }
108 }
109
110 return strings.FieldsFunc(query, f)
111}
112
113func removeQuote(field string) string {
114 if len(field) >= 2 {
115 if field[0] == '"' && field[len(field)-1] == '"' {
116 return field[1 : len(field)-1]
117 }
118 }
119 return field
120}
121
122func (q *Query) parseNoFilter(query string) error {
123 switch query {
124 case "label":
125 q.NoFilters = append(q.NoFilters, NoLabelFilter())
126 default:
127 return fmt.Errorf("unknown \"no\" filter %s", query)
128 }
129
130 return nil
131}
132
133func (q *Query) parseSorting(query string) error {
134 switch query {
135 // default ASC
136 case "id-desc":
137 q.OrderBy = OrderById
138 q.OrderDirection = OrderDescending
139 case "id", "id-asc":
140 q.OrderBy = OrderById
141 q.OrderDirection = OrderAscending
142
143 // default DESC
144 case "creation", "creation-desc":
145 q.OrderBy = OrderByCreation
146 q.OrderDirection = OrderDescending
147 case "creation-asc":
148 q.OrderBy = OrderByCreation
149 q.OrderDirection = OrderAscending
150
151 // default DESC
152 case "edit", "edit-desc":
153 q.OrderBy = OrderByEdit
154 q.OrderDirection = OrderDescending
155 case "edit-asc":
156 q.OrderBy = OrderByEdit
157 q.OrderDirection = OrderAscending
158
159 default:
160 return fmt.Errorf("unknow sorting %s", query)
161 }
162
163 return nil
164}