Files
ssh-netbox-wrapper/cmd/netssh/instances.go
Sebastian UnterschützandClaude Opus 5.5 091f5cc518 fix(completion): offer unique hostnames without instance prefix
bash (and zsh without a matcher) only accept candidates that start with
the typed text, so "fsn1-bx1<Tab>" found nothing when every candidate
was qualified as "prod/…". Hostnames that exist in only one instance
are now offered bare; names present in several instances stay
qualified. Connecting to a bare name still checks every instance for
ambiguity, so the connection behavior is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 11:44:54 +01:00

70 lines
2.2 KiB
Go

package main
import (
"sort"
"strings"
"git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/cache"
"git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/config"
"git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/netbox"
"git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/shortcuts"
)
func newNetBoxClient(cfg *config.Config) *netbox.Client {
if len(cfg.NetBoxes) > 0 {
sources := make([]netbox.Source, 0, len(cfg.NetBoxes))
for _, source := range cfg.NetBoxes {
sources = append(sources, netbox.Source{Name: source.Name, URL: source.URL, Token: source.Token, TokenVersion: source.TokenVersion})
}
return netbox.NewMultiClient(sources)
}
if cfg.NetBox.URL == "" {
return nil
}
return netbox.NewClient(cfg.NetBox.URL, cfg.NetBox.Token, cfg.NetBox.TokenVersion)
}
func normalizeTarget(cfg config.ShortcutsConfig, qualified bool) func(string) string {
return func(target string) string {
source, name := netbox.SplitTarget(target)
name = shortcuts.Normalize(name, cfg)
if qualified && source != "" {
return source + "/" + name
}
return name
}
}
// hostCompletions returns the cached hosts matching toComplete in a form that
// starts with the typed text, because bash and fish only accept such
// candidates. Without an instance prefix, a hostname that is unique across
// all instances is offered bare ("web01"), so it completes like with a single
// NetBox; connecting to it still checks every instance for ambiguity.
// Names present in several instances stay qualified ("prod/web01").
func hostCompletions(entries []cache.Entry, toComplete string) []string {
prefix := strings.ToLower(toComplete)
qualifiedInput := strings.Contains(prefix, "/")
instances := make(map[string]int)
for _, e := range entries {
_, name := netbox.SplitTarget(e.Name)
instances[strings.ToLower(name)]++
}
var out []string
for _, e := range entries {
source, name := netbox.SplitTarget(e.Name)
target := strings.ToLower(e.Name)
switch {
case source != "" && !qualifiedInput && strings.HasPrefix(strings.ToLower(name), prefix):
if instances[strings.ToLower(name)] == 1 {
out = append(out, name)
} else {
out = append(out, e.Name)
}
case strings.HasPrefix(target, prefix):
out = append(out, e.Name)
}
}
sort.Strings(out)
return out
}