Initial release: Go Jdenticon library v0.1.0

- Core library with SVG and PNG generation
- CLI tool with generate and batch commands
- Cross-platform path handling for Windows compatibility
- Comprehensive test suite with integration tests
This commit is contained in:
Kevin McIntyre
2026-01-02 23:56:48 -05:00
parent f84b511895
commit d9e84812ff
292 changed files with 19725 additions and 38884 deletions

192
jdenticon/errors.go Normal file
View File

@@ -0,0 +1,192 @@
package jdenticon
import (
"fmt"
)
// Error types for structured error handling following Go best practices.
// ErrInvalidInput represents an error due to invalid input parameters.
type ErrInvalidInput struct {
Field string // The field or parameter that was invalid
Value string // The invalid value (may be truncated for display)
Reason string // Human-readable explanation of why it's invalid
}
func (e *ErrInvalidInput) Error() string {
if e.Field != "" {
return fmt.Sprintf("jdenticon: invalid input for %s: %s (got: %s)", e.Field, e.Reason, e.Value)
}
return fmt.Sprintf("jdenticon: invalid input: %s", e.Reason)
}
// NewErrInvalidInput creates a new ErrInvalidInput.
func NewErrInvalidInput(field, value, reason string) *ErrInvalidInput {
return &ErrInvalidInput{
Field: field,
Value: value,
Reason: reason,
}
}
// ErrInvalidSize represents an error due to invalid size parameter.
type ErrInvalidSize int
func (e ErrInvalidSize) Error() string {
return fmt.Sprintf("jdenticon: invalid size: must be positive, got %d", int(e))
}
// ErrInvalidIcon represents an error due to invalid icon state.
type ErrInvalidIcon string
func (e ErrInvalidIcon) Error() string {
return fmt.Sprintf("jdenticon: invalid icon: %s", string(e))
}
// ErrRenderFailed represents an error during rendering.
type ErrRenderFailed struct {
Format string // The format being rendered (SVG, PNG)
Cause error // The underlying error
}
func (e *ErrRenderFailed) Error() string {
return fmt.Sprintf("jdenticon: %s rendering failed: %v", e.Format, e.Cause)
}
func (e *ErrRenderFailed) Unwrap() error {
return e.Cause
}
// NewErrRenderFailed creates a new ErrRenderFailed.
func NewErrRenderFailed(format string, cause error) *ErrRenderFailed {
return &ErrRenderFailed{
Format: format,
Cause: cause,
}
}
// ErrGenerationFailed represents an error during identicon generation.
type ErrGenerationFailed struct {
Input string // The input string that failed to generate (may be truncated)
Size int // The requested size
Cause error // The underlying error
}
func (e *ErrGenerationFailed) Error() string {
return fmt.Sprintf("jdenticon: generation failed for input %q (size %d): %v", e.Input, e.Size, e.Cause)
}
func (e *ErrGenerationFailed) Unwrap() error {
return e.Cause
}
// NewErrGenerationFailed creates a new ErrGenerationFailed.
func NewErrGenerationFailed(input string, size int, cause error) *ErrGenerationFailed {
// Truncate long inputs for display
displayInput := input
if len(input) > 50 {
displayInput = input[:47] + "..."
}
return &ErrGenerationFailed{
Input: displayInput,
Size: size,
Cause: cause,
}
}
// ErrCacheCreationFailed represents an error during cache creation.
type ErrCacheCreationFailed struct {
Size int // The requested cache size
Cause error // The underlying error
}
func (e *ErrCacheCreationFailed) Error() string {
return fmt.Sprintf("jdenticon: cache creation failed (size %d): %v", e.Size, e.Cause)
}
func (e *ErrCacheCreationFailed) Unwrap() error {
return e.Cause
}
// NewErrCacheCreationFailed creates a new ErrCacheCreationFailed.
func NewErrCacheCreationFailed(size int, cause error) *ErrCacheCreationFailed {
return &ErrCacheCreationFailed{
Size: size,
Cause: cause,
}
}
// ErrValueTooLarge is returned when a numeric input exceeds a configured limit.
// This supports the configurable DoS protection system.
type ErrValueTooLarge struct {
ParameterName string // The name of the parameter being validated (e.g., "IconSize", "InputLength")
Limit int // The configured limit that was exceeded
Actual int // The actual value that was provided
}
func (e *ErrValueTooLarge) Error() string {
return fmt.Sprintf("jdenticon: %s of %d exceeds configured limit of %d",
e.ParameterName, e.Actual, e.Limit)
}
// NewErrValueTooLarge creates a new ErrValueTooLarge.
func NewErrValueTooLarge(parameterName string, limit, actual int) *ErrValueTooLarge {
return &ErrValueTooLarge{
ParameterName: parameterName,
Limit: limit,
Actual: actual,
}
}
// ErrEffectiveSizeTooLarge is returned when the effective PNG size (size * supersampling) exceeds limits.
// This provides specific context for PNG rendering with supersampling.
type ErrEffectiveSizeTooLarge struct {
Limit int // The configured size limit
Actual int // The calculated effective size (size * supersampling)
Size int // The requested icon size
Supersampling int // The supersampling factor applied
}
func (e *ErrEffectiveSizeTooLarge) Error() string {
return fmt.Sprintf("jdenticon: effective PNG size of %d (size %d × supersampling %d) exceeds limit of %d",
e.Actual, e.Size, e.Supersampling, e.Limit)
}
// NewErrEffectiveSizeTooLarge creates a new ErrEffectiveSizeTooLarge.
func NewErrEffectiveSizeTooLarge(limit, actual, size, supersampling int) *ErrEffectiveSizeTooLarge {
return &ErrEffectiveSizeTooLarge{
Limit: limit,
Actual: actual,
Size: size,
Supersampling: supersampling,
}
}
// ErrComplexityLimitExceeded is returned when the calculated geometric complexity exceeds the configured limit.
// This prevents resource exhaustion attacks through extremely complex identicon generation.
type ErrComplexityLimitExceeded struct {
Limit int // The configured complexity limit
Actual int // The calculated complexity score
InputHash string // The input hash that caused the high complexity (truncated for display)
}
func (e *ErrComplexityLimitExceeded) Error() string {
return fmt.Sprintf("jdenticon: complexity score of %d exceeds limit of %d for input hash %s",
e.Actual, e.Limit, e.InputHash)
}
// NewErrComplexityLimitExceeded creates a new ErrComplexityLimitExceeded.
func NewErrComplexityLimitExceeded(limit, actual int, inputHash string) *ErrComplexityLimitExceeded {
// Truncate long hash for display
displayHash := inputHash
if len(inputHash) > 16 {
displayHash = inputHash[:13] + "..."
}
return &ErrComplexityLimitExceeded{
Limit: limit,
Actual: actual,
InputHash: displayHash,
}
}