1
0
mirror of https://github.com/MetaCubeX/mihomo.git synced 2025-07-18 17:08:06 +08:00

chore: rebuild rule parsing code

This commit is contained in:
wwqgtxx
2025-07-10 11:27:54 +08:00
parent 2b84dd3618
commit 300eb8b12a
6 changed files with 65 additions and 70 deletions

View File

@ -1035,46 +1035,20 @@ func parseRules(rulesConfig []string, proxies map[string]C.Proxy, ruleProviders
// parse rules
for idx, line := range rulesConfig {
rule := trimArr(strings.Split(line, ","))
var (
payload string
target string
params []string
ruleName = strings.ToUpper(rule[0])
)
l := len(rule)
if ruleName == "NOT" || ruleName == "OR" || ruleName == "AND" || ruleName == "SUB-RULE" || ruleName == "DOMAIN-REGEX" || ruleName == "PROCESS-NAME-REGEX" || ruleName == "PROCESS-PATH-REGEX" {
target = rule[l-1]
payload = strings.Join(rule[1:l-1], ",")
} else {
if l < 2 {
return nil, fmt.Errorf("%s[%d] [%s] error: format invalid", format, idx, line)
}
if l < 4 {
rule = append(rule, make([]string, 4-l)...)
}
if ruleName == "MATCH" {
l = 2
}
if l >= 3 {
l = 3
payload = rule[1]
}
target = rule[l-1]
params = rule[l:]
tp, payload, target, params := RC.ParseRulePayload(line, true)
if target == "" {
return nil, fmt.Errorf("%s[%d] [%s] error: format invalid", format, idx, line)
}
if _, ok := proxies[target]; !ok {
if ruleName != "SUB-RULE" {
if tp != "SUB-RULE" {
return nil, fmt.Errorf("%s[%d] [%s] error: proxy [%s] not found", format, idx, line, target)
} else if _, ok = subRules[target]; !ok {
return nil, fmt.Errorf("%s[%d] [%s] error: sub-rule [%s] not found", format, idx, line, target)
}
}
params = trimArr(params)
parsed, parseErr := R.ParseRule(ruleName, payload, target, params, subRules)
parsed, parseErr := R.ParseRule(tp, payload, target, params, subRules)
if parseErr != nil {
return nil, fmt.Errorf("%s[%d] [%s] error: %s", format, idx, line, parseErr.Error())
}

View File

@ -6,19 +6,11 @@ import (
"net/netip"
"os"
"strconv"
"strings"
"github.com/metacubex/mihomo/adapter/outboundgroup"
"github.com/metacubex/mihomo/common/structure"
)
func trimArr(arr []string) (r []string) {
for _, e := range arr {
r = append(r, strings.Trim(e, " "))
}
return
}
// Check if ProxyGroups form DAG(Directed Acyclic Graph), and sort all ProxyGroups by dependency order.
// Meanwhile, record the original index in the config file.
// If loop is detected, return an error with location of loop.

View File

@ -34,22 +34,48 @@ func ParseParams(params []string) (isSrc bool, noResolve bool) {
return
}
func ParseRulePayload(ruleRaw string) (string, string, []string) {
item := strings.Split(ruleRaw, ",")
if len(item) == 1 {
return "", item[0], nil
} else if len(item) == 2 {
return item[0], item[1], nil
} else if len(item) > 2 {
// keep in sync with config/config.go [parseRules]
if item[0] == "NOT" || item[0] == "OR" || item[0] == "AND" || item[0] == "SUB-RULE" || item[0] == "DOMAIN-REGEX" || item[0] == "PROCESS-NAME-REGEX" || item[0] == "PROCESS-PATH-REGEX" {
return item[0], strings.Join(item[1:], ","), nil
} else {
return item[0], item[1], item[2:]
func trimArr(arr []string) (r []string) {
for _, e := range arr {
r = append(r, strings.Trim(e, " "))
}
return
}
// ParseRulePayload parse rule format like:
// `tp,payload,target(,params...)` or `tp,payload(,params...)`
// needTarget control the format contains `target` in string
func ParseRulePayload(ruleRaw string, needTarget bool) (tp, payload, target string, params []string) {
item := trimArr(strings.Split(ruleRaw, ","))
tp = strings.ToUpper(item[0])
if len(item) > 1 {
switch tp {
case "MATCH":
// MATCH doesn't contain payload and params
target = item[1]
case "NOT", "OR", "AND", "SUB-RULE", "DOMAIN-REGEX", "PROCESS-NAME-REGEX", "PROCESS-PATH-REGEX":
// some type of rules that has comma in payload and don't need params
if needTarget {
l := len(item)
target = item[l-1] // don't have params so target must at the end of slices
item = item[:l-1] // remove the target from slices
}
payload = strings.Join(item[1:], ",")
default:
payload = item[1]
if len(item) > 2 {
if needTarget {
target = item[2]
if len(item) > 3 {
params = item[3:]
}
} else {
params = item[2:]
}
}
}
}
return "", "", nil
return
}
type ParseRuleFunc func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (C.Rule, error)

View File

@ -78,14 +78,14 @@ func (r Range) containRange(preStart, preEnd int) bool {
}
func (logic *Logic) payloadToRule(subPayload string, parseRule common.ParseRuleFunc) (C.Rule, error) {
tp, payload, param := common.ParseRulePayload(subPayload)
tp, payload, target, param := common.ParseRulePayload(subPayload, false)
switch tp {
case "MATCH", "SUB-RULE":
return nil, fmt.Errorf("unsupported rule type [%s] on logic rule", tp)
case "":
return nil, fmt.Errorf("[%s] format is error", subPayload)
}
return parseRule(tp, payload, "", param, nil)
return parseRule(tp, payload, target, param, nil)
}
func (logic *Logic) format(payload string) ([]Range, error) {

View File

@ -10,6 +10,10 @@ import (
)
func ParseRule(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error) {
if tp != "MATCH" && payload == "" { // only MATCH allowed doesn't contain payload
return nil, fmt.Errorf("missing subsequent parameters: %s", tp)
}
switch tp {
case "DOMAIN":
parsed = RC.NewDomain(payload, target)
@ -83,8 +87,6 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string]
case "MATCH":
parsed = RC.NewMatch(target)
parseErr = nil
case "":
parseErr = fmt.Errorf("missing subsequent parameters: %s", payload)
default:
parseErr = fmt.Errorf("unsupported rule type: %s", tp)
}

View File

@ -12,7 +12,7 @@ import (
type classicalStrategy struct {
rules []C.Rule
count int
parse func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error)
parse common.ParseRuleFunc
}
func (c *classicalStrategy) Behavior() P.RuleBehavior {
@ -39,25 +39,26 @@ func (c *classicalStrategy) Reset() {
}
func (c *classicalStrategy) Insert(rule string) {
ruleType, rule, params := common.ParseRulePayload(rule)
r, err := c.parse(ruleType, rule, "", params)
r, err := c.payloadToRule(rule)
if err != nil {
log.Warnln("parse classical rule error: %s", err.Error())
log.Warnln("parse classical rule [%s] error: %s", rule, err.Error())
} else {
c.rules = append(c.rules, r)
c.count++
}
}
func (c *classicalStrategy) payloadToRule(rule string) (C.Rule, error) {
tp, payload, target, params := common.ParseRulePayload(rule, false)
switch tp {
case "MATCH", "RULE-SET", "SUB-RULE":
return nil, fmt.Errorf("unsupported rule type on classical rule-set: %s", tp)
}
return c.parse(tp, payload, target, params, nil)
}
func (c *classicalStrategy) FinishInsert() {}
func NewClassicalStrategy(parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) *classicalStrategy {
return &classicalStrategy{rules: []C.Rule{}, parse: func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error) {
switch tp {
case "MATCH", "RULE-SET", "SUB-RULE":
return nil, fmt.Errorf("unsupported rule type on classical rule-set: %s", tp)
default:
return parse(tp, payload, target, params, nil)
}
}}
func NewClassicalStrategy(parse common.ParseRuleFunc) *classicalStrategy {
return &classicalStrategy{rules: []C.Rule{}, parse: parse}
}