github.com/chwjbn/xclash@v0.2.0/adapter/outboundgroup/parser.go (about)

     1  package outboundgroup
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/chwjbn/xclash/adapter/outbound"
     8  	"github.com/chwjbn/xclash/adapter/provider"
     9  	"github.com/chwjbn/xclash/common/structure"
    10  	C "github.com/chwjbn/xclash/constant"
    11  	types "github.com/chwjbn/xclash/constant/provider"
    12  )
    13  
    14  var (
    15  	errFormat            = errors.New("format error")
    16  	errType              = errors.New("unsupport type")
    17  	errMissProxy         = errors.New("`use` or `proxies` missing")
    18  	errMissHealthCheck   = errors.New("`url` or `interval` missing")
    19  	errDuplicateProvider = errors.New("`duplicate provider name")
    20  )
    21  
    22  type GroupCommonOption struct {
    23  	outbound.BasicOption
    24  	Name       string   `group:"name"`
    25  	Type       string   `group:"type"`
    26  	Proxies    []string `group:"proxies,omitempty"`
    27  	Use        []string `group:"use,omitempty"`
    28  	URL        string   `group:"url,omitempty"`
    29  	Interval   int      `group:"interval,omitempty"`
    30  	Lazy       bool     `group:"lazy,omitempty"`
    31  	DisableUDP bool     `group:"disable-udp,omitempty"`
    32  }
    33  
    34  func ParseProxyGroup(config map[string]interface{}, proxyMap map[string]C.Proxy, providersMap map[string]types.ProxyProvider) (C.ProxyAdapter, error) {
    35  	decoder := structure.NewDecoder(structure.Option{TagName: "group", WeaklyTypedInput: true})
    36  
    37  	groupOption := &GroupCommonOption{
    38  		Lazy: true,
    39  	}
    40  	if err := decoder.Decode(config, groupOption); err != nil {
    41  		return nil, errFormat
    42  	}
    43  
    44  	if groupOption.Type == "" || groupOption.Name == "" {
    45  		return nil, errFormat
    46  	}
    47  
    48  	groupName := groupOption.Name
    49  
    50  	providers := []types.ProxyProvider{}
    51  
    52  	if len(groupOption.Proxies) == 0 && len(groupOption.Use) == 0 {
    53  		return nil, errMissProxy
    54  	}
    55  
    56  	if len(groupOption.Proxies) != 0 {
    57  		ps, err := getProxies(proxyMap, groupOption.Proxies)
    58  		if err != nil {
    59  			return nil, err
    60  		}
    61  
    62  		if _, ok := providersMap[groupName]; ok {
    63  			return nil, errDuplicateProvider
    64  		}
    65  
    66  		// select don't need health check
    67  		if groupOption.Type == "select" || groupOption.Type == "relay" {
    68  			hc := provider.NewHealthCheck(ps, "", 0, true)
    69  			pd, err := provider.NewCompatibleProvider(groupName, ps, hc)
    70  			if err != nil {
    71  				return nil, err
    72  			}
    73  
    74  			providers = append(providers, pd)
    75  			providersMap[groupName] = pd
    76  		} else {
    77  			if groupOption.URL == "" || groupOption.Interval == 0 {
    78  				return nil, errMissHealthCheck
    79  			}
    80  
    81  			hc := provider.NewHealthCheck(ps, groupOption.URL, uint(groupOption.Interval), groupOption.Lazy)
    82  			pd, err := provider.NewCompatibleProvider(groupName, ps, hc)
    83  			if err != nil {
    84  				return nil, err
    85  			}
    86  
    87  			providers = append(providers, pd)
    88  			providersMap[groupName] = pd
    89  		}
    90  	}
    91  
    92  	if len(groupOption.Use) != 0 {
    93  		list, err := getProviders(providersMap, groupOption.Use)
    94  		if err != nil {
    95  			return nil, err
    96  		}
    97  		providers = append(providers, list...)
    98  	}
    99  
   100  	var group C.ProxyAdapter
   101  	switch groupOption.Type {
   102  	case "url-test":
   103  		opts := parseURLTestOption(config)
   104  		group = NewURLTest(groupOption, providers, opts...)
   105  	case "select":
   106  		group = NewSelector(groupOption, providers)
   107  	case "fallback":
   108  		group = NewFallback(groupOption, providers)
   109  	case "load-balance":
   110  		strategy := parseStrategy(config)
   111  		return NewLoadBalance(groupOption, providers, strategy)
   112  	case "relay":
   113  		group = NewRelay(groupOption, providers)
   114  	default:
   115  		return nil, fmt.Errorf("%w: %s", errType, groupOption.Type)
   116  	}
   117  
   118  	return group, nil
   119  }
   120  
   121  func getProxies(mapping map[string]C.Proxy, list []string) ([]C.Proxy, error) {
   122  	var ps []C.Proxy
   123  	for _, name := range list {
   124  		p, ok := mapping[name]
   125  		if !ok {
   126  			return nil, fmt.Errorf("'%s' not found", name)
   127  		}
   128  		ps = append(ps, p)
   129  	}
   130  	return ps, nil
   131  }
   132  
   133  func getProviders(mapping map[string]types.ProxyProvider, list []string) ([]types.ProxyProvider, error) {
   134  	var ps []types.ProxyProvider
   135  	for _, name := range list {
   136  		p, ok := mapping[name]
   137  		if !ok {
   138  			return nil, fmt.Errorf("'%s' not found", name)
   139  		}
   140  
   141  		if p.VehicleType() == types.Compatible {
   142  			return nil, fmt.Errorf("proxy group %s can't contains in `use`", name)
   143  		}
   144  		ps = append(ps, p)
   145  	}
   146  	return ps, nil
   147  }