fork of https://github.com/sourcegraph/zoekt
1package query
2
3import (
4 "fmt"
5 "regexp/syntax"
6
7 "github.com/RoaringBitmap/roaring"
8 "github.com/grafana/regexp"
9
10 webserverv1 "github.com/sourcegraph/zoekt/grpc/protos/zoekt/webserver/v1"
11)
12
13func QToProto(q Q) *webserverv1.Q {
14 switch v := q.(type) {
15 case RawConfig:
16 return &webserverv1.Q{Query: &webserverv1.Q_RawConfig{RawConfig: v.ToProto()}}
17 case *Regexp:
18 return &webserverv1.Q{Query: &webserverv1.Q_Regexp{Regexp: v.ToProto()}}
19 case *Symbol:
20 return &webserverv1.Q{Query: &webserverv1.Q_Symbol{Symbol: v.ToProto()}}
21 case *Language:
22 return &webserverv1.Q{Query: &webserverv1.Q_Language{Language: v.ToProto()}}
23 case *Const:
24 return &webserverv1.Q{Query: &webserverv1.Q_Const{Const: v.Value}}
25 case *Repo:
26 return &webserverv1.Q{Query: &webserverv1.Q_Repo{Repo: v.ToProto()}}
27 case *RepoRegexp:
28 return &webserverv1.Q{Query: &webserverv1.Q_RepoRegexp{RepoRegexp: v.ToProto()}}
29 case *BranchesRepos:
30 return &webserverv1.Q{Query: &webserverv1.Q_BranchesRepos{BranchesRepos: v.ToProto()}}
31 case *RepoIDs:
32 return &webserverv1.Q{Query: &webserverv1.Q_RepoIds{RepoIds: v.ToProto()}}
33 case *RepoSet:
34 return &webserverv1.Q{Query: &webserverv1.Q_RepoSet{RepoSet: v.ToProto()}}
35 case *FileNameSet:
36 return &webserverv1.Q{Query: &webserverv1.Q_FileNameSet{FileNameSet: v.ToProto()}}
37 case *Type:
38 return &webserverv1.Q{Query: &webserverv1.Q_Type{Type: v.ToProto()}}
39 case *Substring:
40 return &webserverv1.Q{Query: &webserverv1.Q_Substring{Substring: v.ToProto()}}
41 case *And:
42 return &webserverv1.Q{Query: &webserverv1.Q_And{And: v.ToProto()}}
43 case *Or:
44 return &webserverv1.Q{Query: &webserverv1.Q_Or{Or: v.ToProto()}}
45 case *Not:
46 return &webserverv1.Q{Query: &webserverv1.Q_Not{Not: v.ToProto()}}
47 case *Branch:
48 return &webserverv1.Q{Query: &webserverv1.Q_Branch{Branch: v.ToProto()}}
49 case *Boost:
50 return &webserverv1.Q{Query: &webserverv1.Q_Boost{Boost: v.ToProto()}}
51 default:
52 // The following nodes do not have a proto representation:
53 // - caseQ: only used internally, not by the RPC layer
54 panic(fmt.Sprintf("unknown query node %T", v))
55 }
56}
57
58func QFromProto(p *webserverv1.Q) (Q, error) {
59 switch v := p.Query.(type) {
60 case *webserverv1.Q_RawConfig:
61 return RawConfigFromProto(v.RawConfig), nil
62 case *webserverv1.Q_Regexp:
63 return RegexpFromProto(v.Regexp)
64 case *webserverv1.Q_Symbol:
65 return SymbolFromProto(v.Symbol)
66 case *webserverv1.Q_Language:
67 return LanguageFromProto(v.Language), nil
68 case *webserverv1.Q_Const:
69 return &Const{Value: v.Const}, nil
70 case *webserverv1.Q_Repo:
71 return RepoFromProto(v.Repo)
72 case *webserverv1.Q_RepoRegexp:
73 return RepoRegexpFromProto(v.RepoRegexp)
74 case *webserverv1.Q_BranchesRepos:
75 return BranchesReposFromProto(v.BranchesRepos)
76 case *webserverv1.Q_RepoIds:
77 return RepoIDsFromProto(v.RepoIds)
78 case *webserverv1.Q_RepoSet:
79 return RepoSetFromProto(v.RepoSet), nil
80 case *webserverv1.Q_FileNameSet:
81 return FileNameSetFromProto(v.FileNameSet), nil
82 case *webserverv1.Q_Type:
83 return TypeFromProto(v.Type)
84 case *webserverv1.Q_Substring:
85 return SubstringFromProto(v.Substring), nil
86 case *webserverv1.Q_And:
87 return AndFromProto(v.And)
88 case *webserverv1.Q_Or:
89 return OrFromProto(v.Or)
90 case *webserverv1.Q_Not:
91 return NotFromProto(v.Not)
92 case *webserverv1.Q_Branch:
93 return BranchFromProto(v.Branch), nil
94 case *webserverv1.Q_Boost:
95 return BoostFromProto(v.Boost)
96 case *webserverv1.Q_Meta:
97 return MetaFromProto(v.Meta)
98 default:
99 panic(fmt.Sprintf("unknown query node %T", p.Query))
100 }
101}
102
103func RegexpFromProto(p *webserverv1.Regexp) (*Regexp, error) {
104 parsed, err := syntax.Parse(p.GetRegexp(), regexpFlags)
105 if err != nil {
106 return nil, err
107 }
108 return &Regexp{
109 Regexp: parsed,
110 FileName: p.GetFileName(),
111 Content: p.GetContent(),
112 CaseSensitive: p.GetCaseSensitive(),
113 }, nil
114}
115
116func (r *Regexp) ToProto() *webserverv1.Regexp {
117 return &webserverv1.Regexp{
118 Regexp: r.Regexp.String(),
119 FileName: r.FileName,
120 Content: r.Content,
121 CaseSensitive: r.CaseSensitive,
122 }
123}
124
125func SymbolFromProto(p *webserverv1.Symbol) (*Symbol, error) {
126 expr, err := QFromProto(p.GetExpr())
127 if err != nil {
128 return nil, err
129 }
130
131 return &Symbol{
132 Expr: expr,
133 }, nil
134}
135
136func (s *Symbol) ToProto() *webserverv1.Symbol {
137 return &webserverv1.Symbol{
138 Expr: QToProto(s.Expr),
139 }
140}
141
142func LanguageFromProto(p *webserverv1.Language) *Language {
143 return &Language{
144 Language: p.GetLanguage(),
145 }
146}
147
148func (l *Language) ToProto() *webserverv1.Language {
149 return &webserverv1.Language{Language: l.Language}
150}
151
152func RepoFromProto(p *webserverv1.Repo) (*Repo, error) {
153 r, err := regexp.Compile(p.GetRegexp())
154 if err != nil {
155 return nil, err
156 }
157 return &Repo{
158 Regexp: r,
159 }, nil
160}
161
162func (q *Repo) ToProto() *webserverv1.Repo {
163 return &webserverv1.Repo{
164 Regexp: q.Regexp.String(),
165 }
166}
167
168func RepoRegexpFromProto(p *webserverv1.RepoRegexp) (*RepoRegexp, error) {
169 r, err := regexp.Compile(p.GetRegexp())
170 if err != nil {
171 return nil, err
172 }
173 return &RepoRegexp{
174 Regexp: r,
175 }, nil
176}
177
178func (q *RepoRegexp) ToProto() *webserverv1.RepoRegexp {
179 return &webserverv1.RepoRegexp{
180 Regexp: q.Regexp.String(),
181 }
182}
183
184func BranchesReposFromProto(p *webserverv1.BranchesRepos) (*BranchesRepos, error) {
185 brs := make([]BranchRepos, len(p.GetList()))
186 for i, br := range p.GetList() {
187 branchRepos, err := BranchReposFromProto(br)
188 if err != nil {
189 return nil, err
190 }
191 brs[i] = branchRepos
192 }
193 return &BranchesRepos{
194 List: brs,
195 }, nil
196}
197
198func (br *BranchesRepos) ToProto() *webserverv1.BranchesRepos {
199 list := make([]*webserverv1.BranchRepos, len(br.List))
200 for i, branchRepo := range br.List {
201 list[i] = branchRepo.ToProto()
202 }
203
204 return &webserverv1.BranchesRepos{
205 List: list,
206 }
207}
208
209func RepoIDsFromProto(p *webserverv1.RepoIds) (*RepoIDs, error) {
210 bm := roaring.NewBitmap()
211 err := bm.UnmarshalBinary(p.GetRepos())
212 if err != nil {
213 return nil, err
214 }
215
216 return &RepoIDs{
217 Repos: bm,
218 }, nil
219}
220
221func (q *RepoIDs) ToProto() *webserverv1.RepoIds {
222 b, err := q.Repos.ToBytes()
223 if err != nil {
224 panic("unexpected error marshalling bitmap: " + err.Error())
225 }
226 return &webserverv1.RepoIds{
227 Repos: b,
228 }
229}
230
231func BranchReposFromProto(p *webserverv1.BranchRepos) (BranchRepos, error) {
232 bm := roaring.NewBitmap()
233 err := bm.UnmarshalBinary(p.GetRepos())
234 if err != nil {
235 return BranchRepos{}, err
236 }
237 return BranchRepos{
238 Branch: p.GetBranch(),
239 Repos: bm,
240 }, nil
241}
242
243func (br *BranchRepos) ToProto() *webserverv1.BranchRepos {
244 b, err := br.Repos.ToBytes()
245 if err != nil {
246 panic("unexpected error marshalling bitmap: " + err.Error())
247 }
248
249 return &webserverv1.BranchRepos{
250 Branch: br.Branch,
251 Repos: b,
252 }
253}
254
255func RepoSetFromProto(p *webserverv1.RepoSet) *RepoSet {
256 return &RepoSet{
257 Set: p.GetSet(),
258 }
259}
260
261func (q *RepoSet) ToProto() *webserverv1.RepoSet {
262 return &webserverv1.RepoSet{
263 Set: q.Set,
264 }
265}
266
267func FileNameSetFromProto(p *webserverv1.FileNameSet) *FileNameSet {
268 m := make(map[string]struct{}, len(p.GetSet()))
269 for _, name := range p.GetSet() {
270 m[name] = struct{}{}
271 }
272 return &FileNameSet{
273 Set: m,
274 }
275}
276
277func (q *FileNameSet) ToProto() *webserverv1.FileNameSet {
278 s := make([]string, 0, len(q.Set))
279 for name := range q.Set {
280 s = append(s, name)
281 }
282 return &webserverv1.FileNameSet{
283 Set: s,
284 }
285}
286
287func TypeFromProto(p *webserverv1.Type) (*Type, error) {
288 child, err := QFromProto(p.GetChild())
289 if err != nil {
290 return nil, err
291 }
292
293 var kind uint8
294 switch p.GetType() {
295 case webserverv1.Type_KIND_FILE_MATCH:
296 kind = TypeFileMatch
297 case webserverv1.Type_KIND_FILE_NAME:
298 kind = TypeFileName
299 case webserverv1.Type_KIND_REPO:
300 kind = TypeRepo
301 }
302
303 return &Type{
304 Child: child,
305 // TODO: make proper enum types
306 Type: kind,
307 }, nil
308}
309
310func (q *Type) ToProto() *webserverv1.Type {
311 var kind webserverv1.Type_Kind
312 switch q.Type {
313 case TypeFileMatch:
314 kind = webserverv1.Type_KIND_FILE_MATCH
315 case TypeFileName:
316 kind = webserverv1.Type_KIND_FILE_NAME
317 case TypeRepo:
318 kind = webserverv1.Type_KIND_REPO
319 }
320
321 return &webserverv1.Type{
322 Child: QToProto(q.Child),
323 Type: kind,
324 }
325}
326
327func SubstringFromProto(p *webserverv1.Substring) *Substring {
328 return &Substring{
329 Pattern: p.GetPattern(),
330 CaseSensitive: p.GetCaseSensitive(),
331 FileName: p.GetFileName(),
332 Content: p.GetContent(),
333 }
334}
335
336func (q *Substring) ToProto() *webserverv1.Substring {
337 return &webserverv1.Substring{
338 Pattern: q.Pattern,
339 CaseSensitive: q.CaseSensitive,
340 FileName: q.FileName,
341 Content: q.Content,
342 }
343}
344
345func OrFromProto(p *webserverv1.Or) (*Or, error) {
346 children := make([]Q, len(p.GetChildren()))
347 for i, child := range p.GetChildren() {
348 c, err := QFromProto(child)
349 if err != nil {
350 return nil, err
351 }
352 children[i] = c
353 }
354 return &Or{
355 Children: children,
356 }, nil
357}
358
359func (q *Or) ToProto() *webserverv1.Or {
360 children := make([]*webserverv1.Q, len(q.Children))
361 for i, child := range q.Children {
362 children[i] = QToProto(child)
363 }
364 return &webserverv1.Or{
365 Children: children,
366 }
367}
368
369func BoostFromProto(p *webserverv1.Boost) (*Boost, error) {
370 child, err := QFromProto(p.GetChild())
371 if err != nil {
372 return nil, err
373 }
374 return &Boost{
375 Child: child,
376 Boost: p.GetBoost(),
377 }, nil
378}
379
380func MetaFromProto(p *webserverv1.Meta) (*Meta, error) {
381 re, err := regexp.Compile(p.GetValue())
382 if err != nil {
383 return nil, fmt.Errorf("invalid regexp in Meta.Value: %w", err)
384 }
385 return &Meta{
386 Field: p.GetKey(),
387 Value: re,
388 }, nil
389}
390
391func (q *Boost) ToProto() *webserverv1.Boost {
392 return &webserverv1.Boost{
393 Child: QToProto(q.Child),
394 Boost: q.Boost,
395 }
396}
397
398func NotFromProto(p *webserverv1.Not) (*Not, error) {
399 child, err := QFromProto(p.GetChild())
400 if err != nil {
401 return nil, err
402 }
403 return &Not{
404 Child: child,
405 }, nil
406}
407
408func (q *Not) ToProto() *webserverv1.Not {
409 return &webserverv1.Not{
410 Child: QToProto(q.Child),
411 }
412}
413
414func AndFromProto(p *webserverv1.And) (*And, error) {
415 children := make([]Q, len(p.GetChildren()))
416 for i, child := range p.GetChildren() {
417 c, err := QFromProto(child)
418 if err != nil {
419 return nil, err
420 }
421 children[i] = c
422 }
423 return &And{
424 Children: children,
425 }, nil
426}
427
428func (q *And) ToProto() *webserverv1.And {
429 children := make([]*webserverv1.Q, len(q.Children))
430 for i, child := range q.Children {
431 children[i] = QToProto(child)
432 }
433 return &webserverv1.And{
434 Children: children,
435 }
436}
437
438func BranchFromProto(p *webserverv1.Branch) *Branch {
439 return &Branch{
440 Pattern: p.GetPattern(),
441 Exact: p.GetExact(),
442 }
443}
444
445func (q *Branch) ToProto() *webserverv1.Branch {
446 return &webserverv1.Branch{
447 Pattern: q.Pattern,
448 Exact: q.Exact,
449 }
450}
451
452func RawConfigFromProto(p *webserverv1.RawConfig) (res RawConfig) {
453 for _, protoFlag := range p.Flags {
454 switch protoFlag {
455 case webserverv1.RawConfig_FLAG_ONLY_PUBLIC:
456 res |= RcOnlyPublic
457 case webserverv1.RawConfig_FLAG_ONLY_PRIVATE:
458 res |= RcOnlyPrivate
459 case webserverv1.RawConfig_FLAG_ONLY_FORKS:
460 res |= RcOnlyForks
461 case webserverv1.RawConfig_FLAG_NO_FORKS:
462 res |= RcNoForks
463 case webserverv1.RawConfig_FLAG_ONLY_ARCHIVED:
464 res |= RcOnlyArchived
465 case webserverv1.RawConfig_FLAG_NO_ARCHIVED:
466 res |= RcNoArchived
467 }
468 }
469 return res
470}
471
472func (r RawConfig) ToProto() *webserverv1.RawConfig {
473 var flags []webserverv1.RawConfig_Flag
474 for _, flag := range flagNames {
475 if r&flag.Mask != 0 {
476 switch flag.Mask {
477 case RcOnlyPublic:
478 flags = append(flags, webserverv1.RawConfig_FLAG_ONLY_PUBLIC)
479 case RcOnlyPrivate:
480 flags = append(flags, webserverv1.RawConfig_FLAG_ONLY_PRIVATE)
481 case RcOnlyForks:
482 flags = append(flags, webserverv1.RawConfig_FLAG_ONLY_FORKS)
483 case RcNoForks:
484 flags = append(flags, webserverv1.RawConfig_FLAG_NO_FORKS)
485 case RcOnlyArchived:
486 flags = append(flags, webserverv1.RawConfig_FLAG_ONLY_ARCHIVED)
487 case RcNoArchived:
488 flags = append(flags, webserverv1.RawConfig_FLAG_NO_ARCHIVED)
489 }
490 }
491 }
492 return &webserverv1.RawConfig{Flags: flags}
493}