github.com/sagernet/sing-box@v1.9.0-rc.20/experimental/clashapi.go (about)

     1  package experimental
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"sort"
     7  
     8  	"github.com/sagernet/sing-box/adapter"
     9  	C "github.com/sagernet/sing-box/constant"
    10  	"github.com/sagernet/sing-box/log"
    11  	"github.com/sagernet/sing-box/option"
    12  	"github.com/sagernet/sing/common"
    13  )
    14  
    15  type ClashServerConstructor = func(ctx context.Context, router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error)
    16  
    17  var clashServerConstructor ClashServerConstructor
    18  
    19  func RegisterClashServerConstructor(constructor ClashServerConstructor) {
    20  	clashServerConstructor = constructor
    21  }
    22  
    23  func NewClashServer(ctx context.Context, router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
    24  	if clashServerConstructor == nil {
    25  		return nil, os.ErrInvalid
    26  	}
    27  	return clashServerConstructor(ctx, router, logFactory, options)
    28  }
    29  
    30  func CalculateClashModeList(options option.Options) []string {
    31  	var clashModes []string
    32  	clashModes = append(clashModes, extraClashModeFromRule(common.PtrValueOrDefault(options.Route).Rules)...)
    33  	clashModes = append(clashModes, extraClashModeFromDNSRule(common.PtrValueOrDefault(options.DNS).Rules)...)
    34  	clashModes = common.FilterNotDefault(common.Uniq(clashModes))
    35  	predefinedOrder := []string{
    36  		"Rule", "Global", "Direct",
    37  	}
    38  	var newClashModes []string
    39  	for _, mode := range clashModes {
    40  		if !common.Contains(predefinedOrder, mode) {
    41  			newClashModes = append(newClashModes, mode)
    42  		}
    43  	}
    44  	sort.Strings(newClashModes)
    45  	for _, mode := range predefinedOrder {
    46  		if common.Contains(clashModes, mode) {
    47  			newClashModes = append(newClashModes, mode)
    48  		}
    49  	}
    50  	return newClashModes
    51  }
    52  
    53  func extraClashModeFromRule(rules []option.Rule) []string {
    54  	var clashMode []string
    55  	for _, rule := range rules {
    56  		switch rule.Type {
    57  		case C.RuleTypeDefault:
    58  			if rule.DefaultOptions.ClashMode != "" {
    59  				clashMode = append(clashMode, rule.DefaultOptions.ClashMode)
    60  			}
    61  		case C.RuleTypeLogical:
    62  			clashMode = append(clashMode, extraClashModeFromRule(rule.LogicalOptions.Rules)...)
    63  		}
    64  	}
    65  	return clashMode
    66  }
    67  
    68  func extraClashModeFromDNSRule(rules []option.DNSRule) []string {
    69  	var clashMode []string
    70  	for _, rule := range rules {
    71  		switch rule.Type {
    72  		case C.RuleTypeDefault:
    73  			if rule.DefaultOptions.ClashMode != "" {
    74  				clashMode = append(clashMode, rule.DefaultOptions.ClashMode)
    75  			}
    76  		case C.RuleTypeLogical:
    77  			clashMode = append(clashMode, extraClashModeFromDNSRule(rule.LogicalOptions.Rules)...)
    78  		}
    79  	}
    80  	return clashMode
    81  }