71 lines
2.8 KiB
Go
71 lines
2.8 KiB
Go
package project
|
||
|
||
import "strings"
|
||
|
||
// ParseQuery разбирает строку поиска в духе GitHub/Gmail-фильтров:
|
||
// токены вида "key:value" распознаются как поля Filter (host, method,
|
||
// status, color, minlen, maxlen), всё остальное склеивается и идёт
|
||
// в полнотекстовый запрос. Так один текстовый инпут в TUI закрывает
|
||
// и структурированную фильтрацию, и полнотекстовый поиск, без десятка
|
||
// отдельных полей ввода на экране.
|
||
//
|
||
// Пример: "host:example.com method:POST status:200-299 color:red токен сессии"
|
||
// даёт Filter{Host: "example.com", Method: "POST", Status: "200-299",
|
||
// ColorLabel: "red", FTSQuery: "токен сессии"}.
|
||
//
|
||
// Известный нюанс: если пользователь наберёт в свободнотекстовой части
|
||
// что-то похожее на "слово:значение" с неизвестным ключом (не из
|
||
// распознаваемого набора), этот токен уйдёт в FTSQuery как есть — а
|
||
// FTS5 сам поддерживает синтаксис column-filter с двоеточием, так что
|
||
// в редких случаях это может интерпретироваться иначе, чем ожидается.
|
||
// Осознанный компромисс ради простоты, а не баг, который стоит чинить
|
||
// экранированием на каждый чих.
|
||
func ParseQuery(q string) Filter {
|
||
var f Filter
|
||
var ftsWords []string
|
||
|
||
for _, tok := range strings.Fields(q) {
|
||
idx := strings.Index(tok, ":")
|
||
if idx <= 0 {
|
||
ftsWords = append(ftsWords, tok)
|
||
continue
|
||
}
|
||
|
||
key := strings.ToLower(tok[:idx])
|
||
val := tok[idx+1:]
|
||
|
||
switch key {
|
||
case "host":
|
||
f.Host = val
|
||
case "method":
|
||
f.Method = val
|
||
case "status":
|
||
f.Status = val
|
||
case "color":
|
||
f.ColorLabel = ColorLabel(val)
|
||
case "minlen":
|
||
f.MinLength = parseInt64(val)
|
||
case "maxlen":
|
||
f.MaxLength = parseInt64(val)
|
||
default:
|
||
ftsWords = append(ftsWords, tok)
|
||
}
|
||
}
|
||
|
||
f.FTSQuery = strings.Join(ftsWords, " ")
|
||
return f
|
||
}
|
||
|
||
// parseInt64 — терпимый разбор числа: некорректный ввод просто даёт 0
|
||
// (что для minlen/maxlen эквивалентно "фильтр не задан"), без лишней
|
||
// возни с обработкой ошибки в вызывающем коде ради этого частного случая.
|
||
func parseInt64(s string) int64 {
|
||
var n int64
|
||
for _, r := range s {
|
||
if r < '0' || r > '9' {
|
||
return 0
|
||
}
|
||
n = n*10 + int64(r-'0')
|
||
}
|
||
return n
|
||
}
|