Files
ssh-netbox-wrapper/internal/hook/completion.go
T
2026-10-27 19:19:42 +01:00

117 lines
3.3 KiB
Go

package hook
import (
"fmt"
"os"
"path/filepath"
"strings"
"git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/config"
"git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/storage"
)
const completionMarker = "# netssh command alias completion — generated automatically"
// SyncCommandCompletion installs lazy loaders for the configured command alias.
// The loaders obtain the canonical completion from the current netssh binary,
// so aliases configured by the wizard work without a separate completion install.
func SyncCommandCompletion(alias string) error {
if err := config.ValidateCommandAlias(alias); err != nil {
return err
}
statePath := filepath.Join(filepath.Dir(AliasesPath()), "completion_alias")
data, err := os.ReadFile(statePath)
if err != nil && !os.IsNotExist(err) {
return err
}
previous := strings.TrimSpace(string(data))
if err := config.ValidateCommandAlias(previous); err != nil {
return fmt.Errorf("invalid completion alias state: %w", err)
}
if alias == "" && previous == "" {
return nil
}
for _, shell := range []string{"bash", "zsh", "fish"} {
if previous != "" && previous != alias {
path := commandCompletionPath(shell, previous)
data, err := os.ReadFile(path)
if err != nil && !os.IsNotExist(err) {
return err
}
// Only delete files owned by netssh; preserve any user replacements.
if strings.HasPrefix(string(data), completionMarker+"\n") || strings.HasPrefix(string(data), "#compdef "+previous+"\n"+completionMarker+"\n") {
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
return err
}
}
}
if alias == "" {
continue
}
if shell == "fish" {
if _, err := os.Stat(FishConfigDir()); os.IsNotExist(err) {
continue
} else if err != nil {
return err
}
}
path := commandCompletionPath(shell, alias)
script := commandCompletionScript(shell, alias)
existing, err := os.ReadFile(path)
if err != nil && !os.IsNotExist(err) {
return err
}
if string(existing) == script {
continue
}
if err := storage.WriteFile(path, []byte(script), 0o644); err != nil {
return fmt.Errorf("writing %s alias completion: %w", shell, err)
}
}
if alias == "" {
if err := os.Remove(statePath); err != nil && !os.IsNotExist(err) {
return err
}
return nil
}
return storage.WriteFile(statePath, []byte(alias+"\n"), 0o600)
}
func commandCompletionPath(shell, alias string) string {
switch shell {
case "bash":
return filepath.Join(os.Getenv("HOME"), ".local", "share", "bash-completion", "completions", alias)
case "zsh":
return filepath.Join(os.Getenv("HOME"), ".zfunc", "_"+alias)
default:
return filepath.Join(FishConfigDir(), "completions", alias+".fish")
}
}
func commandCompletionScript(shell, alias string) string {
switch shell {
case "bash":
return fmt.Sprintf(`%s
source <(netssh completion bash)
if [[ $(type -t compopt) == builtin ]]; then
complete -o default -F __start_netssh %s
else
complete -o default -o nospace -F __start_netssh %s
fi
`, completionMarker, alias, alias)
case "zsh":
return fmt.Sprintf(`#compdef %s
%s
source <(netssh completion zsh)
compdef _netssh %s
_netssh "$@"
`, alias, completionMarker, alias)
default:
return fmt.Sprintf(`%s
netssh completion fish | source
complete --command %s --wraps netssh
`, completionMarker, alias)
}
}