From 5f753c3e93f2a7bba74a7d05bca9973ccb64cb07 Mon Sep 17 00:00:00 2001 From: "direct-dev.ru" Date: Fri, 24 Apr 2026 16:32:45 +0600 Subject: [PATCH] fixed compare behaviour --- pkg/linedb/jsonl_file.go | 10 +++- pkg/linedb/line_db.go | 2 +- pkg/linedb/string_match.go | 118 +++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 pkg/linedb/string_match.go diff --git a/pkg/linedb/jsonl_file.go b/pkg/linedb/jsonl_file.go index f1caea6..913d207 100644 --- a/pkg/linedb/jsonl_file.go +++ b/pkg/linedb/jsonl_file.go @@ -1103,14 +1103,18 @@ func (j *JSONLFile) valuesMatch(a, b any, strictCompare bool) bool { return a == b } - // Для строк - нечувствительное к регистру сравнение + if a == b { + return true + } + + // Для строк - нечувствительное к регистру сравнение (равенство, не подстрока) if aStr, ok := a.(string); ok { if bStr, ok := b.(string); ok { - return strings.Contains(strings.ToLower(aStr), strings.ToLower(bStr)) + return matchStringByPattern(aStr, bStr, strictCompare) } } - return a == b + return false } // rewriteFile перезаписывает файл новыми данными diff --git a/pkg/linedb/line_db.go b/pkg/linedb/line_db.go index 5df63ed..c1763b8 100644 --- a/pkg/linedb/line_db.go +++ b/pkg/linedb/line_db.go @@ -1474,7 +1474,7 @@ func (db *LineDb) valuesMatch(a, b any, strictCompare bool) bool { // Сравнение строк if aStr, ok := a.(string); ok { if bStr, ok := b.(string); ok { - return strings.EqualFold(aStr, bStr) + return matchStringByPattern(aStr, bStr, strictCompare) } } diff --git a/pkg/linedb/string_match.go b/pkg/linedb/string_match.go new file mode 100644 index 0000000..4630908 --- /dev/null +++ b/pkg/linedb/string_match.go @@ -0,0 +1,118 @@ +package linedb + +import "strings" + +type stringMatchMode int + +const ( + stringMatchEqual stringMatchMode = iota + stringMatchContains + stringMatchHasPrefix + stringMatchHasSuffix +) + +func matchStringByPattern(value string, pattern string, strictCompare bool) bool { + mode, lit := parsePercentPattern(pattern) + + if !strictCompare { + // For substring checks we use ToLower (close enough to EqualFold intent). + value = strings.ToLower(value) + lit = strings.ToLower(lit) + } + + switch mode { + case stringMatchContains: + return strings.Contains(value, lit) + case stringMatchHasPrefix: + return strings.HasPrefix(value, lit) + case stringMatchHasSuffix: + return strings.HasSuffix(value, lit) + default: + if strictCompare { + return value == lit + } + return strings.EqualFold(value, lit) + } +} + +// parsePercentPattern interprets unescaped % at the start/end of pattern: +// - %foo% => contains "foo" +// - foo% => hasPrefix "foo" +// - %foo => hasSuffix "foo" +// - foo => equal "foo" +// Escaping: \% means literal %, \\ means literal \ (only affects escape processing). +func parsePercentPattern(pattern string) (stringMatchMode, string) { + if pattern == "" { + return stringMatchEqual, "" + } + + leading := pattern[0] == '%' + trailing := len(pattern) > 0 && pattern[len(pattern)-1] == '%' && !isEscapedAt(pattern, len(pattern)-1) + + start := 0 + end := len(pattern) + if leading { + start = 1 + } + if trailing && end > start { + end-- + } + lit := unescapePercents(pattern[start:end]) + + switch { + case leading && trailing: + return stringMatchContains, lit + case trailing: + return stringMatchHasPrefix, lit + case leading: + return stringMatchHasSuffix, lit + default: + return stringMatchEqual, unescapePercents(pattern) + } +} + +func isEscapedAt(s string, idx int) bool { + // idx is escaped if preceded by an odd number of backslashes. + if idx <= 0 || idx >= len(s) { + return false + } + n := 0 + for i := idx - 1; i >= 0 && s[i] == '\\'; i-- { + n++ + } + return n%2 == 1 +} + +func unescapePercents(s string) string { + if s == "" { + return "" + } + var b strings.Builder + b.Grow(len(s)) + esc := false + for i := 0; i < len(s); i++ { + ch := s[i] + if esc { + // Only unescape % and \; keep backslash for other chars. + if ch == '%' || ch == '\\' { + b.WriteByte(ch) + } else { + b.WriteByte('\\') + b.WriteByte(ch) + } + esc = false + continue + } + if ch == '\\' { + esc = true + continue + } + b.WriteByte(ch) + } + if esc { + // dangling backslash + b.WriteByte('\\') + } + return b.String() +} +