Gitfed
bastien-mrq/sssh / internal / host / host.go
// Package host définit le modèle d'un host SSH enregistré.
package host

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

// Host représente une entrée SSH enregistrée.
type Host struct {
	Name     string   `toml:"-"`
	User     string   `toml:"user,omitempty"`
	Host     string   `toml:"host"`
	Port     int      `toml:"port,omitempty"`
	Identity string   `toml:"identity,omitempty"`
	Jump     string   `toml:"jump,omitempty"`
	Tags     []string `toml:"tags,omitempty"`
}

// Parse construit un Host depuis une cible de la forme [user@]host[:port].
func Parse(name, target string) (Host, error) {
	h := Host{Name: name}
	if i := strings.LastIndex(target, "@"); i >= 0 {
		h.User = target[:i]
		target = target[i+1:]
	}
	if i := strings.LastIndex(target, ":"); i >= 0 && !strings.Contains(target, "]") {
		port, err := strconv.Atoi(target[i+1:])
		if err != nil {
			return h, fmt.Errorf("port invalide dans %q", target)
		}
		h.Port = port
		target = target[:i]
	}
	if target == "" {
		return h, errors.New("hôte vide")
	}
	h.Host = target
	return h, nil
}

// Target rend la cible ssh, ex. "inoval@51.210.14.32".
func (h Host) Target() string {
	t := h.Host
	if h.User != "" {
		t = h.User + "@" + t
	}
	return t
}

// JumpSpec rend la forme [user@]host[:port] utilisable après -J.
func (h Host) JumpSpec() string {
	s := h.Target()
	if h.Port != 0 && h.Port != 22 {
		s += ":" + strconv.Itoa(h.Port)
	}
	return s
}

// SSHArgs construit les arguments à passer au binaire ssh.
func (h Host) SSHArgs(extra ...string) []string {
	var args []string
	if h.Port != 0 && h.Port != 22 {
		args = append(args, "-p", strconv.Itoa(h.Port))
	}
	if h.Identity != "" {
		args = append(args, "-i", ExpandTilde(h.Identity))
	}
	if h.Jump != "" {
		args = append(args, "-J", h.Jump)
	}
	args = append(args, h.Target())
	return append(args, extra...)
}

// Describe rend une description courte pour les listes (TUI, sssh list).
func (h Host) Describe() string {
	d := h.Target()
	if h.Port != 0 && h.Port != 22 {
		d += ":" + strconv.Itoa(h.Port)
	}
	if len(h.Tags) > 0 {
		d += "  #" + strings.Join(h.Tags, " #")
	}
	return d
}

// ExpandTilde remplace un préfixe ~/ par le home de l'utilisateur.
func ExpandTilde(p string) string {
	if p == "~" || strings.HasPrefix(p, "~/") {
		home, err := os.UserHomeDir()
		if err != nil {
			return p
		}
		return filepath.Join(home, strings.TrimPrefix(p[1:], "/"))
	}
	return p
}