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("unknown 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("unknown sorting %s", query)
169 }
170
171 return nil
172}