rs2322tcp/internal/client/virtual_port_validation.go

252 lines
5.8 KiB
Go

/*
* ============================================================================
* Projekt.....: rs2322tcp
* Datei.......: internal/client/virtual_port_validation.go
* Copyright (C) 2026 Dieter Lang
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Beschreibung:
* Plattformneutrale Prüfung der in client.json definierten virtuellen Ports
* gegen die tatsächlich vorhandenen virtuellen Schnittstellen.
*
* Die Ermittlung der vorhandenen Ports erfolgt über die
* plattformabhängige Funktion existingVirtualPortLinks().
* ============================================================================
*/
package client
import (
"fmt"
"sort"
"strings"
"git.lang-dieter.de/rs2322tcp/internal/config"
)
///////////////////////////////////////////////////////////////////////////////
// Virtual port validation
///////////////////////////////////////////////////////////////////////////////
// VirtualPortValidationResult contains the differences between configured
// and locally available virtual ports.
type VirtualPortValidationResult struct {
Missing []string
Extra []string
}
// compareVirtualPorts compares the configured virtual ports with the
// locally available virtual ports.
//
// Missing contains ports configured in client.json that are not available
// locally.
//
// Extra contains locally available ports that are not configured in
// client.json.
//
// The order of both input lists is irrelevant.
func compareVirtualPorts(
configured []string,
available []string,
) VirtualPortValidationResult {
configuredSet := make(map[string]struct{}, len(configured))
for _, port := range configured {
configuredSet[port] = struct{}{}
}
availableSet := make(map[string]struct{}, len(available))
for _, port := range available {
availableSet[port] = struct{}{}
}
result := VirtualPortValidationResult{
Missing: make([]string, 0),
Extra: make([]string, 0),
}
for port := range configuredSet {
if _, ok := availableSet[port]; ok {
continue
}
result.Missing = append(result.Missing, port)
}
for port := range availableSet {
if _, ok := configuredSet[port]; ok {
continue
}
result.Extra = append(result.Extra, port)
}
sort.Strings(result.Missing)
sort.Strings(result.Extra)
return result
}
///////////////////////////////////////////////////////////////////////////////
// Local virtual-port validation
///////////////////////////////////////////////////////////////////////////////
// validateLocalVirtualPorts compares the virtual ports defined in
// client.json with the virtual ports currently available on the local
// computer.
//
// The detection of available ports is performed by the platform-specific
// existingVirtualPortLinks() implementation.
//
// A mismatch prevents the client from starting.
func validateLocalVirtualPorts(
cfg *config.ClientConfig,
) error {
if cfg == nil {
return fmt.Errorf("client configuration is nil")
}
configured := make(
[]string,
0,
len(cfg.VirtualPorts),
)
for _, virtualPort := range cfg.VirtualPorts {
configured = append(
configured,
virtualPort.Port,
)
}
available, err := existingVirtualPortLinks(
cfg.VirtualPortRange,
)
if err != nil {
return fmt.Errorf(
"prüfen der virtuellen Ports fehlgeschlagen: %w",
err,
)
}
result := compareVirtualPorts(
configured,
available,
)
if len(result.Missing) == 0 &&
len(result.Extra) == 0 {
return nil
}
var message strings.Builder
////////////////////////////////////////////////////////////////////////////
// Missing ports
////////////////////////////////////////////////////////////////////////////
if len(result.Missing) > 0 &&
len(result.Extra) == 0 {
message.WriteString(
"Virtuelle Ports nicht eingerichtet",
)
message.WriteString(
"\n\nFolgende Ports sind in client.json " +
"eingetragen, aber auf diesem Rechner " +
"nicht eingerichtet:\n\n",
)
for _, port := range result.Missing {
message.WriteString(" ")
message.WriteString(port)
message.WriteByte('\n')
}
message.WriteString(
"\nBitte richten Sie diese virtuellen Ports " +
"entsprechend der Installationsanleitung ein.",
)
return fmt.Errorf(
"%s",
strings.TrimSpace(message.String()),
)
}
////////////////////////////////////////////////////////////////////////////
// Extra ports
////////////////////////////////////////////////////////////////////////////
if len(result.Missing) == 0 &&
len(result.Extra) > 0 {
message.WriteString(
"Virtuelle Ports nicht konfiguriert",
)
message.WriteString(
"\n\nFolgende virtuelle Ports sind auf diesem " +
"Rechner eingerichtet, aber nicht in " +
"client.json eingetragen:\n\n",
)
for _, port := range result.Extra {
message.WriteString(" ")
message.WriteString(port)
message.WriteByte('\n')
}
message.WriteString(
"\nBitte ergänzen Sie die entsprechenden " +
"Einträge in client.json.",
)
return fmt.Errorf(
"%s",
strings.TrimSpace(message.String()),
)
}
////////////////////////////////////////////////////////////////////////////
// Missing and extra ports
////////////////////////////////////////////////////////////////////////////
message.WriteString(
"Virtuelle Portkonfiguration stimmt nicht überein",
)
message.WriteString(
"\n\nIn client.json eingetragen, aber nicht " +
"eingerichtet:\n\n",
)
for _, port := range result.Missing {
message.WriteString(" ")
message.WriteString(port)
message.WriteByte('\n')
}
message.WriteString(
"\nEingerichtet, aber nicht in client.json " +
"eingetragen:\n\n",
)
for _, port := range result.Extra {
message.WriteString(" ")
message.WriteString(port)
message.WriteByte('\n')
}
message.WriteString(
"\nBitte korrigieren Sie die virtuelle " +
"Portkonfiguration.",
)
return fmt.Errorf(
"%s",
strings.TrimSpace(message.String()),
)
}