abstract
Abstractions and utilities to get rid of boilerplate code in business logic
About abstract
This large README is shown as a plain-text preview. Read the full README at the source
# abstract
[![Go Version][version-img]][doc] [![GoDoc][doc-img]][doc] [![Build][ci-img]][ci] [![Coverage][coverage-img]][coverage] [![GoReport][report-img]][report]
**A comprehensive Go library providing generic data structures, cryptographic utilities, concurrency helpers, and powerful abstractions to accelerate development and reduce boilerplate.**
```bash
go get -u github.com/maxbolgarin/abstract
```
## 🚀 Overview
The `abstract` package provides a comprehensive collection of utilities and data structures designed to simplify common programming tasks in Go. It leverages Go's generics to provide type-safe, efficient implementations that work across different types while maintaining excellent performance.
### 🎯 Key Features
- **🗂️ Generic Data Structures**: Maps, Sets, Stacks, Slices, LinkedLists with thread-safe variants
- **🔐 Cryptographic Utilities**: AES encryption, HMAC generation, ECDSA signing/verification
- **⚡ Concurrency Tools**: Futures/Promises, Worker pools, Rate limiting, Concurrent helpers
- **🎲 Random Generation**: Secure random strings, numbers, choices with customizable alphabets
- **📊 CSV Processing**: Advanced table manipulation and data transformation
- **📏 Mathematical Utilities**: Generic math functions with type-safe constraints
- **🆔 ID Generation**: Structured entity ID creation with type safety
- **⏱️ Timing Utilities**: Precise timing, lap timing, deadline management
- **🔧 Type Constraints**: Comprehensive type constraints for generic programming
### ✅ Advantages
- **Clean Code**: Focus on business logic, not boilerplate
- **Type Safety**: Leverage Go's generics for compile-time type checking
- **Performance**: Optimized implementations with minimal overhead
- **Concurrency**: Built-in thread-safe alternatives for concurrent environments
- **Flexibility**: Extensive customization options and extensible design
### ⚠️ Considerations
- **Learning Curve**: Requires familiarity with Go generics and constraints
- **Complexity**: Abstraction layers might add complexity for simple use cases
- **Memory Overhead**: Thread-safe variants use mutexes which add memory overhead
## 📚 Table of Contents
- [Data Structures](#-data-structures)
- [Maps](#maps)
- [Sets](#sets)
- [Stacks](#stacks)
- [Slices](#slices)
- [LinkedLists](#linkedlists)
- [Cryptographic Utilities](#-cryptographic-utilities)
- [Concurrency Tools](#-concurrency-tools)
- [Random Generation](#-random-generation)
- [CSV Processing](#-csv-processing)
- [Mathematical Utilities](#-mathematical-utilities)
- [ID Generation](#-id-generation)
- [Timing Utilities](#-timing-utilities)
- [Type Constraints](#-type-constraints)
- [Installation](#-installation)
- [API Reference](#-api-reference)
## 🗂️ Data Structures
All data structures provide both regular and thread-safe variants (prefixed with "Safe"). Thread-safe variants use RWMutex for concurrent access while maintaining performance.
### Maps
Generic maps with enhanced functionality for key-value storage.
```go
// Basic Map operations
m := abstract.NewMap[string, int]()
m.Set("apple", 5)
m.Set("banana", 3)
fmt.Println(m.Get("apple")) // 5
fmt.Println(m.Has("orange")) // false
fmt.Println(m.Keys()) // [apple, banana]
fmt.Println(m.Values()) // [5, 3]
// Thread-safe variant
safeMap := abstract.NewSafeMap[string, int]()
safeMap.Set("concurrent", 42)
value := safeMap.Get("concurrent") // Safe for concurrent access
// Advanced: EntityMap for objects with ordering
type User struct {
id string
name string
order int
}
func (u User) GetID() string { return u.id }
func (u User) GetName() string { return u.name }
func (u User) GetOrder() int { return u.order }
func (u User) SetOrder(o int) abstract.Entity[string] {
return User{id: u.id, name: u.name, order: o}
}
entityMap := abstract.NewEntityMap[string, User]()
entityMap.Set(User{id: "1", name: "Alice", order: 1})
entityMap.Set(User{id: "2", name: "Bob", order: 2})
users := entityMap.AllOrdered() // Returns users in order
user, found := entityMap.LookupByName("Alice")
```
### Sets
Efficient set implementation for unique value storage.
```go
// String set
stringSet := abstract.NewSet[string]()
stringSet.Add("apple", "banana", "apple") // Duplicates ignored
fmt.Println(stringSet.Has("apple")) // true
fmt.Println(stringSet.Len()) // 2
// Thread-safe set
safeSet := abstract.NewSafeSet[int]()
safeSet.Add(1, 2, 3)
safeSet.Remove(2)
values := safeSet.ToSlice() // [1, 3]
// Set operations
set1 := abstract.NewSet[int]()
set1.Add(1, 2, 3)
set2 := abstract.NewSet[int]()
set2.Add(3, 4, 5)
intersection := set1.Intersection(set2) // [3]
union := set1.Union(set2) // [1, 2, 3, 4, 5]
```
### Stacks
LIFO (Last In, First Out) data structure implementations.
```go
// Basic stack
stack := abstract.NewStack[string]()
stack.Push("first")
stack.Push("second")
stack.Push("third")
fmt.Println(stack.Pop()) // "third"
fmt.Println(stack.Last()) // "second" (peek without removing)
fmt.Println(stack.Len()) // 2
// Thread-safe stack
safeStack := abstract.NewSafeStack[int]()
safeStack.Push(10, 20, 30)
value := safeStack.Pop() // 30
// UniqueStack - no duplicates
uniqueStack := abstract.NewUniqueStack[string]()
uniqueStack.Push("a", "b", "a") // Only adds "a" once
fmt.Println(uniqueStack.Len()) // 2
```
### Slices
Enhanced slice operations with generic support.
```go
// Basic slice operations
slice := abstract.NewSlice[int]()
slice.Append(1, 2, 3, 4, 5)
slice.Insert(2, 99) // Insert 99 at index 2
fmt.Println(slice.Get(2)) // 99
value := slice.Pop() // Remove and return last element
slice.Delete(0) // Remove first element
slice.Reverse() // Reverse in-place
// Thread-safe slice
safeSlice := abstract.NewSafeSlice[string]()
safeSlice.Append("hello", "world")
safeSlice.Sort() // Sorts in-place for comparable types
// Slice utilities
numbers := []int{1, 2, 3, 4, 5}
doubled := abstract.Map(numbers, func(x int) int { return x * 2 })
evens := abstract.Filter(numbers, func(x int) bool { return x%2 == 0 })
```
### LinkedLists
Doubly linked list implementation with efficient insertion/deletion.
```go
// Basic linked list
list := abstract.NewLinkedList[string]()
list.PushFront("first")
list.PushBack("last")
list.PushFront("new first")
fmt.Println(list.Front()) // "new first"
fmt.Println(list.Back()) // "last"
fmt.Println(list.Len()) // 3
// Iterate through list
for elem := list.Front(); elem != nil; elem = elem.Next() {
fmt.Println(elem.Value)
}
// Thread-safe linked list
safeList := abstract.NewSafeLinkedList[int]()
safeList.PushBack(1, 2, 3)
value := safeList.PopFront() // 1
```
## 🔐 Cryptographic Utilities
Secure cryptographic operations with best-practice implementations.
```go
// AES Encryption
key := abstract.NewEncryptionKey()
plaintext := []byte("sensitive data")
// Encrypt
ciphertext, err := abstract.EncryptAES(plaintext, key)
if err != nil {
log.Fatal(err)
}
// Decrypt
decrypted, err := abstract.DecryptAES(ciphertext, key)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Decrypted: %s\n", decrypted)
// HMAC Generation
hmacKey := abstract.NewHMACKey()
message := []byte("important message")
mac := abstract.GenerateHMAC(message, hmacKey)
// Verify HMAC
isValid := abstract.CheckHMAC(message, mac, hmacKey)
fmt.Printf("HMAC valid: %v\n", isValid)
// ECDSA Signing
privateKey, err := abstract.NewSigningKey()
if err != nil {
log.Fatal(err)
}
signature, err := abstract.SignData(message, privateKey)
if err != nil {
log.Fatal(err)
}
// Verify signature
publicKey := &privateKey.PublicKey
isValid = abstract.VerifySign(message, signature, publicKey)
fmt.Printf("Signature valid: %v\n", isValid)
// Key encoding/decoding
pemPrivateKey, _ := abstract.EncodePrivateKey(privateKey)
pemPublicKey, _ := abstract.EncodePublicKey(publicKey)
decodedPrivate, _ := abstract.DecodePrivateKey(pemPrivateKey)
decodedPublic, _ := abstract.DecodePublicKey(pemPublicKey)
```
## ⚡ Concurrency Tools
Powerful concurrency utilities for async operations and parallel processing.
### Futures and Promises
```go
// Basic Future
ctx := context.Background()
future := abstract.NewFuture(ctx, logger, func(ctx context.Context) (string, error) {
time.Sleep(100 * time.Millisecond)
return "async result", nil
})
result, err := future.Get(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Result: %s\n", result)
// Future with timeout
result, err = future.GetWithTimeout(ctx, 50*time.Millisecond)
if err == abstract.ErrTimeout {
fmt.Println("Operation timed out")
}
// Waiter for void operations
waiter := abstract.NewWaiter(ctx, logger, func(ctx context.Context) error {
// Perform some work
return nil
})
err = waiter.Await(ctx)
// WaiterSet for multiple operations
waiterSet := abstract.NewWaiterSet(logger)
waiterSet.Add(ctx, func(ctx context.Context) error {
// Task 1
return nil
})
waiterSet.Add(ctx, func(ctx context.Context) error {
// Task 2
return nil
})
err = waiterSet.Await(ctx) // Wait for all tasks
```
### Worker Pools
#### WorkerPool - Context-Aware Worker Pool (Recommended)
For tasks that need context propagation, cancellation support, and return values.
```go
ctx := context.Background()
// Create context-aware worker pool
pool := abstract.NewWorkerPool[int](5, 100) // 5 workers, queue capacity 100
pool.Start(ctx)
defer pool.Shutdown(ctx)
// Submit context-aware tasks
for i := 0; i < 10; i++ {
i := i
ok := pool.Submit(ctx, func(ctx context.Context) (int, error) {
// Task can check context cancellation
select {
case <-ctx.Done():
return 0, ctx.Err()
default:
// Do work
return i * 2, nil
}
})
if !ok {
fmt.Println("Failed to submit task")
}
}
// Fetch results
results, errs := pool.FetchResults(ctx)
for i, result := range results {
if errs[i] != nil {
fmt.Printf("Task %d error: %v
", i, errs[i])
} else {
fmt.Printf("Task %d result: %d
", i, result)
}
}
// Monitor pool metrics
fmt.Printf("Queue: %d, Running: %d, Finished: %d, Total: %d
",
pool.TasksInQueue(), pool.OnFlyRunningTasks(),
pool.FinishedTasks(), pool.TotalTasks())
// Graceful shutdown with timeout
shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := pool.Shutdown(shutdownCtx); err != nil {
fmt.Printf("Shutdown error: %v
", err)
}
```
#### JobQueue - Fire-and-Forget Job Execution
For side-effect tasks without return values (logging, notifications, cleanup, etc.).
```go
ctx := context.Background()
// Create job queue
queue := abstract.NewJobQueue(5, 100) // 5 workers, queue capacity 100
queue.Start(ctx)
defer queue.StopNoWait()
// Submit fire-and-forget tasks
for i := 0; i < 100; i++ {
i := i
ok := queue.Submit(ctx, func(ctx context.Context) {
// Perform side-effect work
log.Printf("Processing item %d", i)
// Can still respect context cancellation
select {
case <-ctx.Done():
return
default:
// Continue work
}
})
if !ok {
fmt.Println("Failed to submit task")
}
}
// Wait for all tasks to complete
waitCtx, cancel := context.WithTimeout(ctx, 5*time.Minute)
defer cancel()
if err := queue.Wait(waitCtx); err != nil {
fmt.Printf("Wait error: %v
", err)
}
// Monitor queue metrics
fmt.Printf("Pending: %d, Running: %d, Finished: %d
",
queue.PendingTasks(), queue.OnFlyRunningTasks(), queue.FinishedTasks())
// Graceful shutdown
if err := queue.Shutdown(ctx); err != nil {
fmt.Printf("Shutdown error: %v
", err)
}
```
#### WorkerPoolV2 - Simple Generic Worker Pool
```go
// Create generic worker pool
pool := abstract.NewWorkerPoolV2[string](5, 100) // 5 workers, queue capacity 100
pool.Start()
defer pool.Stop()
// Submit tasks
for i := 0; i < 10; i++ {
i := i // Capture loop variable
task := func() (string, error) {
return fmt.Sprintf("Task %d result", i), nil
}
if !pool.Submit(task) {
fmt.Println("Failed to submit task")
}
}
// Fetch results (waits for all submitted tasks at call time)
results, errors := pool.FetchResults(5 * time.Second)
for i, result := range results {
if errors[i] != nil {
fmt.Printf("Task error: %v
", errors[i])
} else {
fmt.Printf("Task result: %v
", result)
}
}
// Submit with timeout
if !pool.Submit(task, 100*time.Millisecond) {
fmt.Println("Task submission timed out")
}
// Monitor pool status
fmt.Printf("Submitted: %d, Running: %d, Finished: %d
",
pool.Submitted(), pool.Running(), pool.Finished())
// Fetch all results (including tasks submitted after call)
allResults, allErrors := pool.FetchAllResults(10 * time.Second)
```go
// Create generic worker pool
pool := abstract.NewWorkerPoolV2[string](5, 100) // 5 workers, queue capacity 100
pool.Start()
defer pool.Stop()
// Submit tasks
for i := 0; i < 10; i++ {
i := i // Capture loop variable
task := func() (string, error) {
return fmt.Sprintf("Task %d result", i), nil
}
if !pool.Submit(task) {
fmt.Println("Failed to submit task")
}
}
// Fetch results (waits for all submitted tasks at call time)
results, errors := pool.FetchResults(5 * time.Second)
for i, result := range results {
if errors[i] != nil {
fmt.Printf("Task error: %v
", errors[i])
} else {
fmt.Printf("Task result: %v
", result)
}
}
// Submit with timeout
if !pool.Submit(task, 100*time.Millisecond) {
fmt.Println("Task submission timed out")
}
// Monitor pool status
fmt.Printf("Submitted: %d, Running: %d, Finished: %d
",
pool.Submitted(), pool.Running(), pool.Finished())
// Fetch all results (including tasks submitted after call)
allResults, allErrors := pool.FetchAllResults(10 * time.Second)
```
#### ContextPool - Context-Aware Worker Pool
For tasks that need context propagation, cancellation support, and return values.
```go
ctx := context.Background()
// Create context-aware worker pool
pool := abstract.NewContextPool[int](5, 100) // 5 workers, queue capacity 100
pool.Start(ctx)
defer pool.Shutdown(ctx)
// Submit context-aware tasks
for i := 0; i < 10; i++ {
i := i
ok := pool.Submit(ctx, func(ctx context.Context) (int, error) {
// Task can check context cancellation
select {
case <-ctx.Done():
return 0, ctx.Err()
default:
// Do work
return i * 2, nil
}
})
if !ok {
fmt.Println("Failed to submit task")
}
}
// Fetch results
results, errs := pool.FetchResults(ctx)
for i, result := range results {
if errs[i] != nil {
fmt.Printf("Task %d error: %v
", i, errs[i])
} else {
fmt.Printf("Task %d result: %d
", i, result)
}
}
// Monitor pool metrics
fmt.Printf("Queue: %d, Running: %d, Finished: %d, Total: %d
",
pool.TasksInQueue(), pool.OnFlyRunningTasks(),
pool.FinishedTasks(), pool.TotalTasks())
// Graceful shutdown with timeout
shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := pool.Shutdown(shutdownCtx); err != nil {
fmt.Printf("Shutdown error: %v
", err)
}
```
#### ContextJobQueue - Fire-and-Forget Job Execution
For side-effect tasks without return values (logging, notifications, cleanup, etc.).
```go
ctx := context.Background()
// Create job queue
queue := abstract.NewContextJobQueue(5, 100) // 5 workers, queue capacity 100
queue.Start(ctx)
defer queue.StopNoWait()
// Submit fire-and-forget tasks
for i := 0; i < 100; i++ {
i := i
ok := queue.Submit(ctx, func(ctx context.Context) {
// Perform side-effect work
log.Printf("Processing item %d", i)
// Can still respect context cancellation
select {
case <-ctx.Done():
return
default:
// Continue work
}
})
if !ok {
fmt.Println("Failed to submit task")
}
}
// Wait for all tasks to complete
waitCtx, cancel := context.WithTimeout(ctx, 5*time.Minute)
defer cancel()
if err := queue.Wait(waitCtx); err != nil {
fmt.Printf("Wait error: %v
", err)
}
// Monitor queue metrics
fmt.Printf("Pending: %d, Running: %d, Finished: %d
",
queue.PendingTasks(), queue.OnFlyRunningTasks(), queue.FinishedTasks())
// Graceful shutdown
if err := queue.Shutdown(ctx); err != nil {
fmt.Printf("Shutdown error: %v
", err)
}
```
### Rate Limiting
```go
// Rate-limited processing
ctx := context.Background()
processor := abstract.NewRateProcessor(ctx, 10) // Max 10 operations per second
// Add rate-limited tasks
for i := 0; i < 100; i++ {
i := i
processor.AddTask(func(ctx context.Context) error {
// This will be rate-limited
fmt.Printf("Processing item %d\n", i)
return nil
})
}
// Wait for completion
errors := processor.Wait()
if len(errors) > 0 {
fmt.Printf("Encountered %d errors\n", len(errors))
}
```
### Concurrent Helpers
```go
// Periodic updater
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
abstract.StartUpdater(ctx, 30*time.Second, logger, func() {
fmt.Println("Periodic health check")
})
// Immediate execution, then periodic
abstract.StartUpdaterNow(ctx, time.Minute, logger, func() {
fmt.Println("Sync operation")
})
// With shutdown handler
abstract.StartUpdaterWithShutdown(ctx, 10*time.Second, logger,
func() {
// Periodic work
},
func() {
fmt.Println("Shutting down...")
},
)
```
## 🎲 Random Generation
Cryptographically secure random generation with multiple character sets.
```go
// Basic random strings
token := abstract.GetRandomString(32) // Hexadecimal
sessionID := abstract.GetRandomAlphaNumeric(16) // Letters + numbers
pin := abstract.GetRandomNumeric(6) // Numbers only
code := abstract.GetRandomLowerAlpha(8) // Lowercase letters
id := abstract.GetRandomUpperAlpha(4) // Uppercase letters
// Custom alphabet
customAlphabet := []byte("!@#$%^&*")
symbols := abstract.GetRandomStringWithAlphabet(8, customAlphabet)
// Random numbers and choices
dice := abstract.GetRandomInt(1, 6)
coinFlip := abstract.GetRandomBool()
colors := []string{"red", "green", "blue", "yellow"}
color, ok := abstract.GetRandomChoice(colors)
if ok {
fmt.Printf("Random color: %s\n", color)
}
// Shuffle operations
cards := []string{"A", "K", "Q", "J", "10", "9", "8", "7"}
abstract.ShuffleSlice(cards)
fmt.Printf("Shuffled: %v\n", cards)
// Random network addresses (for testing)
addr := abstract.GetRandListenAddress() // ":12345" (random port)
```
## 📊 CSV Processing
Advanced CSV table manipulation with indexing and querying capabilities.
```go
// Load CSV from file
table, err := abstract.NewCSVTableFromFilePath("data.csv")
if err != nil {
log.Fatal(err)
}
// Create from data
data := map[string]map[string]string{
"user1": {"name": "Alice", "age": "30", "city": "NYC"},
"user2": {"name": "Bob", "age": "25", "city": "LA"},
}
table = abstract.NewCSVTableFromMap(data, "userID")
// Query operations
row := table.Row("user1")
value := table.Value("user1", "name") // "Alice"
exists := table.Has("user3") // false
// Find operations
criteria := map[string]string{"city": "NYC"}
foundID, foundRow := table.FindRow(criteria)
allNYCUsers := table.Find(criteria)
// Modifications
table.AddRow("user3", map[string]string{
"name": "Charlie",
"age": "35",
"city": "Chicago",
})
table.UpdateRow("user1", map[string]string{
"age": "31",
})
table.AppendColumn("country", []string{"USA", "USA", "USA"})
table.DeleteColumn("age")
table.DeleteRow("user2")
// Export
csvBytes := table.Bytes()
fmt.Printf("CSV Data:\n%s", csvBytes)
// Thread-safe operations
safeTable := abstract.NewCSVTableSafe(records)Frequently Asked Questions
What is abstract?
abstract is a Utilities library for the Go programming language. Abstractions and utilities to get rid of boilerplate code in business logic
How do I install abstract?
Install abstract with the Go module system using `go get maxbolgarin/abstract`. Check the repository for the current installation instructions.
What category does abstract belong to?
abstract is listed under Utilities, specifically Utilities.