forgejo/modules/indexer/issues/internal/qstring.go
Shiny Nematoda 86b6553f3a
Some checks failed
/ release (push) Has been cancelled
testing / backend-checks (push) Has been cancelled
testing / frontend-checks (push) Has been cancelled
testing / test-unit (push) Has been cancelled
testing / test-e2e (push) Has been cancelled
testing / test-remote-cacher (redis) (push) Has been cancelled
testing / test-remote-cacher (valkey) (push) Has been cancelled
testing / test-remote-cacher (garnet) (push) Has been cancelled
testing / test-remote-cacher (redict) (push) Has been cancelled
testing / test-mysql (push) Has been cancelled
testing / test-pgsql (push) Has been cancelled
testing / test-sqlite (push) Has been cancelled
testing / security-check (push) Has been cancelled
[v11/forgejo] fix: skip empty tokens in SearchOptions.Tokens() (#8412)
backport of #8261 to v11

Co-authored-by: Danko Aleksejevs <danko@very.lv>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8412
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Shiny Nematoda <snematoda@noreply.codeberg.org>
Co-committed-by: Shiny Nematoda <snematoda@noreply.codeberg.org>
2025-07-06 10:42:45 +02:00

115 lines
1.8 KiB
Go

// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package internal
import (
"io"
"strings"
)
type BoolOpt int
const (
BoolOptMust BoolOpt = iota
BoolOptShould
BoolOptNot
)
type Token struct {
Term string
Kind BoolOpt
Fuzzy bool
}
type Tokenizer struct {
in *strings.Reader
}
func (t *Tokenizer) next() (tk Token, err error) {
var (
sb strings.Builder
r rune
)
tk.Kind = BoolOptShould
tk.Fuzzy = true
// skip all leading white space
for {
if r, _, err = t.in.ReadRune(); err != nil || r != ' ' {
break
}
}
if err != nil {
return tk, err
}
// check for +/- op, increment to the next rune in both cases
switch r {
case '+':
tk.Kind = BoolOptMust
r, _, err = t.in.ReadRune()
case '-':
tk.Kind = BoolOptNot
r, _, err = t.in.ReadRune()
}
if err != nil {
return tk, err
}
// parse the string, escaping special characters
for esc := false; err == nil; r, _, err = t.in.ReadRune() {
if esc {
if !strings.ContainsRune("+-\\\"", r) {
sb.WriteRune('\\')
}
sb.WriteRune(r)
esc = false
continue
}
switch r {
case '\\':
esc = true
case '"':
if !tk.Fuzzy {
goto nextEnd
}
tk.Fuzzy = false
case ' ', '\t':
if tk.Fuzzy {
goto nextEnd
}
sb.WriteRune(r)
default:
sb.WriteRune(r)
}
}
nextEnd:
tk.Term = sb.String()
if err == io.EOF {
err = nil
} // do not consider EOF as an error at the end
return tk, err
}
// Tokenize the keyword
func (o *SearchOptions) Tokens() (tokens []Token, err error) {
if o.Keyword == "" {
return nil, nil
}
in := strings.NewReader(o.Keyword)
it := Tokenizer{in: in}
for token, err := it.next(); err == nil; token, err = it.next() {
if token.Term != "" {
tokens = append(tokens, token)
}
}
if err != nil && err != io.EOF {
return nil, err
}
return tokens, nil
}