kit

package module
v1.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 12, 2026 License: LGPL-3.0 Imports: 18 Imported by: 1

README

kit - auxilary utilities

  • auxilary functions: Go Reference
  • crypter subpackage: Go Reference
  • format subpackage: Go Reference
  • migrater subpackage: Go Reference
  • retry subpackage: Go Reference
  • template subpackage: Go Reference
  • workpool subpackage: Go Reference

This is a shared utils package, used by other etke.cc projects. Check godoc for the details

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrUnknown = errors.New("unknown error")

ErrUnknown is a sentinel error representing an unknown or unclassified error. It can be used as a fallback when no more specific error type is available.

Functions

func AnonymizeIP added in v1.7.0

func AnonymizeIP(ip string) string

AnonymizeIP returns an anonymized form of the given IP address for GDPR-compliant logging.

For IPv4 addresses, it replaces the last octet with 0 (e.g., 1.2.3.4 becomes 1.2.3.0). For IPv6 addresses, it replaces the last group with 0 (e.g., 2001:db8::1 becomes 2001:db8::0).

The function handles three cases:

  • Empty string: returns empty string unchanged.
  • Non-IP string (invalid format): returns the input string unchanged (not an error).
  • Valid IP (IPv4 or IPv6): returns the anonymized form with the last segment zeroed.

func Chunk

func Chunk[T any](items []T, chunkSize int) (chunks [][]T)

Chunk divides items into sub-slices of at most chunkSize elements each. The last chunk may be smaller than chunkSize if the total number of items is not evenly divisible. Note: chunkSize must be greater than 0; values <= 0 will cause an infinite loop. A nil or empty input slice returns a single empty chunk.

func Eq

func Eq(s1, s2 string) bool

Eq checks if 2 strings are equal in constant time.

This function uses constant-time comparison to prevent timing attacks. Unlike the == operator, which short-circuits and leaks timing information character by character, Eq compares both the length and content in constant time. This prevents attackers from inferring secret values (such as tokens, passwords, or HMACs) by measuring how long the comparison takes.

When comparing secrets, always use Eq instead of s1 == s2. For non-secret string comparisons where timing doesn't matter, == is fine.

func Hash

func Hash(str string) string

Hash computes the SHA-256 hash of a string and returns it as a lowercase hex-encoded string. The result is always 64 characters long. This is a one-way cryptographic hash, not encryption.

func IsNil added in v1.7.6

func IsNil(i any) bool

IsNil checks if the given value is nil, including interfaces holding nil pointers.

Go has a subtle pitfall with interfaces: a typed nil pointer wrapped in an interface{} is NOT equal to nil via a plain == check. For example:

var p *int = nil
var i any = p
i == nil          // false (interface{} holds a non-nil interface value)
IsNil(i)          // true (correctly identifies the nil pointer)

IsNil handles all nilable kinds: pointers, channels, functions, maps, slices, and unsafe pointers. It also traverses through pointer layers to detect nil at any depth. For non-nilable types (int, string, struct, etc.), it returns false.

Use this function when you need to check for nil in a way that accounts for interface{} wrapping and polymorphic types.

func IsValidIP added in v1.7.4

func IsValidIP(ipStr string) bool

IsValidIP reports whether ipStr is a valid public IPv4 or IPv6 address suitable for general use.

It parses ipStr as either IPv4 or IPv6 and explicitly rejects the following address categories:

  • Unspecified addresses (0.0.0.0 for IPv4, :: for IPv6)
  • Loopback addresses (127.x.x.x for IPv4, ::1 for IPv6)
  • Private addresses (RFC 1918 for IPv4: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16; RFC 4193 for IPv6: fc00::/7)
  • Multicast addresses (224.0.0.0/4 for IPv4, ff00::/8 for IPv6)
  • Link-local unicast addresses (169.254.0.0/16 for IPv4, fe80::/10 for IPv6)
  • Link-local multicast addresses

Returns false for empty strings or strings that are not valid IP addresses.

func MapFromSlice added in v1.7.2

func MapFromSlice[T cmp.Ordered](slice []T) map[T]bool

MapFromSlice creates a map from slice elements as keys, enabling O(1) membership testing. The map values are set to true, indicating the presence of the key. This is useful for converting a slice to a set for quick lookups.

Duplicate elements in the input are silently collapsed, with each unique element appearing once in the resulting map. An empty or nil slice returns an empty map (not nil).

func MapKeys

func MapKeys[T cmp.Ordered, V any](data map[T]V) []T

MapKeys returns the keys of the provided map as a sorted (ascending) slice. An empty or nil map returns an empty slice (not nil). This function is used internally by List.Slice().

func MergeMapKeys

func MergeMapKeys[V any](m map[string]V, adds ...map[string]V) []string

MergeMapKeys deduplicates keys across all provided maps and returns them as a sorted (ascending) slice. The first argument m is required; adds is variadic and may be empty. The return is always a non-nil slice, which will be empty if all provided maps are empty.

func MergeSlices

func MergeSlices[K cmp.Ordered](slices ...[]K) []K

MergeSlices concatenates any number of input slices, deduplicates the result, and returns the elements in sorted order. The order of input slices does not affect the output order, as the result is always sorted via MapKeys. Empty or nil input slices are silently ignored. The generic type K must be ordered (implements cmp.Ordered).

func RemoveFromSlice

func RemoveFromSlice[K comparable](base, toRemove []K) []K

RemoveFromSlice returns a new slice containing elements from base that do not appear in the toRemove slice, preserving the order of elements from base. The result is also deduplicated: if an element appears multiple times in base, it appears only once in the output. A nil base slice returns an empty slice.

func Reverse added in v1.3.0

func Reverse[T any](slice []T)

Reverse reverses the elements of slice in place without allocating new memory. For a nil or empty slice, this is a no-op.

func SliceToString

func SliceToString(slice []string, delimiter string, hook func(string) string) string

SliceToString joins a slice of strings into a single string using the provided delimiter. An optional hook function can be applied to transform each element before joining. Pass nil for the hook parameter to skip transformation. Returns an empty string if the slice is empty.

func StringToInt

func StringToInt(value string, optionalDefaultValue ...int) int

StringToInt converts a string to an integer. Leading and trailing whitespace is trimmed before parsing. Returns the provided default value (or 0 if not provided) on empty string or parse failure. Accepts an optional variadic parameter for the default value; if multiple defaults are provided, only the first is used.

func StringToSlice

func StringToSlice(value string, optionalDefaultValue ...string) []string

StringToSlice converts a comma-separated string into a slice of strings. Each element is whitespace-trimmed. Accepts an optional variadic parameter for a default value. An empty input (after trimming) returns []string{defaultValue}. A non-empty input with no comma returns []string{value} — the value itself, not the default. A comma-separated input is split and each element is trimmed individually.

func Truncate

func Truncate(s string, length int) string

Truncate truncates a string to a specified length measured in Unicode code points (runes), appending "..." if truncation occurs. Length is measured in runes, not bytes. Returns empty string if length <= 0 or s is empty. The ellipsis is appended only when actual truncation occurs; if the string is already shorter than length, it is returned unchanged. Example: Truncate("hello world", 5) returns "hello...".

func Uniq

func Uniq(slice []string) []string

Uniq removes duplicates from the input slice while preserving insertion order. The first occurrence of each element is kept, and subsequent duplicates are discarded. Unlike MergeSlices, the result maintains the order of first appearance rather than being sorted. A nil or empty input slice returns an empty non-nil slice.

func Unquote

func Unquote(s string) string

Unquote handles Go-style quoted strings by wrapping strconv.Unquote. It decodes escape sequences in quoted strings (e.g., "hello\nworld" becomes hello with a newline). Returns the original string unchanged if the input is not properly quoted or if any escape sequence is invalid. Never returns an error; always returns a string.

Types

type AggregateError added in v1.7.7

type AggregateError struct {
	Errors []error
	// contains filtered or unexported fields
}

AggregateError is a thread-safe container for multiple errors that occurred together. It is commonly used to collect errors from concurrent operations or validation checks and report them all at once. The zero value is ready to use.

AggregateError is safe for concurrent use; all methods are protected by an internal mutex. The Errors field holds the underlying error slice and can be read via Unwrap, but callers must not modify it directly.

func NewAggregateError added in v1.7.7

func NewAggregateError(errs ...error) *AggregateError

NewAggregateError creates a new AggregateError and adds the provided errors to it. Nil errors are silently skipped. It returns nil if no non-nil errors were provided, allowing idiomatically correct error handling:

if err := NewAggregateError(e1, e2); err != nil { ... }

func (*AggregateError) As added in v1.7.7

func (a *AggregateError) As(target any) bool

As reports whether the aggregate or any of its underlying errors matches the target type by recursively checking each error with errors.As. This allows type assertions on wrapped errors to work correctly across the aggregate.

func (*AggregateError) Error added in v1.7.7

func (a *AggregateError) Error() string

Error returns the aggregated error messages as a single string by joining all errors with a semicolon and space separator. It returns an empty string if no errors are present. This method implements the error interface.

func (*AggregateError) Is added in v1.7.7

func (a *AggregateError) Is(target error) bool

Is reports whether the target error matches any error in the aggregate by recursively checking each error with errors.Is. This allows callers to check for specific error types within the aggregate using standard error chain semantics.

func (*AggregateError) Join added in v1.7.7

func (a *AggregateError) Join(errs ...error) *AggregateError

Join adds non-nil errors to the aggregate. Nil errors are silently skipped. It returns the receiver if any errors are present after filtering, or nil if the aggregate is empty after the operation. This allows callers to idiomatically chain:

if err := agg.Join(e1, e2, e3); err != nil { ... }

The nil return when the aggregate is still empty is intentional—it prevents accidental propagation of empty aggregates.

func (*AggregateError) Unwrap added in v1.7.7

func (a *AggregateError) Unwrap() []error

Unwrap returns the underlying error slice. The returned slice is the original, not a copy; callers must not modify it. Returns nil if the aggregate contains no errors. This method is used by the errors package to unwrap error chains.

type ErrorResponse

type ErrorResponse struct {
	StatusCode int    `json:"-"`     // HTTP status code, optional, not serialized
	Err        string `json:"error"` // Error message
	// contains filtered or unexported fields
}

ErrorResponse represents a structured HTTP error response. It combines an HTTP status code with machine-serializable and human-readable error details.

The Err field is the JSON-serialized error message (key "error" in JSON output). StatusCode defaults to http.StatusBadRequest (400) if not explicitly set and is never serialized to JSON. The underlying error is preserved for errors.Is/As chain semantics via the Unwrap method but is not exposed in JSON output.

ErrorResponse implements the error interface and the errors.Wrapper interface.

func NewErrorResponse

func NewErrorResponse(err error, optionalStatusCode ...int) *ErrorResponse

NewErrorResponse creates a new ErrorResponse from an error. The StatusCode defaults to http.StatusBadRequest (400); an optional status code may be provided as a variadic argument and is used if positive (> 0), otherwise the default is applied. If err is nil, Err is set to "unknown error". The underlying error is preserved for chain inspection but is not serialized to JSON.

func (ErrorResponse) Error

func (e ErrorResponse) Error() string

Error returns the error message string, implementing the error interface.

func (ErrorResponse) Unwrap added in v1.7.7

func (e ErrorResponse) Unwrap() error

Unwrap returns the underlying error, if any. This enables ErrorResponse to participate in error chains inspected by errors.Is and errors.As.

type List

type List[T cmp.Ordered, V any] struct {
	// contains filtered or unexported fields
}

List is a concurrency-safe ordered unique set that stores elements of type T without duplicates. T must be cmp.Ordered so elements can be sorted; V is a type parameter used only to satisfy the AddMapKeys method signature (which accepts map[T]V). The List is safe for concurrent use across all methods. The zero value is not usable — always construct a List with NewList or NewListFrom.

func NewList

func NewList[T cmp.Ordered, V any]() *List[T, V]

NewList creates and returns a new empty List. Both type parameters T and V must be explicitly provided. Use NewListFrom when starting a List from an existing slice.

func NewListFrom

func NewListFrom[T cmp.Ordered](slice []T) *List[T, T]

NewListFrom creates a new List and populates it from the provided slice. The V type parameter is fixed to T (since no map is involved). The returned List owns a deduplicated copy of the slice contents; duplicate elements in the input slice are included only once in the List.

func (*List[T, V]) Add

func (l *List[T, V]) Add(item T)

Add inserts an item into the List. If the item already exists, Add is a no-op and the item is not duplicated.

func (*List[T, V]) AddMapKeys

func (l *List[T, V]) AddMapKeys(datamap map[T]V)

AddMapKeys adds all keys from the provided map to the List. Duplicate keys are ignored due to the List's uniqueness guarantee.

func (*List[T, V]) AddSlice

func (l *List[T, V]) AddSlice(dataslice []T)

AddSlice adds all elements from the provided slice to the List. Duplicate elements are ignored due to the List's uniqueness guarantee.

func (*List[T, V]) Len

func (l *List[T, V]) Len() int

Len returns the number of items in the List. Note: this method does not acquire the mutex — it reads the map length directly. While Go map length reads are atomic for reading purposes, the returned value may be stale if called concurrently with Add or Remove operations.

func (*List[T, V]) Remove

func (l *List[T, V]) Remove(item T)

Remove deletes an item from the List. If the item does not exist, Remove is a no-op.

func (*List[T, V]) RemoveSlice

func (l *List[T, V]) RemoveSlice(dataslice []T)

RemoveSlice removes all elements from the provided slice from the List. If an element does not exist, it is ignored.

func (*List[T, V]) Slice

func (l *List[T, V]) Slice() []T

Slice returns the contents of the List as a sorted slice in ascending order. The returned slice is a fresh allocation each time and is not a view into internal state. The sorting is provided by the underlying MapKeys function.

type MatrixError

type MatrixError struct {
	Code string `json:"errcode"`
	Err  string `json:"error"`
}

MatrixError represents an error response from the Matrix Client-Server API. It follows the Matrix specification error format with two fields: Code is the machine-readable error code (e.g., "M_FORBIDDEN", "M_UNKNOWN") and Err is the human-readable error message.

MatrixError implements the error interface.

func MatrixErrorFrom

func MatrixErrorFrom(r io.Reader) *MatrixError

MatrixErrorFrom decodes a MatrixError from an io.Reader containing JSON data. It returns nil if r is nil. If JSON decoding fails, it returns a MatrixError with code "M_UNKNOWN" and a descriptive error message that includes the raw body, allowing debugging of malformed responses.

func NewMatrixError

func NewMatrixError(code, err string) *MatrixError

NewMatrixError creates a new MatrixError with the given error code and message. The code should be a standard Matrix error code like "M_FORBIDDEN" or "M_UNKNOWN".

func (MatrixError) Error

func (e MatrixError) Error() string

Error returns the human-readable error message, implementing the error interface.

type Mutex added in v1.4.0

type Mutex struct {
	// contains filtered or unexported fields
}

Mutex is a key-based mutex that allows independent locking of different keys. Different keys can be locked simultaneously without blocking each other — only goroutines contending on the exact same key block.

Memory management

Per-key entries are reference-counted and removed automatically: when the last goroutine holding or waiting for a key releases it, the entry is deleted from the internal map. The map therefore stays bounded to the number of keys currently in active use rather than accumulating every key ever seen.

Thread safety

A single Mutex may be used concurrently from any number of goroutines without external synchronization.

The zero value is not usable; always construct a Mutex using NewMutex.

func NewMutex added in v1.4.0

func NewMutex() *Mutex

NewMutex creates and returns a new, initialized Mutex ready for use.

func (*Mutex) Lock added in v1.4.0

func (km *Mutex) Lock(key string)

Lock locks the mutex for the specified key, blocking until the lock is acquired. Multiple goroutines may hold locks on different keys simultaneously.

The operation proceeds in two steps:

  1. The global lock is acquired, the per-key entry is created if absent, and its reference count is incremented. The global lock is then released. Incrementing refcount before releasing the global lock ensures a concurrent Unlock cannot delete the entry while this goroutine is still waiting for it.
  2. The per-key mutex is locked (potentially blocking until the current holder calls Unlock).

func (*Mutex) Unlock added in v1.4.0

func (km *Mutex) Unlock(key string)

Unlock unlocks the mutex for the specified key.

It is a no-op if the key has no entry in the internal map (i.e. it was never locked or its entry was already cleaned up). Calling Unlock on a key that is locked but whose per-key mutex is not held by the caller will panic, consistent with the behavior of sync.Mutex.

The operation proceeds in two steps:

  1. The global lock is acquired, the reference count is decremented, and the entry is deleted from the map if the count reaches zero. A count of zero guarantees no other goroutine is waiting — because Lock increments refcount before waiting — so removing the entry is safe. The global lock is then released.
  2. The per-key mutex is unlocked. This happens after the entry may have been removed from the map; that is safe because the local variable m still holds a reference to the keyMutex and no new goroutine can acquire the same pointer.

type StringsBuilder added in v1.7.5

type StringsBuilder struct {
	// contains filtered or unexported fields
}

StringsBuilder is a wrapper around strings.Builder that provides fluent chaining through shorthand methods. The shorthand methods S, B, and R return *StringsBuilder for method chaining, enabling fluent construction like sb.S("hello").B(' ').S("world").String(). It also implements io.Writer (via the Write method) and io.StringWriter/io.RuneWriter interfaces, making it fully compatible with code expecting those interfaces. All methods of strings.Builder are exposed, so StringsBuilder serves as a drop-in replacement. The zero value is usable without initialization (strings.Builder's zero value is valid), but NewStringsBuilder is provided for clarity.

func NewStringsBuilder added in v1.7.5

func NewStringsBuilder() StringsBuilder

NewStringsBuilder creates a new StringsBuilder instance. Although the zero value of StringsBuilder is usable, this function is provided for code clarity and consistency.

func (*StringsBuilder) B added in v1.7.5

B is a shorthand for WriteByte that appends the byte b to sb's buffer and returns the receiver to enable method chaining. It allows fluent construction like sb.B('x').B('y').

func (*StringsBuilder) Cap added in v1.7.5

func (sb *StringsBuilder) Cap() int

Cap returns the current capacity of the accumulated string buffer.

func (*StringsBuilder) Grow added in v1.7.5

func (sb *StringsBuilder) Grow(n int) *StringsBuilder

Grow grows sb's capacity, if necessary, to guarantee space for another n bytes and returns the receiver to enable method chaining. Unlike strings.Builder.Grow which returns nothing, this method enables fluent construction like sb.Grow(10).S("hello").

func (*StringsBuilder) Len added in v1.7.5

func (sb *StringsBuilder) Len() int

Len returns the current length of the accumulated string.

func (*StringsBuilder) R added in v1.7.5

R is a shorthand for WriteRune that appends the rune r to sb's buffer and returns the receiver to enable method chaining. It allows fluent construction like sb.R('α').R('β').

func (*StringsBuilder) Reset added in v1.7.5

func (sb *StringsBuilder) Reset() *StringsBuilder

Reset resets the StringsBuilder to be empty and returns the receiver to enable method chaining. Unlike strings.Builder.Reset which returns nothing, this method enables fluent construction like sb.Reset().S("new content").

func (*StringsBuilder) S added in v1.7.5

S is a shorthand for WriteString that appends the string s to sb's buffer and returns the receiver to enable method chaining. It allows fluent construction like sb.S("hello").S(" ").S("world").

func (*StringsBuilder) String added in v1.7.5

func (sb *StringsBuilder) String() string

String returns the accumulated string built by this StringsBuilder.

func (*StringsBuilder) Unwrap added in v1.7.5

func (sb *StringsBuilder) Unwrap() strings.Builder

Unwrap returns a copy of the underlying strings.Builder (as a value, not a pointer). Mutations to the returned builder do not affect the StringsBuilder, as it is a copy.

func (*StringsBuilder) Write added in v1.7.5

func (sb *StringsBuilder) Write(p []byte) (int, error)

Write appends the contents of p to sb's buffer and satisfies the io.Writer interface. It returns the number of bytes written and an error; the error is always nil as strings.Builder never errors.

func (*StringsBuilder) WriteRune added in v1.7.5

func (sb *StringsBuilder) WriteRune(r rune) (int, error)

WriteRune appends the UTF-8 encoding of the Unicode code point r to sb's buffer and satisfies the io.RuneWriter interface. It returns the number of bytes written and an error; the error is always nil as strings.Builder never errors.

func (*StringsBuilder) WriteString added in v1.7.5

func (sb *StringsBuilder) WriteString(s string) (int, error)

WriteString appends the contents of s to sb's buffer and satisfies the io.StringWriter interface. It returns the number of bytes written and an error; the error is always nil as strings.Builder never errors.

type WaitGroup added in v1.7.3

type WaitGroup struct {
	// contains filtered or unexported fields
}

WaitGroup is a wrapper around sync.WaitGroup that provides ergonomic improvements for concurrent goroutine management.

Unlike sync.WaitGroup, WaitGroup's Do method accepts functions directly and automatically manages Add and Done calls, eliminating boilerplate. However, Do does not block until goroutines complete — the caller must explicitly call Wait after launching goroutines.

The zero value is NOT usable; always construct with NewWaitGroup.

Example:

wg := kit.NewWaitGroup()
wg.Do(f1, f2, f3)  // launches goroutines but does not block
wg.Wait()          // blocks until all goroutines complete

func NewWaitGroup added in v1.7.3

func NewWaitGroup() *WaitGroup

NewWaitGroup constructs a new WaitGroup ready for use.

func (*WaitGroup) Do added in v1.7.3

func (w *WaitGroup) Do(f ...func())

Do launches the given functions concurrently in separate goroutines and returns immediately without blocking.

Each function f is wrapped with automatic Add and Done calls, so the caller need not manage the sync.WaitGroup counter manually. Passing zero functions is a no-op.

Do may be called multiple times before Wait; each call adds to the same counter, so Wait will not return until all goroutines launched across all Do calls have completed.

The caller must explicitly call Wait() to block until all goroutines finish.

func (*WaitGroup) Get added in v1.7.3

func (w *WaitGroup) Get() *sync.WaitGroup

Get returns the underlying *sync.WaitGroup.

This allows callers to perform advanced operations directly on the underlying WaitGroup, such as calling TryWait, or to pass the pointer to other code that expects *sync.WaitGroup for interoperability with the standard library.

func (*WaitGroup) Wait added in v1.7.3

func (w *WaitGroup) Wait()

Wait blocks until all goroutines launched via Do have returned.

If Do has not been called, Wait returns immediately.

Directories

Path Synopsis
Package crypter provides AES-GCM authenticated encryption and decryption for string values.
Package crypter provides AES-GCM authenticated encryption and decryption for string values.
format module
Package migrater applies numbered SQL migration files to a database/sql.DB.
Package migrater applies numbered SQL migration files to a database/sql.DB.
Package retry provides a simple retry mechanism for Go with exponential backoff and jitter.
Package retry provides a simple retry mechanism for Go with exponential backoff and jitter.
Package template provides a handy wrapper around the html/template package.
Package template provides a handy wrapper around the html/template package.
Package workpool provides a bounded goroutine pool for running tasks concurrently with a fixed number of workers.
Package workpool provides a bounded goroutine pool for running tasks concurrently with a fixed number of workers.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL