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 }