Monorepo for Tangled
1package searchquery
2
3import (
4 "context"
5 "log/slog"
6 "strings"
7)
8
9// IdentResolver converts a handle/identifier string to a DID string.
10type IdentResolver func(ctx context.Context, ident string) (string, error)
11
12// ResolveAuthor extracts the "author" tag from the query and resolves
13// both the positive and negated values to DIDs using the provided resolver.
14// Returns empty string / nil on missing tags or resolution failure.
15func ResolveAuthor(ctx context.Context, q *Query, resolve IdentResolver, log *slog.Logger) (authorDid string, negatedAuthorDids []string) {
16 if authorHandle := q.Get("author"); authorHandle != nil {
17 did, err := resolve(ctx, *authorHandle)
18 if err != nil {
19 log.Debug("failed to resolve author handle", "handle", *authorHandle, "err", err)
20 } else {
21 authorDid = did
22 }
23 }
24
25 for _, handle := range q.GetAllNegated("author") {
26 did, err := resolve(ctx, handle)
27 if err != nil {
28 log.Debug("failed to resolve negated author handle", "handle", handle, "err", err)
29 continue
30 }
31 negatedAuthorDids = append(negatedAuthorDids, did)
32 }
33
34 return authorDid, negatedAuthorDids
35}
36
37// TextFilters holds the keyword and phrase filters extracted from a query.
38type TextFilters struct {
39 Keywords []string
40 NegatedKeywords []string
41 Phrases []string
42 NegatedPhrases []string
43}
44
45// ExtractTextFilters extracts keyword and quoted-phrase items from the query,
46// separated into positive and negated slices.
47func ExtractTextFilters(q *Query) TextFilters {
48 var tf TextFilters
49 for _, item := range q.Items() {
50 switch item.Kind {
51 case KindKeyword:
52 if item.Negated {
53 tf.NegatedKeywords = append(tf.NegatedKeywords, item.Value)
54 } else {
55 tf.Keywords = append(tf.Keywords, item.Value)
56 }
57 case KindQuoted:
58 if item.Negated {
59 tf.NegatedPhrases = append(tf.NegatedPhrases, item.Value)
60 } else {
61 tf.Phrases = append(tf.Phrases, item.Value)
62 }
63 }
64 }
65 return tf
66}
67
68// ResolveDIDLabelValues resolves handle values to DIDs for dynamic label
69// tags whose name appears in didLabels. Tags not in didLabels are returned
70// unchanged. On resolution failure the original value is kept.
71func ResolveDIDLabelValues(
72 ctx context.Context,
73 tags []string,
74 didLabels map[string]bool,
75 resolve IdentResolver,
76 log *slog.Logger,
77) []string {
78 resolved := make([]string, 0, len(tags))
79 for _, tag := range tags {
80 idx := strings.Index(tag, ":")
81 if idx < 0 {
82 resolved = append(resolved, tag)
83 continue
84 }
85 name, val := tag[:idx], tag[idx+1:]
86 if didLabels[name] {
87 did, err := resolve(ctx, val)
88 if err != nil {
89 log.Debug("failed to resolve DID label value", "label", name, "value", val, "err", err)
90 resolved = append(resolved, tag)
91 continue
92 }
93 resolved = append(resolved, name+":"+did)
94 } else {
95 resolved = append(resolved, tag)
96 }
97 }
98 return resolved
99}