- 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
193 lines
6.0 KiB
Go
193 lines
6.0 KiB
Go
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,
|
||
}
|
||
}
|