2025-03-02 08:51:13 +01:00

171 lines
3.6 KiB
Go

package internal
import (
"context"
"fmt"
"io"
"log/slog"
"os"
"os/signal"
"strings"
"syscall"
)
// LogClose closes the given Closer and logs any error that occurs
func LogClose(c io.Closer) {
if err := c.Close(); err != nil {
slog.Error("error during Close()", "error", err)
}
}
// LogError logs the given error if it is not nil.
// If a message is given, it is prepended to the error message.
// Only the first message is used.
func LogError(err error, msg ...string) {
if err == nil {
return
}
if len(msg) > 0 {
slog.Error(msg[0], "error", err)
} else {
slog.Error(err.Error())
}
}
// SignalAwareContext returns a context that gets closed once a given signal is retrieved.
// By default, the following signals are handled: syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP
func SignalAwareContext(ctx context.Context, sig ...os.Signal) context.Context {
c := make(chan os.Signal, 1)
if len(sig) == 0 {
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
} else {
signal.Notify(c, sig...)
}
signalCtx, cancel := context.WithCancel(ctx)
// Attach signal handlers to context
go func() {
select {
case <-ctx.Done():
// normal shutdown, quit go routine
case <-c:
cancel() // cancel the context
}
// cleanup
signal.Stop(c)
close(c)
}()
return signalCtx
}
// AssertNoError panics if the given error is not nil.
func AssertNoError(err error) {
if err != nil {
panic(err)
}
}
// MapDefaultString returns the string value for the given key or a default value
func MapDefaultString(m map[string]any, key string, dflt string) string {
if m == nil {
return dflt
}
if tmp, ok := m[key]; !ok {
return dflt
} else {
switch v := tmp.(type) {
case string:
return v
case nil:
return dflt
default:
return fmt.Sprintf("%v", v)
}
}
}
// MapDefaultStringSlice returns the string slice value for the given key or a default value
func MapDefaultStringSlice(m map[string]any, key string, dflt []string) []string {
if m == nil {
return dflt
}
if tmp, ok := m[key]; !ok {
return dflt
} else {
switch v := tmp.(type) {
case []any:
result := make([]string, 0, len(v))
for _, elem := range v {
switch vElem := elem.(type) {
case string:
result = append(result, vElem)
default:
result = append(result, fmt.Sprintf("%v", vElem))
}
}
return result
case []string:
return v
case string:
return []string{v}
case nil:
return dflt
default:
return []string{fmt.Sprintf("%v", v)}
}
}
}
// UniqueStringSlice removes duplicates in the given string slice
func UniqueStringSlice(slice []string) []string {
keys := make(map[string]struct{})
uniqueSlice := make([]string, 0, len(slice))
for _, entry := range slice {
if _, exists := keys[entry]; !exists {
keys[entry] = struct{}{}
uniqueSlice = append(uniqueSlice, entry)
}
}
return uniqueSlice
}
// SliceString returns a string slice from a comma-separated string
func SliceString(str string) []string {
strParts := strings.Split(str, ",")
stringSlice := make([]string, 0, len(strParts))
for _, s := range strParts {
trimmed := strings.TrimSpace(s)
if trimmed != "" {
stringSlice = append(stringSlice, trimmed)
}
}
return stringSlice
}
// SliceToString returns a comma-separated string from a string slice
func SliceToString(slice []string) string {
return strings.Join(slice, ",")
}
// TruncateString returns a string truncated to the given length
func TruncateString(s string, max int) string {
if max > len(s) {
return s
}
return s[:max]
}
// BoolToFloat64 converts a boolean to a float64. True is 1.0, false is 0.0
func BoolToFloat64(b bool) float64 {
if b {
return 1.0
}
return 0.0
}