internal/utils/utils.go

package utils

import (
	"encoding/binary"
	"fmt"
	"log/slog"
	"net"
	"strconv"
	"strings"
)

// SanitizePort removes the port description from the port string
// and returns the port number as an uint16.
func SanitizePort(port string, logger *slog.Logger) uint16 {
	if port == "" {
		logger.Error("port is empty")
		return 0
	}

	p, err := strconv.Atoi(strings.Split(port, "(")[0])
	if err != nil {
		logger.Error("failed to convert port to integer", "error", err)
		return 0
	}

	if p < 1 || p > 65535 {
		logger.Error("port is out of range", "port", port)
		return 0
	}

	return uint16(p)
}

// SplitAddr splits a network address into host and sanitized port.
func SplitAddr(addr string, logger *slog.Logger) (string, uint16) {
	host, port, err := net.SplitHostPort(addr)
	if err != nil {
		logger.Error("failed to split host and port", "error", err)
		return "", 0
	}
	return host, SanitizePort(port, logger)
}

// BuildAddress builds a network address from listen address and port.
func BuildAddress(listenAddr string, port uint16) string {
	return net.JoinHostPort(listenAddr, strconv.Itoa(int(port)))
}

func IntToIP(ip uint32) string {
	return fmt.Sprintf("%d.%d.%d.%d",
		(ip>>24)&0xff,
		(ip>>16)&0xff,
		(ip>>8)&0xff,
		ip&0xff,
	)
}

func IPToInt(ip string) (uint32, error) {
	parsed := net.ParseIP(ip)
	if parsed == nil {
		return 0, fmt.Errorf("invalid IP address: %s", ip)
	}
	ipv4 := parsed.To4()
	if ipv4 == nil {
		return 0, fmt.Errorf("not an IPv4 address: %s", ip)
	}
	return binary.BigEndian.Uint32(ipv4), nil
}