Awesome Go

abstract

CategoryUtilities
SubcategoryUtilities
Stars9

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.

← Back to Utilities