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 }