diff --git a/adapter/provider/fetcher.go b/adapter/provider/fetcher.go index 4a7be8b1..0426ecc2 100644 --- a/adapter/provider/fetcher.go +++ b/adapter/provider/fetcher.go @@ -16,28 +16,28 @@ var ( dirMode os.FileMode = 0o755 ) -type parser = func([]byte) (any, error) +type parser[V any] func([]byte) (V, error) -type fetcher struct { +type fetcher[V any] struct { name string vehicle types.Vehicle updatedAt *time.Time ticker *time.Ticker done chan struct{} hash [16]byte - parser parser - onUpdate func(any) + parser parser[V] + onUpdate func(V) } -func (f *fetcher) Name() string { +func (f *fetcher[V]) Name() string { return f.name } -func (f *fetcher) VehicleType() types.VehicleType { +func (f *fetcher[V]) VehicleType() types.VehicleType { return f.vehicle.Type() } -func (f *fetcher) Initial() (any, error) { +func (f *fetcher[V]) Initial() (V, error) { var ( buf []byte err error @@ -53,24 +53,24 @@ func (f *fetcher) Initial() (any, error) { } if err != nil { - return nil, err + return getZero[V](), err } proxies, err := f.parser(buf) if err != nil { if !isLocal { - return nil, err + return getZero[V](), err } // parse local file error, fallback to remote buf, err = f.vehicle.Read() if err != nil { - return nil, err + return getZero[V](), err } proxies, err = f.parser(buf) if err != nil { - return nil, err + return getZero[V](), err } isLocal = false @@ -78,7 +78,7 @@ func (f *fetcher) Initial() (any, error) { if f.vehicle.Type() != types.File && !isLocal { if err := safeWrite(f.vehicle.Path(), buf); err != nil { - return nil, err + return getZero[V](), err } } @@ -92,28 +92,28 @@ func (f *fetcher) Initial() (any, error) { return proxies, nil } -func (f *fetcher) Update() (any, bool, error) { +func (f *fetcher[V]) Update() (V, bool, error) { buf, err := f.vehicle.Read() if err != nil { - return nil, false, err + return getZero[V](), false, err } now := time.Now() hash := md5.Sum(buf) if bytes.Equal(f.hash[:], hash[:]) { f.updatedAt = &now - os.Chtimes(f.vehicle.Path(), now, now) - return nil, true, nil + _ = os.Chtimes(f.vehicle.Path(), now, now) + return getZero[V](), true, nil } proxies, err := f.parser(buf) if err != nil { - return nil, false, err + return getZero[V](), false, err } if f.vehicle.Type() != types.File { if err := safeWrite(f.vehicle.Path(), buf); err != nil { - return nil, false, err + return getZero[V](), false, err } } @@ -123,14 +123,14 @@ func (f *fetcher) Update() (any, bool, error) { return proxies, false, nil } -func (f *fetcher) Destroy() error { +func (f *fetcher[V]) Destroy() error { if f.ticker != nil { f.done <- struct{}{} } return nil } -func (f *fetcher) pullLoop() { +func (f *fetcher[V]) pullLoop() { for { select { case <-f.ticker.C: @@ -168,13 +168,13 @@ func safeWrite(path string, buf []byte) error { return os.WriteFile(path, buf, fileMode) } -func newFetcher(name string, interval time.Duration, vehicle types.Vehicle, parser parser, onUpdate func(any)) *fetcher { +func newFetcher[V any](name string, interval time.Duration, vehicle types.Vehicle, parser parser[V], onUpdate func(V)) *fetcher[V] { var ticker *time.Ticker if interval != 0 { ticker = time.NewTicker(interval) } - return &fetcher{ + return &fetcher[V]{ name: name, ticker: ticker, vehicle: vehicle, @@ -183,3 +183,8 @@ func newFetcher(name string, interval time.Duration, vehicle types.Vehicle, pars onUpdate: onUpdate, } } + +func getZero[V any]() V { + var result V + return result +} diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 69f547da..ffe00447 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -23,13 +23,13 @@ type ProxySchema struct { Proxies []map[string]any `yaml:"proxies"` } -// for auto gc +// ProxySetProvider for auto gc type ProxySetProvider struct { *proxySetProvider } type proxySetProvider struct { - *fetcher + *fetcher[[]C.Proxy] proxies []C.Proxy healthCheck *HealthCheck } @@ -93,7 +93,7 @@ func (pp *proxySetProvider) setProxies(proxies []C.Proxy) { func stopProxyProvider(pd *ProxySetProvider) { pd.healthCheck.close() - pd.fetcher.Destroy() + _ = pd.fetcher.Destroy() } func NewProxySetProvider(name string, interval time.Duration, filter string, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) { @@ -111,45 +111,7 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, veh healthCheck: hc, } - onUpdate := func(elm any) { - ret := elm.([]C.Proxy) - pd.setProxies(ret) - } - - proxiesParseAndFilter := func(buf []byte) (any, error) { - schema := &ProxySchema{} - - if err := yaml.Unmarshal(buf, schema); err != nil { - return nil, err - } - - if schema.Proxies == nil { - return nil, errors.New("file must have a `proxies` field") - } - - proxies := []C.Proxy{} - for idx, mapping := range schema.Proxies { - if name, ok := mapping["name"]; ok && len(filter) > 0 && !filterReg.MatchString(name.(string)) { - continue - } - proxy, err := adapter.ParseProxy(mapping) - if err != nil { - return nil, fmt.Errorf("proxy %d error: %w", idx, err) - } - proxies = append(proxies, proxy) - } - - if len(proxies) == 0 { - if len(filter) > 0 { - return nil, errors.New("doesn't match any proxy, please check your filter") - } - return nil, errors.New("file doesn't have any proxy") - } - - return proxies, nil - } - - fetcher := newFetcher(name, interval, vehicle, proxiesParseAndFilter, onUpdate) + fetcher := newFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, filterReg), proxiesOnUpdate(pd)) pd.fetcher = fetcher wrapper := &ProxySetProvider{pd} @@ -157,7 +119,7 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, veh return wrapper, nil } -// for auto gc +// CompatibleProvider for auto gc type CompatibleProvider struct { *compatibleProvider } @@ -233,3 +195,44 @@ func NewCompatibleProvider(name string, proxies []C.Proxy, hc *HealthCheck) (*Co runtime.SetFinalizer(wrapper, stopCompatibleProvider) return wrapper, nil } + +func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) { + return func(elm []C.Proxy) { + pd.setProxies(elm) + } +} + +func proxiesParseAndFilter(filter string, filterReg *regexp.Regexp) parser[[]C.Proxy] { + return func(buf []byte) ([]C.Proxy, error) { + schema := &ProxySchema{} + + if err := yaml.Unmarshal(buf, schema); err != nil { + return nil, err + } + + if schema.Proxies == nil { + return nil, errors.New("file must have a `proxies` field") + } + + proxies := []C.Proxy{} + for idx, mapping := range schema.Proxies { + if name, ok := mapping["name"]; ok && len(filter) > 0 && !filterReg.MatchString(name.(string)) { + continue + } + proxy, err := adapter.ParseProxy(mapping) + if err != nil { + return nil, fmt.Errorf("proxy %d error: %w", idx, err) + } + proxies = append(proxies, proxy) + } + + if len(proxies) == 0 { + if len(filter) > 0 { + return nil, errors.New("doesn't match any proxy, please check your filter") + } + return nil, errors.New("file doesn't have any proxy") + } + + return proxies, nil + } +}