Skip to content

Commit 3080a9e

Browse files
committed
Update docs and migrate to golangci-lint v2
- Add missing ErrorOrFuncf and ErrorOrFuncWithErr functions to README - Enhance package documentation in ctrl.go with comprehensive godoc - Migrate golangci-lint configuration to v2 format - Update GitHub workflow to use golangci-lint-action@v7 - Fix shadow variable issue in http_shutdown_test.go
1 parent ff4ef0e commit 3080a9e

File tree

5 files changed

+185
-72
lines changed

5 files changed

+185
-72
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ jobs:
2929
go build -race
3030
3131
- name: golangci-lint
32-
uses: golangci/golangci-lint-action@v3
32+
uses: golangci/golangci-lint-action@v7
3333
with:
34-
version: latest
34+
version: v2.3.1
3535
skip-pkg-cache: true
3636

3737
- name: install goveralls

.golangci.yml

Lines changed: 69 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,82 @@
1-
linters-settings:
2-
govet:
3-
shadow: true
4-
gocyclo:
5-
min-complexity: 15
6-
maligned:
7-
suggest-new: true
8-
goconst:
9-
min-len: 2
10-
min-occurrences: 2
11-
misspell:
12-
locale: US
13-
lll:
14-
line-length: 140
15-
gocritic:
16-
enabled-tags:
17-
- performance
18-
- style
19-
- experimental
20-
disabled-checks:
21-
- wrapperFunc
22-
1+
version: "2"
2+
run:
3+
concurrency: 4
234
linters:
5+
default: none
246
enable:
25-
- staticcheck
26-
- revive
27-
- govet
28-
- unconvert
29-
- gosec
30-
- unparam
31-
- typecheck
32-
- ineffassign
33-
- stylecheck
34-
- gochecknoinits
35-
- copyloopvar
36-
- gocritic
37-
- nakedret
38-
- gosimple
39-
- prealloc
40-
- unused
417
- contextcheck
428
- copyloopvar
439
- decorder
4410
- errorlint
4511
- exptostd
4612
- gochecknoglobals
47-
- gofmt
48-
- goimports
13+
- gochecknoinits
14+
- gocritic
15+
- gosec
16+
- govet
17+
- ineffassign
4918
- intrange
19+
- nakedret
5020
- nilerr
21+
- prealloc
5122
- predeclared
23+
- revive
24+
- staticcheck
5225
- testifylint
5326
- thelper
54-
fast: false
55-
disable-all: true
56-
57-
58-
run:
59-
concurrency: 4
60-
61-
issues:
62-
exclude-rules:
63-
- text: "G114: Use of net/http serve function that has no support for setting timeouts"
64-
linters:
65-
- gosec
66-
- linters:
67-
- unparam
68-
- revive
69-
path: _test\.go$
70-
text: "unused-parameter"
71-
- linters:
72-
- prealloc
73-
path: _test\.go$
74-
text: "Consider pre-allocating"
75-
- linters:
76-
- gosec
77-
- intrange
78-
path: _test\.go$
79-
exclude-use-default: false
27+
- unconvert
28+
- unparam
29+
- unused
30+
settings:
31+
goconst:
32+
min-len: 2
33+
min-occurrences: 2
34+
gocritic:
35+
disabled-checks:
36+
- wrapperFunc
37+
enabled-tags:
38+
- performance
39+
- style
40+
- experimental
41+
gocyclo:
42+
min-complexity: 15
43+
govet:
44+
enable:
45+
- shadow
46+
lll:
47+
line-length: 140
48+
misspell:
49+
locale: US
50+
exclusions:
51+
generated: lax
52+
rules:
53+
- linters:
54+
- gosec
55+
text: 'G114: Use of net/http serve function that has no support for setting timeouts'
56+
- linters:
57+
- revive
58+
- unparam
59+
path: _test\.go$
60+
text: unused-parameter
61+
- linters:
62+
- prealloc
63+
path: _test\.go$
64+
text: Consider pre-allocating
65+
- linters:
66+
- gosec
67+
- intrange
68+
path: _test\.go$
69+
paths:
70+
- third_party$
71+
- builtin$
72+
- examples$
73+
formatters:
74+
enable:
75+
- gofmt
76+
- goimports
77+
exclusions:
78+
generated: lax
79+
paths:
80+
- third_party$
81+
- builtin$
82+
- examples$

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,26 @@ if err := ctrl.ErrorOrFunc(func() bool {
101101
return err
102102
}
103103

104+
// Function-based with formatted error message
105+
if err := ctrl.ErrorOrFuncf(func() bool {
106+
return cache.Size() < maxSize
107+
}, "cache size exceeded: %d/%d", cache.Size(), maxSize); err != nil {
108+
return err
109+
}
110+
104111
// With custom error
105112
customErr := ErrDatabaseNotConnected
106113
if err := ctrl.ErrorOrWithErr(database.IsConnected(), customErr); err != nil {
107114
return err // Will return customErr if condition fails
108115
}
116+
117+
// Function-based with custom error
118+
cacheErr := ErrCacheFull
119+
if err := ctrl.ErrorOrFuncWithErr(func() bool {
120+
return cache.Size() < maxSize
121+
}, cacheErr); err != nil {
122+
return err // Will return cacheErr if condition fails
123+
}
109124
```
110125

111126
These functions are particularly useful in validators, middleware, and other scenarios where returning an error is more appropriate than panicking.

ctrl.go

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,97 @@
1-
// Package ctrl provides a set of control functions for assertions, termination, and so on.
1+
// Package ctrl provides a set of control functions for assertions, error handling, HTTP server management,
2+
// and graceful shutdown handling in Go applications. Built for Go 1.21+, it offers a clean API with flexible
3+
// configuration options and no external runtime dependencies.
4+
//
5+
// # Assertions
6+
//
7+
// The package provides assertion functions that panic when conditions are not met, useful for runtime
8+
// invariant checking:
9+
//
10+
// ctrl.Assert(user.IsAuthenticated())
11+
// ctrl.Assertf(count > 0, "expected positive count, got %d", count)
12+
// ctrl.AssertFunc(func() bool { return database.IsConnected() })
13+
// ctrl.AssertFuncf(func() bool { return cache.Size() < maxSize }, "cache exceeded: %d", cache.Size())
14+
//
15+
// # Error Handling
16+
//
17+
// For scenarios where returning an error is more appropriate than panicking, the package provides
18+
// ErrorOr variants:
19+
//
20+
// if err := ctrl.ErrorOr(user.IsAuthenticated()); err != nil {
21+
// return err
22+
// }
23+
// if err := ctrl.ErrorOrf(count > 0, "expected positive count, got %d", count); err != nil {
24+
// return err
25+
// }
26+
// if err := ctrl.ErrorOrFunc(func() bool { return database.IsConnected() }); err != nil {
27+
// return err
28+
// }
29+
// if err := ctrl.ErrorOrFuncf(func() bool { return cache.Size() < maxSize },
30+
// "cache size exceeded: %d/%d", cache.Size(), maxSize); err != nil {
31+
// return err
32+
// }
33+
// customErr := errors.New("database not connected")
34+
// if err := ctrl.ErrorOrWithErr(database.IsConnected(), customErr); err != nil {
35+
// return err // Returns customErr if condition fails
36+
// }
37+
// if err := ctrl.ErrorOrFuncWithErr(func() bool { return cache.Size() < maxSize }, ErrCacheFull); err != nil {
38+
// return err // Returns ErrCacheFull if condition fails
39+
// }
40+
//
41+
// # HTTP Server Management
42+
//
43+
// The package helps manage HTTP server lifecycle, particularly graceful shutdown:
44+
//
45+
// // Shutdown an HTTP server with a timeout
46+
// err := ctrl.ShutdownHTTPServer(ctx, server,
47+
// ctrl.WithHTTPShutdownTimeout(5*time.Second))
48+
//
49+
// // Run a server with context-aware shutdown
50+
// errCh := ctrl.RunHTTPServerWithContext(ctx, server,
51+
// func() error { return server.ListenAndServe() },
52+
// ctrl.WithHTTPShutdownTimeout(5*time.Second),
53+
// ctrl.WithHTTPLogger(logger))
54+
//
55+
// # Graceful Shutdown
56+
//
57+
// The package provides robust handling of process termination signals:
58+
//
59+
// // Basic setup
60+
// ctx, cancel := ctrl.GracefulShutdown()
61+
// defer cancel()
62+
//
63+
// // With custom configuration
64+
// ctx, cancel := ctrl.GracefulShutdown(
65+
// ctrl.WithTimeout(30*time.Second),
66+
// ctrl.WithSignals(syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP),
67+
// ctrl.WithExitCode(2),
68+
// ctrl.WithOnShutdown(func(sig os.Signal) {
69+
// log.Printf("shutting down due to %s signal", sig)
70+
// database.Close()
71+
// }),
72+
// ctrl.WithOnForceExit(func() {
73+
// log.Printf("force exiting after timeout")
74+
// }),
75+
// ctrl.WithLogger(logger))
76+
//
77+
// # Best Practices
78+
//
79+
// Use assertions for internal invariants that should never fail in correct code:
80+
//
81+
// ctrl.Assert(len(buffer) >= headerSize) // Internal invariant
82+
//
83+
// Use ErrorOr variants for validating external input or recoverable conditions:
84+
//
85+
// if err := ctrl.ErrorOr(len(userInput) < maxLength); err != nil {
86+
// return err // External input validation
87+
// }
88+
//
89+
// For HTTP servers, combine graceful shutdown with server lifecycle management:
90+
//
91+
// ctx, cancel := ctrl.GracefulShutdown(ctrl.WithTimeout(10*time.Second))
92+
// defer cancel()
93+
// errCh := ctrl.RunHTTPServerWithContext(ctx, server, server.ListenAndServe)
94+
// if err := <-errCh; err != nil {
95+
// log.Fatalf("server error: %v", err)
96+
// }
297
package ctrl

http_shutdown_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ func TestShutdownHTTPServer(t *testing.T) {
2929

3030
// start the server
3131
go func() {
32-
err := server.Serve(listener)
33-
if err != nil && !errors.Is(err, http.ErrServerClosed) {
34-
t.Errorf("unexpected server error: %v", err)
32+
serveErr := server.Serve(listener)
33+
if serveErr != nil && !errors.Is(serveErr, http.ErrServerClosed) {
34+
t.Errorf("unexpected server error: %v", serveErr)
3535
}
3636
}()
3737

0 commit comments

Comments
 (0)