diff --git a/.golangci.yml b/.golangci.yml index 7d65a80..7f37cca 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -108,6 +108,7 @@ linters: - prealloc # Too many false-positive. - makezero # Not relevant - dupl # Too strict + - gci # I don't care - gosec # Too strict - gochecknoinits - gochecknoglobals diff --git a/internal/api/v1/query/query.go b/internal/api/v1/query/query.go index 7757004..7b59772 100644 --- a/internal/api/v1/query/query.go +++ b/internal/api/v1/query/query.go @@ -14,7 +14,7 @@ import ( ) func ReloadServer() E.Error { - resp, err := U.Post(fmt.Sprintf("%s/v1/reload", common.APIHTTPURL), "", nil) + resp, err := U.Post(common.APIHTTPURL+"/v1/reload", "", nil) if err != nil { return E.From(err) } diff --git a/internal/config/query.go b/internal/config/query.go index 8933cf3..99131cb 100644 --- a/internal/config/query.go +++ b/internal/config/query.go @@ -69,17 +69,18 @@ func HomepageConfig() homepage.Config { ) } - if entry.IsDocker(r) { + switch { + case entry.IsDocker(r): if item.Category == "" { item.Category = "Docker" } item.SourceType = string(proxy.ProviderTypeDocker) - } else if entry.UseLoadBalance(r) { + case entry.UseLoadBalance(r): if item.Category == "" { item.Category = "Load-balanced" } item.SourceType = "loadbalancer" - } else { + default: if item.Category == "" { item.Category = "Others" } diff --git a/internal/docker/client.go b/internal/docker/client.go index f1ecf5b..f3b176f 100644 --- a/internal/docker/client.go +++ b/internal/docker/client.go @@ -52,13 +52,10 @@ func (c *SharedClient) Connected() bool { } // if the client is still referenced, this is no-op. -func (c *SharedClient) Close() error { - if !c.Connected() { - return nil +func (c *SharedClient) Close() { + if c.Connected() { + c.refCount.Sub() } - - c.refCount.Sub() - return nil } // ConnectClient creates a new Docker client connection to the specified host. @@ -115,7 +112,6 @@ func ConnectClient(host string) (Client, error) { } client, err := client.NewClientWithOpts(opt...) - if err != nil { return nil, err } diff --git a/internal/docker/idlewatcher/loading_page.go b/internal/docker/idlewatcher/loading_page.go index d545c9c..80913c7 100644 --- a/internal/docker/idlewatcher/loading_page.go +++ b/internal/docker/idlewatcher/loading_page.go @@ -3,7 +3,6 @@ package idlewatcher import ( "bytes" _ "embed" - "fmt" "strings" "text/template" @@ -21,7 +20,7 @@ var loadingPage []byte var loadingPageTmpl = template.Must(template.New("loading_page").Parse(string(loadingPage))) func (w *Watcher) makeLoadingPageBody() []byte { - msg := fmt.Sprintf("%s is starting...", w.ContainerName) + msg := w.ContainerName + " is starting..." data := new(templateData) data.CheckRedirectHeader = common.HeaderCheckRedirect diff --git a/internal/docker/idlewatcher/waker.go b/internal/docker/idlewatcher/waker.go index 6fda047..ea5fed7 100644 --- a/internal/docker/idlewatcher/waker.go +++ b/internal/docker/idlewatcher/waker.go @@ -45,17 +45,18 @@ func newWaker(providerSubTask task.Task, entry entry.Entry, rp *gphttp.ReversePr return nil, E.Errorf("register watcher: %w", err) } - if rp != nil { + switch { + case rp != nil: waker.hc = health.NewHTTPHealthChecker(entry.TargetURL(), hcCfg, rp.Transport) - } else if stream != nil { + case stream != nil: waker.hc = health.NewRawHealthChecker(entry.TargetURL(), hcCfg) - } else { + default: panic("both nil") } return watcher, nil } -// lifetime should follow route provider +// lifetime should follow route provider. func NewHTTPWaker(providerSubTask task.Task, entry entry.Entry, rp *gphttp.ReverseProxy) (Waker, E.Error) { return newWaker(providerSubTask, entry, rp, nil) } diff --git a/internal/docker/idlewatcher/waker_stream.go b/internal/docker/idlewatcher/waker_stream.go index 5cd648b..f1ade45 100644 --- a/internal/docker/idlewatcher/waker_stream.go +++ b/internal/docker/idlewatcher/waker_stream.go @@ -28,7 +28,7 @@ func (w *Watcher) Accept() (conn types.StreamConn, err error) { return } if wakeErr := w.wakeFromStream(); wakeErr != nil { - w.WakeError(wakeErr).Msg("error waking from stream") + w.WakeError(wakeErr) } return } @@ -58,7 +58,7 @@ func (w *Watcher) wakeFromStream() error { wakeErr := w.wakeIfStopped() if wakeErr != nil { wakeErr = fmt.Errorf("%s failed: %w", w.String(), wakeErr) - w.WakeError(wakeErr).Msg("wake failed") + w.WakeError(wakeErr) return wakeErr } diff --git a/internal/docker/idlewatcher/watcher.go b/internal/docker/idlewatcher/watcher.go index d23502d..69593c2 100644 --- a/internal/docker/idlewatcher/watcher.go +++ b/internal/docker/idlewatcher/watcher.go @@ -17,7 +17,6 @@ import ( U "github.com/yusing/go-proxy/internal/utils" F "github.com/yusing/go-proxy/internal/utils/functional" "github.com/yusing/go-proxy/internal/watcher" - W "github.com/yusing/go-proxy/internal/watcher" "github.com/yusing/go-proxy/internal/watcher/events" ) @@ -108,8 +107,8 @@ func (w *Watcher) WakeTrace() *zerolog.Event { return w.Trace().Str("action", "wake") } -func (w *Watcher) WakeError(err error) *zerolog.Event { - return w.Err(err).Str("action", "wake") +func (w *Watcher) WakeError(err error) { + w.Err(err).Str("action", "wake").Msg("error") } func (w *Watcher) LogReason(action, reason string) { @@ -204,17 +203,17 @@ func (w *Watcher) resetIdleTimer() { func (w *Watcher) getEventCh(dockerWatcher watcher.DockerWatcher) (eventTask task.Task, eventCh <-chan events.Event, errCh <-chan E.Error) { eventTask = w.task.Subtask("docker event watcher") - eventCh, errCh = dockerWatcher.EventsWithOptions(eventTask.Context(), W.DockerListOptions{ - Filters: W.NewDockerFilter( - W.DockerFilterContainer, - W.DockerFilterContainerNameID(w.ContainerID), - W.DockerFilterStart, - W.DockerFilterStop, - W.DockerFilterDie, - W.DockerFilterKill, - W.DockerFilterDestroy, - W.DockerFilterPause, - W.DockerFilterUnpause, + eventCh, errCh = dockerWatcher.EventsWithOptions(eventTask.Context(), watcher.DockerListOptions{ + Filters: watcher.NewDockerFilter( + watcher.DockerFilterContainer, + watcher.DockerFilterContainerNameID(w.ContainerID), + watcher.DockerFilterStart, + watcher.DockerFilterStop, + watcher.DockerFilterDie, + watcher.DockerFilterKill, + watcher.DockerFilterDestroy, + watcher.DockerFilterPause, + watcher.DockerFilterUnpause, ), }) return @@ -230,9 +229,9 @@ func (w *Watcher) getEventCh(dockerWatcher watcher.DockerWatcher) (eventTask tas // stop method. // // it exits only if the context is canceled, the container is destroyed, -// errors occured on docker client, or route provider died (mainly caused by config reload). +// errors occurred on docker client, or route provider died (mainly caused by config reload). func (w *Watcher) watchUntilDestroy() (returnCause error) { - dockerWatcher := W.NewDockerWatcherWithClient(w.client) + dockerWatcher := watcher.NewDockerWatcherWithClient(w.client) eventTask, dockerEventCh, dockerEventErrCh := w.getEventCh(dockerWatcher) defer eventTask.Finish("stopped") @@ -279,9 +278,13 @@ func (w *Watcher) watchUntilDestroy() (returnCause error) { case <-w.ticker.C: w.ticker.Stop() if w.ContainerRunning { - if err := w.stopByMethod(); err != nil && !errors.Is(err, context.Canceled) { + err := w.stopByMethod() + switch { + case errors.Is(err, context.Canceled): + continue + case err != nil: w.Err(err).Msgf("container stop with method %q failed", w.StopMethod) - } else { + default: w.LogReason("container stopped", "idle timeout") } } diff --git a/internal/error/builder.go b/internal/error/builder.go index 12aa474..80681c1 100644 --- a/internal/error/builder.go +++ b/internal/error/builder.go @@ -27,26 +27,31 @@ func (b *Builder) HasError() bool { return len(b.errs) > 0 } -func (b *Builder) Error() Error { +func (b *Builder) error() Error { if !b.HasError() { return nil } - if len(b.errs) == 1 { - return From(b.errs[0]) - } return &nestedError{Err: New(b.about), Extras: b.errs} } +func (b *Builder) Error() Error { + if len(b.errs) == 1 { + return From(b.errs[0]) + } + return b.error() +} + func (b *Builder) String() string { - if !b.HasError() { + err := b.error() + if err == nil { return "" } - return (&nestedError{Err: New(b.about), Extras: b.errs}).Error() + return err.Error() } // Add adds an error to the Builder. // -// adding nil is no-op, +// adding nil is no-op. func (b *Builder) Add(err error) *Builder { if err == nil { return b @@ -90,6 +95,21 @@ func (b *Builder) Addf(format string, args ...any) *Builder { return b } +func (b *Builder) AddFrom(other *Builder, flatten bool) *Builder { + if other == nil || !other.HasError() { + return b + } + + b.Lock() + defer b.Unlock() + if flatten { + b.errs = append(b.errs, other.errs...) + } else { + b.errs = append(b.errs, other.error()) + } + return b +} + func (b *Builder) AddRange(errs ...error) *Builder { b.Lock() defer b.Unlock() diff --git a/internal/error/error.go b/internal/error/error.go index e584653..5e744ba 100644 --- a/internal/error/error.go +++ b/internal/error/error.go @@ -22,7 +22,7 @@ type Error interface { Subjectf(format string, args ...any) Error } -// this makes JSON marshalling work, +// this makes JSON marshaling work, // as the builtin one doesn't. type errStr string diff --git a/internal/logging/logging.go b/internal/logging/logging.go index 664ea4a..a77c413 100644 --- a/internal/logging/logging.go +++ b/internal/logging/logging.go @@ -15,13 +15,14 @@ func init() { var level zerolog.Level var exclude []string - if common.IsTrace { + switch { + case common.IsTrace: timeFmt = "04:05" level = zerolog.TraceLevel - } else if common.IsDebug { + case common.IsDebug: timeFmt = "01-02 15:04" level = zerolog.DebugLevel - } else { + default: timeFmt = "01-02 15:04" level = zerolog.InfoLevel exclude = []string{"module"} diff --git a/internal/net/http/content_type.go b/internal/net/http/content_type.go index 59b021d..3b4be65 100644 --- a/internal/net/http/content_type.go +++ b/internal/net/http/content_type.go @@ -5,8 +5,10 @@ import ( "net/http" ) -type ContentType string -type AcceptContentType []ContentType +type ( + ContentType string + AcceptContentType []ContentType +) func GetContentType(h http.Header) ContentType { ct := h.Get("Content-Type") diff --git a/internal/net/http/loadbalancer/loadbalancer.go b/internal/net/http/loadbalancer/loadbalancer.go index 4812116..edda918 100644 --- a/internal/net/http/loadbalancer/loadbalancer.go +++ b/internal/net/http/loadbalancer/loadbalancer.go @@ -55,7 +55,6 @@ func New(cfg *Config) *LoadBalancer { Logger: logger.With().Str("name", cfg.Link).Logger(), Config: new(Config), pool: newPool(), - task: task.DummyTask(), } lb.UpdateConfigIfNeeded(cfg) return lb diff --git a/internal/net/http/loadbalancer/mode.go b/internal/net/http/loadbalancer/mode.go index 919d311..cea093d 100644 --- a/internal/net/http/loadbalancer/mode.go +++ b/internal/net/http/loadbalancer/mode.go @@ -1,7 +1,7 @@ package loadbalancer import ( - U "github.com/yusing/go-proxy/internal/utils" + "github.com/yusing/go-proxy/internal/utils/strutils" ) type Mode string @@ -14,7 +14,7 @@ const ( ) func (mode *Mode) ValidateUpdate() bool { - switch U.ToLowerNoSnake(string(*mode)) { + switch strutils.ToLowerNoSnake(string(*mode)) { case "": return true case string(RoundRobin): diff --git a/internal/net/http/middleware/cloudflare_real_ip.go b/internal/net/http/middleware/cloudflare_real_ip.go index f2c5b92..aa82345 100644 --- a/internal/net/http/middleware/cloudflare_real_ip.go +++ b/internal/net/http/middleware/cloudflare_real_ip.go @@ -109,9 +109,9 @@ func fetchUpdateCFIPRange(endpoint string, cfCIDRs []*types.CIDR) error { _, cidr, err := net.ParseCIDR(line) if err != nil { return fmt.Errorf("cloudflare responeded an invalid CIDR: %s", line) - } else { - cfCIDRs = append(cfCIDRs, (*types.CIDR)(cidr)) } + + cfCIDRs = append(cfCIDRs, (*types.CIDR)(cidr)) } return nil diff --git a/internal/net/http/middleware/errorpage/error_page.go b/internal/net/http/middleware/errorpage/error_page.go index 7f63d5d..7226f67 100644 --- a/internal/net/http/middleware/errorpage/error_page.go +++ b/internal/net/http/middleware/errorpage/error_page.go @@ -19,24 +19,33 @@ import ( const errPagesBasePath = common.ErrorPagesBasePath var ( + setupMu sync.Mutex dirWatcher W.Watcher fileContentMap = F.NewMapOf[string, []byte]() ) -var setup = sync.OnceFunc(func() { +func setup() { + setupMu.Lock() + defer setupMu.Unlock() + + if dirWatcher != nil { + return + } + task := task.GlobalTask("error page") dirWatcher = W.NewDirectoryWatcher(task.Subtask("dir watcher"), errPagesBasePath) loadContent() go watchDir(task) -}) +} func GetStaticFile(filename string) ([]byte, bool) { + setup() return fileContentMap.Load(filename) } // try .html -> 404.html -> not ok. func GetErrorPageByStatus(statusCode int) (content []byte, ok bool) { - content, ok = fileContentMap.Load(fmt.Sprintf("%d.html", statusCode)) + content, ok = GetStaticFile(fmt.Sprintf("%d.html", statusCode)) if !ok && statusCode != 404 { return fileContentMap.Load("404.html") } diff --git a/internal/net/http/middleware/middlewares.go b/internal/net/http/middleware/middlewares.go index 4ff3699..111bd63 100644 --- a/internal/net/http/middleware/middlewares.go +++ b/internal/net/http/middleware/middlewares.go @@ -9,7 +9,6 @@ import ( "github.com/yusing/go-proxy/internal/common" E "github.com/yusing/go-proxy/internal/error" "github.com/yusing/go-proxy/internal/utils" - U "github.com/yusing/go-proxy/internal/utils" "github.com/yusing/go-proxy/internal/utils/strutils" ) @@ -21,7 +20,7 @@ var ( ) func Get(name string) (*Middleware, Error) { - middleware, ok := allMiddlewares[U.ToLowerNoSnake(name)] + middleware, ok := allMiddlewares[strutils.ToLowerNoSnake(name)] if !ok { return nil, ErrUnknownMiddleware. Subject(name). @@ -34,7 +33,7 @@ func All() map[string]*Middleware { return allMiddlewares } -// initialize middleware names and label parsers +// initialize middleware names and label parsers. func init() { allMiddlewares = map[string]*Middleware{ "setxforwarded": SetXForwarded, @@ -67,7 +66,7 @@ func init() { func LoadComposeFiles() { errs := E.NewBuilder("middleware compile errors") - middlewareDefs, err := U.ListFiles(common.MiddlewareComposeBasePath, 0) + middlewareDefs, err := utils.ListFiles(common.MiddlewareComposeBasePath, 0) if err != nil { logger.Err(err).Msg("failed to list middleware definitions") return @@ -82,7 +81,7 @@ func LoadComposeFiles() { errs.Add(ErrDuplicatedMiddleware.Subject(name)) continue } - allMiddlewares[U.ToLowerNoSnake(name)] = m + allMiddlewares[strutils.ToLowerNoSnake(name)] = m logger.Info(). Str("name", name). Str("src", path.Base(defFile)). diff --git a/internal/net/http/middleware/trace.go b/internal/net/http/middleware/trace.go index 2b444f3..b1ad036 100644 --- a/internal/net/http/middleware/trace.go +++ b/internal/net/http/middleware/trace.go @@ -22,8 +22,10 @@ type Trace struct { type Traces []*Trace -var traces = Traces{} -var tracesMu sync.Mutex +var ( + traces = make(Traces, 0) + tracesMu sync.Mutex +) const MaxTraceNum = 100 diff --git a/internal/net/http/modify_response_writer.go b/internal/net/http/modify_response_writer.go index a7a82c2..6a8122f 100644 --- a/internal/net/http/modify_response_writer.go +++ b/internal/net/http/modify_response_writer.go @@ -10,18 +10,20 @@ import ( "net/http" ) -type ModifyResponseFunc func(*http.Response) error -type ModifyResponseWriter struct { - w http.ResponseWriter - r *http.Request +type ( + ModifyResponseFunc func(*http.Response) error + ModifyResponseWriter struct { + w http.ResponseWriter + r *http.Request - headerSent bool - code int + headerSent bool + code int - modifier ModifyResponseFunc - modified bool - modifierErr error -} + modifier ModifyResponseFunc + modified bool + modifierErr error + } +) func NewModifyResponseWriter(w http.ResponseWriter, r *http.Request, f ModifyResponseFunc) *ModifyResponseWriter { return &ModifyResponseWriter{ diff --git a/internal/notif/dispatcher.go b/internal/notif/dispatcher.go index 79dcc82..c6d390d 100644 --- a/internal/notif/dispatcher.go +++ b/internal/notif/dispatcher.go @@ -53,15 +53,15 @@ func RegisterProvider(configSubTask task.Task, cfg ProviderConfig) (Provider, er Subject(name). Withf(strutils.DoYouMean(utils.NearestField(name, Providers))) } - if provider, err := createFunc(cfg); err != nil { - return nil, err - } else { + + provider, err := createFunc(cfg) + if err == nil { dispatcher.providers.Add(provider) configSubTask.OnCancel("remove provider", func() { dispatcher.providers.Remove(provider) }) - return provider, nil } + return provider, err } func (disp *Dispatcher) start() { diff --git a/internal/proxy/entry/reverse_proxy.go b/internal/proxy/entry/reverse_proxy.go index 976dde3..fc9d5f4 100644 --- a/internal/proxy/entry/reverse_proxy.go +++ b/internal/proxy/entry/reverse_proxy.go @@ -68,7 +68,7 @@ func validateRPEntry(m *RawEntry, s fields.Scheme, errs *E.Builder) *ReverseProx port := E.Collect(errs, fields.ValidatePort, m.Port) pathPats := E.Collect(errs, fields.ValidatePathPatterns, m.PathPatterns) url := E.Collect(errs, url.Parse, fmt.Sprintf("%s://%s:%d", s, host, port)) - iwCfg := E.Collect(errs, idlewatcher.ValidateConfig, m.Container) + iwCfg := E.Collect(errs, idlewatcher.ValidateConfig, cont) if errs.HasError() { return nil diff --git a/internal/proxy/entry/stream.go b/internal/proxy/entry/stream.go index 5ffabe8..08597d8 100644 --- a/internal/proxy/entry/stream.go +++ b/internal/proxy/entry/stream.go @@ -61,7 +61,7 @@ func validateStreamEntry(m *RawEntry, errs *E.Builder) *StreamEntry { port := E.Collect(errs, fields.ValidateStreamPort, m.Port) scheme := E.Collect(errs, fields.ValidateStreamScheme, m.Scheme) url := E.Collect(errs, net.ParseURL, fmt.Sprintf("%s://%s:%d", scheme.ListeningScheme, host, port.ListeningPort)) - idleWatcherCfg := E.Collect(errs, idlewatcher.ValidateConfig, m.Container) + idleWatcherCfg := E.Collect(errs, idlewatcher.ValidateConfig, cont) if errs.HasError() { return nil diff --git a/internal/route/http.go b/internal/route/http.go index 46edeb8..5230665 100755 --- a/internal/route/http.go +++ b/internal/route/http.go @@ -89,7 +89,6 @@ func NewHTTPRoute(entry *entry.ReverseProxyEntry) (impl, E.Error) { r := &HTTPRoute{ ReverseProxyEntry: entry, rp: rp, - task: task.DummyTask(), l: logger.With(). Str("type", string(entry.Scheme)). Str("name", string(entry.Alias)). diff --git a/internal/route/provider/event_handler.go b/internal/route/provider/event_handler.go index 6c990ce..9a67373 100644 --- a/internal/route/provider/event_handler.go +++ b/internal/route/provider/event_handler.go @@ -18,9 +18,9 @@ type EventHandler struct { updated *E.Builder } -func (provider *Provider) newEventHandler() *EventHandler { +func (p *Provider) newEventHandler() *EventHandler { return &EventHandler{ - provider: provider, + provider: p, errs: E.NewBuilder("event errors"), added: E.NewBuilder("added"), removed: E.NewBuilder("removed"), @@ -60,11 +60,12 @@ func (handler *EventHandler) Handle(parent task.Task, events []watcher.Event) { oldRoutes.RangeAll(func(k string, oldr *route.Route) { newr, ok := newRoutes.Load(k) - if !ok { + switch { + case !ok: handler.Remove(oldr) - } else if handler.matchAny(events, newr) { + case handler.matchAny(events, newr): handler.Update(parent, oldr, newr) - } else if entry.ShouldNotServe(newr) { + case entry.ShouldNotServe(newr): handler.Remove(oldr) } }) @@ -122,11 +123,11 @@ func (handler *EventHandler) Update(parent task.Task, oldRoute *route.Route, new } func (handler *EventHandler) Log() { - results := E.NewBuilder("event occured") - results.Add(handler.added.Error()) - results.Add(handler.removed.Error()) - results.Add(handler.updated.Error()) - results.Add(handler.errs.Error()) + results := E.NewBuilder("event occurred") + results.AddFrom(handler.added, false) + results.AddFrom(handler.removed, false) + results.AddFrom(handler.updated, false) + results.AddFrom(handler.errs, false) if result := results.String(); result != "" { handler.provider.Logger().Info().Msg(result) } diff --git a/internal/route/provider/provider.go b/internal/route/provider/provider.go index e6cb01e..0c87caf 100644 --- a/internal/route/provider/provider.go +++ b/internal/route/provider/provider.go @@ -45,9 +45,7 @@ const ( providerEventFlushInterval = 300 * time.Millisecond ) -var ( - ErrEmptyProviderName = errors.New("empty provider name") -) +var ErrEmptyProviderName = errors.New("empty provider name") func newProvider(name string, t ProviderType) *Provider { return &Provider{ @@ -109,12 +107,11 @@ func (p *Provider) startRoute(parent task.Task, r *R.Route) E.Error { p.routes.Delete(r.Entry.Alias) subtask.Finish(err) // just to ensure return err.Subject(r.Entry.Alias) - } else { - p.routes.Store(r.Entry.Alias, r) - subtask.OnFinished("del from provider", func() { - p.routes.Delete(r.Entry.Alias) - }) } + p.routes.Store(r.Entry.Alias, r) + subtask.OnFinished("del from provider", func() { + p.routes.Delete(r.Entry.Alias) + }) return nil } diff --git a/internal/route/route.go b/internal/route/route.go index 8cace61..fe0f800 100755 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -80,11 +80,12 @@ func FromEntries(entries entry.RawEntries) (Routes, E.Error) { entries.RangeAllParallel(func(alias string, en *entry.RawEntry) { en.Alias = alias r, err := NewRoute(en) - if err != nil { + switch { + case err != nil: b.Add(err.Subject(alias)) - } else if entry.ShouldNotServe(r) { + case entry.ShouldNotServe(r): return - } else { + default: routes.Store(alias, r) } }) diff --git a/internal/route/stream.go b/internal/route/stream.go index cf22ff6..c288a4b 100755 --- a/internal/route/stream.go +++ b/internal/route/stream.go @@ -44,7 +44,6 @@ func NewStreamRoute(entry *entry.StreamEntry) (impl, E.Error) { } return &StreamRoute{ StreamEntry: entry, - task: task.DummyTask(), l: logger.With(). Str("type", string(entry.Scheme.ListeningScheme)). Str("name", entry.TargetName()). diff --git a/internal/server/server.go b/internal/server/server.go index f6f4bf5..a6caae4 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -6,7 +6,6 @@ import ( "errors" "io" "log" - "net/http" "time" diff --git a/internal/utils/io.go b/internal/utils/io.go index c60ff33..d70e168 100644 --- a/internal/utils/io.go +++ b/internal/utils/io.go @@ -102,7 +102,7 @@ func (p BidirectionalPipe) Start() E.Error { // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // This is a copy of io.Copy with context handling -// Author: yusing +// Author: yusing . func Copy(dst *ContextWriter, src *ContextReader) (err error) { size := 32 * 1024 if l, ok := src.Reader.(*io.LimitedReader); ok && int64(size) > l.N { diff --git a/internal/utils/nearest_field.go b/internal/utils/nearest_field.go index 01cae8b..9919dd4 100644 --- a/internal/utils/nearest_field.go +++ b/internal/utils/nearest_field.go @@ -18,9 +18,10 @@ func NearestField(input string, s any) string { if t.Kind() == reflect.Ptr { t = t.Elem() } - if t.Kind() == reflect.Struct { + switch t.Kind() { + case reflect.Struct: fields = make([]string, 0) - for i := 0; i < t.NumField(); i++ { + for i := range t.NumField() { jsonTag, ok := t.Field(i).Tag.Lookup("json") if ok { fields = append(fields, jsonTag) @@ -28,13 +29,13 @@ func NearestField(input string, s any) string { fields = append(fields, t.Field(i).Name) } } - } else if t.Kind() == reflect.Map { + case reflect.Map: keys := reflect.ValueOf(s).MapKeys() fields = make([]string, len(keys)) for i, key := range keys { fields[i] = key.String() } - } else { + default: panic("unsupported type: " + t.String()) } } diff --git a/internal/utils/serialization.go b/internal/utils/serialization.go index 7ab9ce4..b215068 100644 --- a/internal/utils/serialization.go +++ b/internal/utils/serialization.go @@ -27,7 +27,7 @@ var ( ErrInvalidType = E.New("invalid type") ErrNilValue = E.New("nil") ErrUnsettable = E.New("unsettable") - ErrUnsupportedConvertion = E.New("unsupported convertion") + ErrUnsupportedConversion = E.New("unsupported conversion") ErrMapMissingColon = E.New("map missing colon") ErrMapTooManyColons = E.New("map too many colons") ErrUnknownField = E.New("unknown field") @@ -176,10 +176,10 @@ func Deserialize(src SerializedObject, dst any) E.Error { case reflect.Struct: mapping := make(map[string]reflect.Value) for _, field := range reflect.VisibleFields(dstT) { - mapping[ToLowerNoSnake(field.Name)] = dstV.FieldByName(field.Name) + mapping[strutils.ToLowerNoSnake(field.Name)] = dstV.FieldByName(field.Name) } for k, v := range src { - if field, ok := mapping[ToLowerNoSnake(k)]; ok { + if field, ok := mapping[strutils.ToLowerNoSnake(k)]; ok { err := Convert(reflect.ValueOf(v), field) if err != nil { errs.Add(err.Subject(k)) @@ -199,11 +199,11 @@ func Deserialize(src SerializedObject, dst any) E.Error { if err != nil { errs.Add(err.Subject(k)) } - dstV.SetMapIndex(reflect.ValueOf(ToLowerNoSnake(k)), tmp) + dstV.SetMapIndex(reflect.ValueOf(strutils.ToLowerNoSnake(k)), tmp) } return errs.Error() default: - return ErrUnsupportedConvertion.Subject("deserialize to " + dstT.String()) + return ErrUnsupportedConversion.Subject("deserialize to " + dstT.String()) } } @@ -250,12 +250,12 @@ func Convert(src reflect.Value, dst reflect.Value) E.Error { case srcT.Kind() == reflect.Map: obj, ok := src.Interface().(SerializedObject) if !ok { - return ErrUnsupportedConvertion.Subject(dstT.String() + " to " + srcT.String()) + return ErrUnsupportedConversion.Subject(dstT.String() + " to " + srcT.String()) } return Deserialize(obj, dst.Addr().Interface()) case srcT.Kind() == reflect.Slice: if dstT.Kind() != reflect.Slice { - return ErrUnsupportedConvertion.Subject(dstT.String() + " to slice") + return ErrUnsupportedConversion.Subject(dstT.String() + " to slice") } newSlice := reflect.MakeSlice(dstT, 0, src.Len()) i := 0 @@ -280,7 +280,7 @@ func Convert(src reflect.Value, dst reflect.Value) E.Error { var ok bool // check if (*T).Convertor is implemented if converter, ok = dst.Addr().Interface().(Converter); !ok { - return ErrUnsupportedConvertion.Subjectf("%s to %s", srcT, dstT) + return ErrUnsupportedConversion.Subjectf("%s to %s", srcT, dstT) } return converter.ConvertFrom(src.Interface()) @@ -310,6 +310,7 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr E.E } dst.Set(reflect.ValueOf(d)) return + default: } // primitive types / simple types switch dst.Kind() { @@ -392,7 +393,3 @@ func DeserializeJSON(j map[string]string, target any) error { } return json.Unmarshal(data, target) } - -func ToLowerNoSnake(s string) string { - return strings.ToLower(strings.ReplaceAll(s, "_", "")) -} diff --git a/internal/utils/strutils/format.go b/internal/utils/strutils/format.go index c5027fc..db4dea0 100644 --- a/internal/utils/strutils/format.go +++ b/internal/utils/strutils/format.go @@ -2,6 +2,7 @@ package strutils import ( "fmt" + "strconv" "strings" "time" @@ -57,6 +58,10 @@ func ParseBool(s string) bool { } } +func PortString(port uint16) string { + return strconv.FormatUint(uint64(port), 10) +} + func DoYouMean(s string) string { return "Did you mean " + ansi.HighlightGreen + s + ansi.Reset + "?" } diff --git a/internal/utils/strutils/string.go b/internal/utils/strutils/string.go index a2af3a0..c329f2f 100644 --- a/internal/utils/strutils/string.go +++ b/internal/utils/strutils/string.go @@ -2,7 +2,6 @@ package strutils import ( "net/url" - "strconv" "strings" "golang.org/x/text/cases" @@ -29,8 +28,8 @@ func ExtractPort(fullURL string) (int, error) { return Atoi(url.Port()) } -func PortString(port uint16) string { - return strconv.FormatUint(uint64(port), 10) +func ToLowerNoSnake(s string) string { + return strings.ToLower(strings.ReplaceAll(s, "_", "")) } func LevenshteinDistance(a, b string) int { @@ -60,7 +59,7 @@ func LevenshteinDistance(a, b string) int { cost = 1 } - v1[j+1] = min(v1[j]+1, v0[j+1]+1, v0[j]+cost) + v1[j+1] = min3(v1[j]+1, v0[j+1]+1, v0[j]+cost) } for j := 0; j <= len(b); j++ { @@ -71,7 +70,7 @@ func LevenshteinDistance(a, b string) int { return v1[len(b)] } -func min(a, b, c int) int { +func min3(a, b, c int) int { if a < b && a < c { return a } diff --git a/internal/watcher/health/monitor.go b/internal/watcher/health/monitor.go index 281ad84..9790755 100644 --- a/internal/watcher/health/monitor.go +++ b/internal/watcher/health/monitor.go @@ -37,7 +37,6 @@ func newMonitor(url types.URL, config *HealthCheckConfig, healthCheckFunc Health config: config, checkHealth: healthCheckFunc, startTime: time.Now(), - task: task.DummyTask(), } mon.url.Store(url) mon.status.Store(StatusHealthy)