diff --git a/pkg/formatters/duration.go b/pkg/formatters/duration.go new file mode 100644 index 0000000..e5bff50 --- /dev/null +++ b/pkg/formatters/duration.go @@ -0,0 +1,37 @@ +package formatters + +import ( + "fmt" + "strings" + "time" +) + +// FormatDuration takes a duration and returns a human-readable duration string. +// For example: 1928371 seconds -> "22d 7h 39m 31s" +func FormatDuration(d time.Duration) string { + if d == 0 { + return "N/A" + } + + var parts []string + + days := int(d.Hours()) / 24 + hours := int(d.Hours()) % 24 + minutes := int(d.Minutes()) % 60 + seconds := int(d.Seconds()) % 60 + + if days > 0 { + parts = append(parts, fmt.Sprintf("%dd", days)) + } + if hours > 0 { + parts = append(parts, fmt.Sprintf("%dh", hours)) + } + if minutes > 0 { + parts = append(parts, fmt.Sprintf("%dm", minutes)) + } + if seconds > 0 { + parts = append(parts, fmt.Sprintf("%ds", seconds)) + } + + return strings.Join(parts, " ") +} diff --git a/pkg/formatters/numbers.go b/pkg/formatters/numbers.go new file mode 100644 index 0000000..67b4d94 --- /dev/null +++ b/pkg/formatters/numbers.go @@ -0,0 +1,45 @@ +package formatters + +import ( + "fmt" + "math" +) + +// FormatNumber takes an int64 and returns a human-readable string. +// For example: 19823 -> "19.8k", 1500000 -> "1.5M" +func FormatNumber(input int64) string { + if input == 0 { + return "0" + } + + // Handle Negative + negative := input < 0 + if negative { + input = -input + } + + abbreviations := []string{"", "k", "M", "B", "T"} + abbrevIndex := int(math.Log10(float64(input)) / 3) + + // Bounds Check + if abbrevIndex >= len(abbreviations) { + abbrevIndex = len(abbreviations) - 1 + } + + scaledNumber := float64(input) / math.Pow(10, float64(abbrevIndex*3)) + + var result string + if scaledNumber >= 100 { + result = fmt.Sprintf("%.0f%s", scaledNumber, abbreviations[abbrevIndex]) + } else if scaledNumber >= 10 { + result = fmt.Sprintf("%.1f%s", scaledNumber, abbreviations[abbrevIndex]) + } else { + result = fmt.Sprintf("%.2f%s", scaledNumber, abbreviations[abbrevIndex]) + } + + if negative { + result = "-" + result + } + + return result +} diff --git a/pkg/ptr/ptr.go b/pkg/ptr/ptr.go new file mode 100644 index 0000000..98db77e --- /dev/null +++ b/pkg/ptr/ptr.go @@ -0,0 +1,13 @@ +package ptr + +func Of[T any](v T) *T { + return &v +} + +func Deref[T any](v *T) T { + var zeroT T + if v == nil { + return zeroT + } + return *v +} diff --git a/pkg/sliceutils/sliceutils.go b/pkg/sliceutils/sliceutils.go new file mode 100644 index 0000000..8a8eeb4 --- /dev/null +++ b/pkg/sliceutils/sliceutils.go @@ -0,0 +1,17 @@ +package sliceutils + +func First[T any](s []T) (T, bool) { + if len(s) == 0 { + var zeroT T + return zeroT, false + } + return s[0], true +} + +func Map[R, I any](s []I, f func(I) R) []R { + r := make([]R, 0, len(s)) + for _, v := range s { + r = append(r, f(v)) + } + return r +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go new file mode 100644 index 0000000..0a687b9 --- /dev/null +++ b/pkg/utils/utils.go @@ -0,0 +1,18 @@ +package utils + +func Ternary[T any](cond bool, tVal, fVal T) T { + if cond { + return tVal + } + return fVal +} + +func FirstNonZero[T comparable](v ...T) T { + var zero T + for _, val := range v { + if val != zero { + return val + } + } + return zero +}