Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v2
uses: actions/setup-go@v5
with:
go-version: 1.23
go-version: "1.25"

- name: Setup golangci-lint
uses: golangci/golangci-lint-action@v3.1.0
uses: golangci/golangci-lint-action@v8
with:
version: v1.61.0
version: v2.5.0

- name: Test
run: make test coverage
116 changes: 58 additions & 58 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,81 +3,81 @@
# license that can be found in the LICENSE file.

---
version: "2"

run:
timeout: 1m
skip-files:
- internal/*

linters:
enable-all: true
default: all
disable:
- wrapcheck
- testpackage
- gochecknoglobals
- exhaustivestruct
- exhaustruct
- paralleltest
- godox
- cyclop
- tagliatelle
- goerr113
- err113 # renamed from goerr113
- varnamelen
- errorlint
- forcetypeassert
- perfsprint
- depguard
- testifylint
fast: false

# Settings for specific linters
linters-settings:
funlen:
lines: 150
statements: 45

issues:
exclude-rules:
- path: cmd/
linters:
- gochecknoinits
- gomnd
- forbidigo
- lll

- path: example_test.go
linters:
- lll
- dupword

- path: internal/http/
linters:
- unparam
- nlreturn

- path: _test\.go
linters:
- scopelint
- wsl
- nlreturn
- funlen
- dupl
- gocognit
- nosnakecase
- lll

- path: doc.go
linters:
- lll

- linters:
- lll
source: "json:"

- linters:
- lll
source: "Err"
# Linters introduced in golangci-lint v2 that are not part of this repo's
# v1 baseline and conflict with its established idioms; kept off so the v2
# migration preserves the previous effective rule set.
- noinlineerr # idiomatic `if err := f(); err != nil` is used throughout
- funcorder # repo groups code via "//////" section dividers, not ctor-first
- wsl_v5 # superseded ruleset; `wsl` (below) preserves the prior behavior
settings:
funlen:
lines: 150
statements: 45
exclusions:
paths:
- internal/.*
rules:
- path: cmd/
linters:
- gochecknoinits
- mnd # renamed from gomnd
- forbidigo
- lll
- path: example_test.go
linters:
- lll
- dupword
- path: internal/http/
linters:
- unparam
- nlreturn
- path: _test\.go
linters:
- wsl
- nlreturn
- funlen
- dupl
- gocognit
- lll
- gosmopolitan # i18n test fixtures legitimately contain non-Latin text
- path: doc.go
linters:
- lll
- source: "json:"
linters:
- lll
- source: "Err"
linters:
- lll
- source: "//////"
linters:
- gocritic
- godot

- linters:
- gocritic
- godot
source: "//////"
formatters:
enable:
- gofmt
- goimports
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.0.0] - 2026-05-30
### Changed
- **BREAKING:** module path is now `github.com/thalesfsp/customerror/v2`.
- **BREAKING:** minimum Go is now 1.25 (`go.mod` `go 1.25.0`); CI runs Go 1.25.
- `From` returns a modified copy instead of mutating the original (safe for shared sentinels).
- `WithLanguage`/`WithTranslation`/`FormatError` no longer panic on invalid input; they degrade gracefully.
- `ErrorCode` validation is now strict/anchored: only letters, digits, and underscores are accepted.
- `Catalog.Get`/`MustGet` apply the provided options and return a copy (the stored entry is never handed out or mutated).
- `WithFields` merges into existing fields instead of replacing them.
- `Wrap` preserves the identity of every wrapped error (`errors.Is`/`errors.As` match all of them).
- Chinese language code corrected to ISO 639-1 `zh` (was `ch`); Italian "failed to" template spelling fixed.
- Dependencies updated to latest; linting migrated to golangci-lint v2.

### Removed
- **BREAKING:** removed the unused exported `CustomError.LanguageErrorTypeMap` field.

### Fixed
- `New` no longer terminates the process (`log.Fatalf`/`os.Exit`) on invalid input; it panics (recoverable).
- `NewHTTPError` (method) no longer mutates its receiver (no more "sticky" status code on reused factories).
- `MarshalJSON` no longer lets user fields clobber structural keys (`code`, `message`, `statusCode`, `tags`, `retryable`, `retried`).
- `Error()`/`APIError()` no longer emit dangling `". Tags:"`/`". Fields:"` for empty sets/maps.
- Instance factory methods honor ignore options without a nil type-assertion panic.
- Resolved the open Dependabot security advisories by upgrading dependencies.

## [1.1.1] - 2023-03-29
### Added
- Added `NewNotFoundError`.
Expand Down
77 changes: 77 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# customerror — project guide for Claude

Go library providing rich, structured custom errors (`*CustomError`) with a code,
HTTP status code, tags, fields, i18n/translations, retryability, and an error
`Catalog`. Module path: `github.com/thalesfsp/customerror/v2` (v2 — importers use
the `/v2` suffix; the package name is still `customerror`).

## Layout
- `customerror.go` — core `CustomError` type; `Error()/APIError()/JustError()`;
`MarshalJSON`; `Is`/`Unwrap`; `Copy`; package funcs (`New`, `Factory`, `From`,
`Wrap`, `To`, `IsCustomError`, `IsHTTPStatus`, `IsErrorCode`, `IsRetryable`);
instance factory methods (`cE.NewFailedToError/NewInvalidError/NewMissingError/
NewRequiredError/NewHTTPError/New`, `FormatError`, `NewChildError`).
- `builtin.go` — package-level constructors (`NewFailedToError`, `NewInvalidError`,
`NewMissingError`, `NewRequiredError`, `NewNotFoundError`, `NewHTTPError`).
- `options.go` — functional options + `prependOptions`.
- `language.go` — `Language` type, ISO 639-1/3166-1 regex, `NewLanguage`.
- `languageprefixtemplate.go` — built-in translation templates (singleton via
`sync.Once`), `GetTemplate`, `AddNewLanguage`, `NewErrorPrefixMap`.
- `catalog.go` — `Catalog` (error code → error), `ErrorCode` validation.
- Tests: `customerror_test.go`, `catalog_test.go`,
`languageprefixtemplate_test.go`, `example_test.go` (runnable Examples that
assert EXACT stdout), `bugfixes_test.go` (regression tests; happy/bad/edge).

## Conventions
- Functional options (Rob Pike style). Options are `func(*CustomError)` and
cannot return errors, so invalid input is ignored gracefully (do NOT add
`panic` to options — `WithLanguage`/`WithTranslation` ignore bad codes).
- `//////` section dividers; doc comment on every exported symbol; comments end
with a period (godot is enabled).
- `Copy(src, target)` mutates and returns `target`, copying only NON-ZERO src
fields; `LanguageMessageMap`/`Fields`/`Tags` are merged into fresh containers.
- Factory methods intentionally apply options twice (in `FormatError`/method
body, then again in the inner package constructor) and depend on `Copy`'s
"non-zero overwrites" merge for option precedence (e.g. a user `WithStatusCode`
must win over a built-in default). Do NOT "dedupe" this without re-checking
precedence — it is load-bearing.

## Gotchas
- `Error()` output is non-deterministic for >1 field (`sync.Map` range order);
assert with `strings.Contains`, not equality.
- `example_test.go` asserts EXACT stdout — any message-format change breaks it.
- The template singleton (`languageprefixtemplate.go`) is package-global and
persists across tests. In tests, register/use UNIQUE language codes (e.g.
"xy", "qq") so you don't pollute other tests.
- `New`/`Factory` PANIC (recoverable) on invalid attributes: message `gte=3`,
code `gte=2`, status `100..511`. They must NEVER call `os.Exit`/`log.Fatalf`.
- `From(*CustomError, …)` returns a modified COPY (never mutates the original);
so `errors.Is(From(x,…), x)` is false for a `*CustomError` input.

## Build / test / lint
- `make test` / `make coverage` — `go test -race -cover` (passes; ~93%).
- `make lint` — golangci-lint with `.golangci.yml` (**v2 format**, `default: all`).
CI pins **golangci-lint v2.5.0** + **Go 1.25** (`.github/workflows/go.yml`,
`golangci/golangci-lint-action@v8`). A modern (v2.x) golangci-lint runs the
config directly — no version gymnastics needed.
- The v2 migration disabled three linters that are new in v2 and clash with
the repo's idioms, to preserve the prior baseline: `noinlineerr`,
`funcorder`, `wsl_v5` (the old `wsl` stays). Re-enable deliberately if you
want to adopt them (each needs codebase-wide changes).
- `default: all` is strict: `intrange`/`copyloopvar` are on (use
`for i := range n`); non-Latin template strings need a `gosmopolitan`
exception (test files are already excluded) or `//nolint:gosmopolitan`.

## Dependency policy
- Tracks the **latest** releases (go.mod `go` directive is `1.25.0`, matching CI).
When bumping, confirm a candidate's required Go via
`curl -s https://proxy.golang.org/<module>/@v/<ver>.mod | grep '^go '` and keep
it ≤ the CI Go. Do NOT add a `toolchain` directive (a library shouldn't pin one).

## CI & releases
- The workflow runs ONLY on push to `main` and PRs targeting `main`. Pushing a
feature branch does NOT trigger CI.
- Fresh clones may not fetch tags (`git fetch --tags`). Versioned via semver
tags. This module is **v2** (`/v2` path); the v1 line stopped at `v1.2.9`.
A breaking change now requires a `/v3` path; prefer additive/deprecation
within `v2.x`.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@

## Install

`$ go get github.com/thalesfsp/customerror@vX.Y.Z`
`$ go get github.com/thalesfsp/customerror/v2@latest`

Import it (the package name remains `customerror`):

```go
import "github.com/thalesfsp/customerror/v2"
```

## Usage

See [`example_test.go`](example_test.go), and [`customerror_test.go`](customerror_test.go) file.

## Documentation

Run `$ make doc` or check out [online](https://pkg.go.dev/github.com/thalesfsp/customerror).
Run `$ make doc` or check out [online](https://pkg.go.dev/github.com/thalesfsp/customerror/v2).

## Development

Expand Down
Loading
Loading