Files
cgrates/utils/rsrfilters.go
2025-04-06 12:08:40 +02:00

194 lines
5.3 KiB
Go

/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package utils
import (
"fmt"
"regexp"
"strings"
)
// NewRSRFilter instantiates a new RSRFilter, setting it's properties
func NewRSRFilter(fltrVal string) (rsrFltr *RSRFilter, err error) {
rsrFltr = new(RSRFilter)
if fltrVal == "" {
return
}
if fltrVal[:1] == NegativePrefix {
rsrFltr.negative = true
fltrVal = fltrVal[1:]
if fltrVal == "" {
return
}
}
rsrFltr.filterRule = fltrVal
if fltrVal[:1] == DynamicDataPrefix {
if rsrFltr.fltrRgxp, err = regexp.Compile(fltrVal[1:]); err != nil {
return
}
}
return
}
// NewRSRFilterMustCompile is used mostly in tests
func NewRSRFilterMustCompile(fltrVal string) (rsrFltr *RSRFilter) {
var err error
if rsrFltr, err = NewRSRFilter(fltrVal); err != nil {
panic(fmt.Sprintf("parsing <%s>, err: %s", fltrVal, err.Error()))
}
return
}
// One filter rule
type RSRFilter struct {
filterRule string // Value in raw format
fltrRgxp *regexp.Regexp
negative bool // Rule should not match
}
// Clone method for RSRFilter struct
func (rsrFltr *RSRFilter) Clone() *RSRFilter {
if rsrFltr == nil {
return nil
}
rsrf := &RSRFilter{
filterRule: rsrFltr.filterRule,
negative: rsrFltr.negative,
}
if rsrFltr.fltrRgxp != nil {
rsrf.fltrRgxp = rsrFltr.fltrRgxp.Copy()
}
return rsrf
}
func (rsrFltr *RSRFilter) FilterRule() string {
return rsrFltr.filterRule
}
func (rsrFltr *RSRFilter) Pass(val string) bool {
if rsrFltr.filterRule == "" {
return !rsrFltr.negative
}
if rsrFltr.filterRule[:1] == DynamicDataPrefix {
return rsrFltr.fltrRgxp.MatchString(val) != rsrFltr.negative
}
if rsrFltr.filterRule == "^$" { // Special case to test empty value
return len(val) == 0 != rsrFltr.negative
}
if rsrFltr.filterRule[:1] == MatchStartPrefix {
if rsrFltr.filterRule[len(rsrFltr.filterRule)-1:] == MatchEndPrefix { // starts with ^ and ends with $, exact match
return val == rsrFltr.filterRule[1:len(rsrFltr.filterRule)-1] != rsrFltr.negative
}
return strings.HasPrefix(val, rsrFltr.filterRule[1:]) != rsrFltr.negative
}
lastIdx := len(rsrFltr.filterRule) - 1
if rsrFltr.filterRule[lastIdx:] == MatchEndPrefix {
return strings.HasSuffix(val, rsrFltr.filterRule[:lastIdx]) != rsrFltr.negative
}
if len(rsrFltr.filterRule) > 2 && rsrFltr.filterRule[:2] == MatchGreaterThanOrEqual {
gt, err := GreaterThan(StringToInterface(val),
StringToInterface(rsrFltr.filterRule[2:]), true)
if err != nil {
Logger.Warning(fmt.Sprintf("<RSRFilter> rule: <%s>, err: <%s>", rsrFltr.filterRule, err.Error()))
return false
}
return gt != rsrFltr.negative
}
if len(rsrFltr.filterRule) > 2 && rsrFltr.filterRule[:2] == MatchLessThanOrEqual {
gt, err := GreaterThan(StringToInterface(rsrFltr.filterRule[2:]), // compare the rule with the val
StringToInterface(val),
true)
if err != nil {
Logger.Warning(fmt.Sprintf("<RSRFilter> rule: <%s>, err: <%s>", rsrFltr.filterRule, err.Error()))
return false
}
return gt != rsrFltr.negative
}
if rsrFltr.filterRule[:1] == MatchGreaterThan {
gt, err := GreaterThan(StringToInterface(val),
StringToInterface(rsrFltr.filterRule[1:]), false)
if err != nil {
Logger.Warning(fmt.Sprintf("<RSRFilter> rule: <%s>, err: <%s>", rsrFltr.filterRule, err.Error()))
return false
}
return gt != rsrFltr.negative
}
if rsrFltr.filterRule[:1] == MatchLessThan {
gt, err := GreaterThan(StringToInterface(rsrFltr.filterRule[1:]), // compare the rule with the val
StringToInterface(val),
false)
if err != nil {
Logger.Warning(fmt.Sprintf("<RSRFilter> rule: <%s>, err: <%s>", rsrFltr.filterRule, err.Error()))
return false
}
return gt != rsrFltr.negative
}
return strings.Contains(val, rsrFltr.filterRule) != rsrFltr.negative // default is string index
}
func ParseRSRFilters(fldsStr, sep string) (RSRFilters, error) {
if fldsStr == "" {
return nil, nil
}
fltrSplt := strings.Split(fldsStr, sep)
return ParseRSRFiltersFromSlice(fltrSplt)
}
func ParseRSRFiltersFromSlice(fltrStrs []string) (RSRFilters, error) {
rsrFltrs := make(RSRFilters, len(fltrStrs))
for i, rlStr := range fltrStrs {
rsrFltr, err := NewRSRFilter(rlStr)
if err != nil {
return nil, err
}
rsrFltrs[i] = rsrFltr
}
return rsrFltrs, nil
}
type RSRFilters []*RSRFilter
func (fltrs RSRFilters) FilterRules() (rls string) {
for _, fltr := range fltrs {
rls += fltr.FilterRule()
}
return
}
// @all: specifies whether all filters should match or at least one
func (fltrs RSRFilters) Pass(val string, allMustMatch bool) (matched bool) {
if len(fltrs) == 0 {
return true
}
for _, fltr := range fltrs {
matched = fltr.Pass(val)
if allMustMatch {
if !matched {
return
}
} else if matched {
return
}
}
return
}