route: merge rule_set branches into outer rules

Treat rule_set items as merged branches instead of standalone boolean
sub-items.

Evaluate each branch inside a referenced rule-set as if it were merged
into the outer rule and keep OR semantics between branches. This lets
outer grouped fields satisfy matching groups inside a branch without
introducing a standalone outer fallback or cross-branch state union.

Keep inherited grouped state outside inverted default and logical
branches. Negated rule-set branches now evaluate !(...) against their
own conditions and only reapply the outer grouped match after negation
succeeds, so configs like outer-group && !inner-condition continue to
work.

Add regression tests for same-group merged matches, cross-group and
extra-AND failures, DNS merged-branch behaviour, and inverted merged
branches. Update the route and DNS rule docs to clarify that rule-set
branches merge into the outer rule while keeping OR semantics between
branches.
This commit is contained in:
世界
2026-03-25 10:32:09 +08:00
parent fcdea284e1
commit fdfd0ffb07
10 changed files with 308 additions and 32 deletions

View File

@@ -326,11 +326,15 @@ func (s *RemoteRuleSet) Match(metadata *adapter.InboundContext) bool {
}
func (s *RemoteRuleSet) matchStates(metadata *adapter.InboundContext) ruleMatchStateSet {
return s.matchStatesWithBase(metadata, 0)
}
func (s *RemoteRuleSet) matchStatesWithBase(metadata *adapter.InboundContext, base ruleMatchState) ruleMatchStateSet {
var stateSet ruleMatchStateSet
for _, rule := range s.rules {
nestedMetadata := *metadata
nestedMetadata.ResetRuleMatchCache()
stateSet = stateSet.merge(matchHeadlessRuleStates(rule, &nestedMetadata))
stateSet = stateSet.merge(matchHeadlessRuleStatesWithBase(rule, &nestedMetadata, base))
}
return stateSet
}