Skip to content

Commit 4e04a27

Browse files
committed
Moved MatcherTokensFromQueryString and supporting logic to their own file.
1 parent 47444d7 commit 4e04a27

File tree

2 files changed

+140
-116
lines changed

2 files changed

+140
-116
lines changed

Diff for: commands/lib/search.go

+1-116
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"github.com/arduino/arduino-cli/arduino"
2424
"github.com/arduino/arduino-cli/arduino/libraries/librariesindex"
2525
"github.com/arduino/arduino-cli/arduino/libraries/librariesmanager"
26-
"github.com/arduino/arduino-cli/arduino/utils"
2726
"github.com/arduino/arduino-cli/commands/internal/instances"
2827
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2928
semver "go.bug.st/relaxed-semver"
@@ -38,128 +37,14 @@ func LibrarySearch(ctx context.Context, req *rpc.LibrarySearchRequest) (*rpc.Lib
3837
return searchLibrary(req, lm), nil
3938
}
4039

41-
// matcherTokensFromQueryString parses the query string into tokens of interest
42-
// for the qualifier-value pattern matching.
43-
func matcherTokensFromQueryString(query string) []string {
44-
escaped := false
45-
quoted := false
46-
tokens := []string{}
47-
sb := &strings.Builder{}
48-
49-
for _, r := range query {
50-
// Short circuit the loop on backslash so that all other paths can clear
51-
// the escaped flag.
52-
if !escaped && r == '\\' {
53-
escaped = true
54-
continue
55-
}
56-
57-
if r == '"' {
58-
if !escaped {
59-
quoted = !quoted
60-
} else {
61-
sb.WriteRune(r)
62-
}
63-
} else if !quoted && r == ' ' {
64-
tokens = append(tokens, strings.ToLower(sb.String()))
65-
sb.Reset()
66-
} else {
67-
sb.WriteRune(r)
68-
}
69-
escaped = false
70-
}
71-
if sb.Len() > 0 {
72-
tokens = append(tokens, strings.ToLower(sb.String()))
73-
}
74-
75-
return tokens
76-
}
77-
78-
// defaulLibraryMatchExtractor returns a string describing the library that
79-
// is used for the simple search.
80-
func defaultLibraryMatchExtractor(lib *librariesindex.Library) string {
81-
res := lib.Name + " " +
82-
lib.Latest.Paragraph + " " +
83-
lib.Latest.Sentence + " " +
84-
lib.Latest.Author + " "
85-
for _, include := range lib.Latest.ProvidesIncludes {
86-
res += include + " "
87-
}
88-
return res
89-
}
90-
91-
var qualifiers map[string]func(lib *librariesindex.Library) string = map[string]func(lib *librariesindex.Library) string{
92-
"name": func(lib *librariesindex.Library) string { return lib.Name },
93-
"architectures": func(lib *librariesindex.Library) string { return strings.Join(lib.Latest.Architectures, " ") },
94-
"author": func(lib *librariesindex.Library) string { return lib.Latest.Author },
95-
"category": func(lib *librariesindex.Library) string { return lib.Latest.Category },
96-
"dependencies": func(lib *librariesindex.Library) string {
97-
names := make([]string, len(lib.Latest.Dependencies))
98-
for i, dep := range lib.Latest.Dependencies {
99-
names[i] = dep.GetName()
100-
}
101-
return strings.Join(names, " ")
102-
},
103-
"maintainer": func(lib *librariesindex.Library) string { return lib.Latest.Maintainer },
104-
"paragraph": func(lib *librariesindex.Library) string { return lib.Latest.Paragraph },
105-
"sentence": func(lib *librariesindex.Library) string { return lib.Latest.Sentence },
106-
"types": func(lib *librariesindex.Library) string { return strings.Join(lib.Latest.Types, " ") },
107-
"version": func(lib *librariesindex.Library) string { return lib.Latest.Version.String() },
108-
"website": func(lib *librariesindex.Library) string { return lib.Latest.Website },
109-
}
110-
111-
// matcherFromQueryString returns a closure that takes a library as a
112-
// parameter and returns true if the library matches the query.
113-
func matcherFromQueryString(query string) func(*librariesindex.Library) bool {
114-
// A qv-query is one using <qualifier>[:=]<value> syntax.
115-
qvQuery := strings.Contains(query, ":") || strings.Contains(query, "=")
116-
117-
if !qvQuery {
118-
queryTerms := utils.SearchTermsFromQueryString(query)
119-
return func(lib *librariesindex.Library) bool {
120-
return utils.Match(defaultLibraryMatchExtractor(lib), queryTerms)
121-
}
122-
}
123-
124-
queryTerms := matcherTokensFromQueryString(query)
125-
126-
return func(lib *librariesindex.Library) bool {
127-
matched := true
128-
for _, term := range queryTerms {
129-
130-
if sepIdx := strings.IndexAny(term, "=:"); sepIdx != -1 {
131-
potentialKey := term[:sepIdx]
132-
separator := term[sepIdx]
133-
134-
extractor, ok := qualifiers[potentialKey]
135-
if ok {
136-
target := term[sepIdx+1:]
137-
if separator == ':' {
138-
matched = (matched && utils.Match(extractor(lib), []string{target}))
139-
} else { // "="
140-
matched = (matched && strings.ToLower(extractor(lib)) == target)
141-
}
142-
} else {
143-
// Unknown qualifier names revert to basic search terms.
144-
matched = (matched && utils.Match(defaultLibraryMatchExtractor(lib), []string{term}))
145-
}
146-
} else {
147-
// Terms that do not use qv-syntax are handled as usual.
148-
matched = (matched && utils.Match(defaultLibraryMatchExtractor(lib), []string{term}))
149-
}
150-
}
151-
return matched
152-
}
153-
}
154-
15540
func searchLibrary(req *rpc.LibrarySearchRequest, lm *librariesmanager.LibrariesManager) *rpc.LibrarySearchResponse {
15641
res := []*rpc.SearchedLibrary{}
15742
query := req.GetSearchArgs()
15843
if query == "" {
15944
query = req.GetQuery()
16045
}
16146

162-
matcher := matcherFromQueryString(query)
47+
matcher := MatcherFromQueryString(query)
16348

16449
for _, lib := range lm.Index.Libraries {
16550
if matcher(lib) {

Diff for: commands/lib/search_matcher.go

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2023 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to [email protected].
15+
16+
package lib
17+
18+
import (
19+
"strings"
20+
21+
"github.com/arduino/arduino-cli/arduino/libraries/librariesindex"
22+
"github.com/arduino/arduino-cli/arduino/utils"
23+
)
24+
25+
// matcherTokensFromQueryString parses the query string into tokens of interest
26+
// for the qualifier-value pattern matching.
27+
func matcherTokensFromQueryString(query string) []string {
28+
escaped := false
29+
quoted := false
30+
tokens := []string{}
31+
sb := &strings.Builder{}
32+
33+
for _, r := range query {
34+
// Short circuit the loop on backslash so that all other paths can clear
35+
// the escaped flag.
36+
if !escaped && r == '\\' {
37+
escaped = true
38+
continue
39+
}
40+
41+
if r == '"' {
42+
if !escaped {
43+
quoted = !quoted
44+
} else {
45+
sb.WriteRune(r)
46+
}
47+
} else if !quoted && r == ' ' {
48+
tokens = append(tokens, strings.ToLower(sb.String()))
49+
sb.Reset()
50+
} else {
51+
sb.WriteRune(r)
52+
}
53+
escaped = false
54+
}
55+
if sb.Len() > 0 {
56+
tokens = append(tokens, strings.ToLower(sb.String()))
57+
}
58+
59+
return tokens
60+
}
61+
62+
// defaulLibraryMatchExtractor returns a string describing the library that
63+
// is used for the simple search.
64+
func defaultLibraryMatchExtractor(lib *librariesindex.Library) string {
65+
res := lib.Name + " " +
66+
lib.Latest.Paragraph + " " +
67+
lib.Latest.Sentence + " " +
68+
lib.Latest.Author + " "
69+
for _, include := range lib.Latest.ProvidesIncludes {
70+
res += include + " "
71+
}
72+
return res
73+
}
74+
75+
var qualifiers map[string]func(lib *librariesindex.Library) string = map[string]func(lib *librariesindex.Library) string{
76+
"name": func(lib *librariesindex.Library) string { return lib.Name },
77+
"architectures": func(lib *librariesindex.Library) string { return strings.Join(lib.Latest.Architectures, " ") },
78+
"author": func(lib *librariesindex.Library) string { return lib.Latest.Author },
79+
"category": func(lib *librariesindex.Library) string { return lib.Latest.Category },
80+
"dependencies": func(lib *librariesindex.Library) string {
81+
names := make([]string, len(lib.Latest.Dependencies))
82+
for i, dep := range lib.Latest.Dependencies {
83+
names[i] = dep.GetName()
84+
}
85+
return strings.Join(names, " ")
86+
},
87+
"maintainer": func(lib *librariesindex.Library) string { return lib.Latest.Maintainer },
88+
"paragraph": func(lib *librariesindex.Library) string { return lib.Latest.Paragraph },
89+
"sentence": func(lib *librariesindex.Library) string { return lib.Latest.Sentence },
90+
"types": func(lib *librariesindex.Library) string { return strings.Join(lib.Latest.Types, " ") },
91+
"version": func(lib *librariesindex.Library) string { return lib.Latest.Version.String() },
92+
"website": func(lib *librariesindex.Library) string { return lib.Latest.Website },
93+
}
94+
95+
// matcherFromQueryString returns a closure that takes a library as a
96+
// parameter and returns true if the library matches the query.
97+
func MatcherFromQueryString(query string) func(*librariesindex.Library) bool {
98+
// A qv-query is one using <qualifier>[:=]<value> syntax.
99+
qvQuery := strings.Contains(query, ":") || strings.Contains(query, "=")
100+
101+
if !qvQuery {
102+
queryTerms := utils.SearchTermsFromQueryString(query)
103+
return func(lib *librariesindex.Library) bool {
104+
return utils.Match(defaultLibraryMatchExtractor(lib), queryTerms)
105+
}
106+
}
107+
108+
queryTerms := matcherTokensFromQueryString(query)
109+
110+
return func(lib *librariesindex.Library) bool {
111+
matched := true
112+
for _, term := range queryTerms {
113+
114+
if sepIdx := strings.IndexAny(term, "=:"); sepIdx != -1 {
115+
potentialKey := term[:sepIdx]
116+
separator := term[sepIdx]
117+
118+
extractor, ok := qualifiers[potentialKey]
119+
if ok {
120+
target := term[sepIdx+1:]
121+
if separator == ':' {
122+
matched = (matched && utils.Match(extractor(lib), []string{target}))
123+
} else { // "="
124+
matched = (matched && strings.ToLower(extractor(lib)) == target)
125+
}
126+
} else {
127+
// Unknown qualifier names revert to basic search terms.
128+
matched = (matched && utils.Match(defaultLibraryMatchExtractor(lib), []string{term}))
129+
}
130+
} else {
131+
// Terms that do not use qv-syntax are handled as usual.
132+
matched = (matched && utils.Match(defaultLibraryMatchExtractor(lib), []string{term}))
133+
}
134+
}
135+
return matched
136+
}
137+
}
138+
139+

0 commit comments

Comments
 (0)