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