Files
go-jdenticon/jdenticon/generator.go
Kevin McIntyre f1544ef49c
Some checks failed
CI / Test (Go 1.24.x, ubuntu-latest) (push) Successful in 1m53s
CI / Code Quality (push) Failing after 26s
CI / Security Scan (push) Failing after 11s
CI / Test Coverage (push) Successful in 1m13s
CI / Benchmarks (push) Failing after 10m22s
CI / Build CLI (push) Failing after 8s
Benchmarks / Run Benchmarks (push) Failing after 10m13s
Release / Test (push) Successful in 55s
Release / Build (amd64, darwin, ) (push) Failing after 12s
Release / Build (amd64, linux, ) (push) Failing after 6s
Release / Build (amd64, windows, .exe) (push) Failing after 12s
Release / Build (arm64, darwin, ) (push) Failing after 12s
Release / Build (arm64, linux, ) (push) Failing after 12s
Release / Release (push) Has been skipped
CI / Test (Go 1.24.x, macos-latest) (push) Has been cancelled
CI / Test (Go 1.24.x, windows-latest) (push) Has been cancelled
chore: update module path to gitea.dockr.co/kev/go-jdenticon
Move hosting from GitHub to private Gitea instance.
2026-02-10 10:07:57 -05:00

125 lines
3.6 KiB
Go

package jdenticon
import (
"context"
"fmt"
"gitea.dockr.co/kev/go-jdenticon/internal/engine"
"gitea.dockr.co/kev/go-jdenticon/internal/util"
)
// Generator provides thread-safe identicon generation with caching.
// It wraps the internal engine.Generator to provide a clean public API.
type Generator struct {
engine *engine.Generator
config Config // Store the configuration for validation during Generate calls
}
// NewGenerator creates a new Generator with default configuration and default caching.
func NewGenerator() (*Generator, error) {
engineGen, err := engine.NewDefaultGenerator()
if err != nil {
return nil, NewErrGenerationFailed("", 0, err)
}
return &Generator{
engine: engineGen,
config: DefaultConfig(),
}, nil
}
// NewGeneratorWithCacheSize creates a new Generator with the specified cache size.
func NewGeneratorWithCacheSize(cacheSize int) (*Generator, error) {
if cacheSize <= 0 {
return nil, NewErrInvalidInput("cacheSize", fmt.Sprintf("%d", cacheSize), "must be positive")
}
config := engine.DefaultGeneratorConfig()
config.CacheSize = cacheSize
engineGen, err := engine.NewGeneratorWithConfig(config)
if err != nil {
return nil, NewErrCacheCreationFailed(cacheSize, err)
}
return &Generator{
engine: engineGen,
config: DefaultConfig(),
}, nil
}
// NewGeneratorWithConfig creates a new Generator with custom configuration and caching.
func NewGeneratorWithConfig(config Config, cacheSize int) (*Generator, error) {
if cacheSize <= 0 {
return nil, NewErrInvalidInput("cacheSize", fmt.Sprintf("%d", cacheSize), "must be positive")
}
// Convert public config to internal config
colorConfig, err := config.toEngineColorConfig()
if err != nil {
return nil, err
}
generatorConfig := engine.GeneratorConfig{
ColorConfig: colorConfig,
CacheSize: cacheSize,
MaxComplexity: config.MaxComplexity,
MaxIconSize: config.MaxIconSize,
}
engineGen, err := engine.NewGeneratorWithConfig(generatorConfig)
if err != nil {
return nil, NewErrCacheCreationFailed(cacheSize, err)
}
return &Generator{
engine: engineGen,
config: config,
}, nil
}
// Generate creates an identicon for the given input string and size with context support.
// The context can be used to set timeouts or cancel generation.
//
// The input string is hashed to generate a deterministic identicon.
// Size must be positive and represents the width/height in pixels.
//
// This method applies the security limits that were configured when the Generator was created.
// For different limits, create a new Generator with NewGeneratorWithConfig.
//
// This method is thread-safe and uses caching if configured.
func (g *Generator) Generate(ctx context.Context, input string, size int) (*Icon, error) {
// Apply validation using the generator's stored configuration
if err := validateInputs(input, size, g.config); err != nil {
return nil, err
}
// Check for early cancellation
if err := ctx.Err(); err != nil {
return nil, err
}
// Convert input to hash
hash := util.ComputeHash(input)
// Validate complexity before generation
if err := validateComplexity(hash, g.config); err != nil {
return nil, err
}
// Generate using the internal engine with context
engineIcon, err := g.engine.Generate(ctx, hash, float64(size))
if err != nil {
return nil, NewErrGenerationFailed(input, size, err)
}
// Wrap in public Icon directly
return newIcon(engineIcon), nil
}
// GetCacheMetrics returns the cache hit and miss counts.
// These metrics are thread-safe to read.
func (g *Generator) GetCacheMetrics() (hits, misses int64) {
return g.engine.GetCacheMetrics()
}