Skip to content

Commit 42880a3

Browse files
schighclaude
andcommitted
Modernize str as a pipeline-first string toolkit
Drop all hashing functions (MD5, SHA1, SHA256, HMAC, CRC32, CRC64) and global mutable state. Rebuild from scratch around a func(string) string design language where every transformation is composable via str.Pipe. New API: Pipe, PipeErr, ToSnakeCase, ToCamelCase, ToPascalCase, ToKebabCase, ToTitleCase, ToScreamingSnake, PadLeft, PadRight, Truncate, Substring, Wrap, Reverse, CollapseWhitespace, SlugifyASCII, Levenshtein, JaroWinkler. All rune-based, zero dependencies, no panics, thread-safe. Adds go.mod, GitHub Actions CI, benchmarks, fuzz testing, 97.5% coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 361193c commit 42880a3

38 files changed

Lines changed: 1586 additions & 1136 deletions

.github/workflows/ci.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
go-version: ['1.24']
15+
steps:
16+
- uses: actions/checkout@v4
17+
- uses: actions/setup-go@v5
18+
with:
19+
go-version: ${{ matrix.go-version }}
20+
- run: go test -race -coverprofile=coverage.out ./...
21+
- run: |
22+
COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | tr -d '%')
23+
echo "Coverage: ${COVERAGE}%"
24+
if (( $(echo "$COVERAGE < 95" | bc -l) )); then
25+
echo "Coverage ${COVERAGE}% is below 95% threshold"
26+
exit 1
27+
fi
28+
- run: go vet ./...
29+
30+
bench:
31+
runs-on: ubuntu-latest
32+
steps:
33+
- uses: actions/checkout@v4
34+
- uses: actions/setup-go@v5
35+
with:
36+
go-version: '1.24'
37+
- run: go test -run=^$ -bench=. -benchmem ./...

.travis.yml

Lines changed: 0 additions & 3 deletions
This file was deleted.

CONTRIBUTING.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Contributing to str
2+
3+
## Design Language
4+
5+
Every transformation function in this library has the signature `func(string) string`. This is the library's identity. It makes every function composable with `Pipe`.
6+
7+
## API Design Rules
8+
9+
Three calling patterns, one decision tree:
10+
11+
1. **Direct** (`func(string) string`) — no configuration needed.
12+
- Examples: `ToSnakeCase`, `Reverse`, `CollapseWhitespace`
13+
14+
2. **Partial application** (`func(params) func(string) string`) — 1-2 required parameters.
15+
- Examples: `PadLeft(pad, width)`, `Truncate(maxLen, ellipsis)`
16+
17+
3. **Functional options** (`func(required, opts...) func(string) string`) — required param + optional modifiers.
18+
- Examples: `Wrap(width, opts...)`
19+
20+
**Decision tree for new functions:**
21+
- No config? -> Direct
22+
- All params required, 1-2 params? -> Partial application
23+
- Required + optional behavior? -> Functional options
24+
- Not a string transformation? -> Standalone, document clearly
25+
26+
## Naming Conventions
27+
28+
- Transformers: verb form (`Truncate`, `Reverse`, `Wrap`)
29+
- Case conversions: `To{Format}` (`ToSnakeCase`, `ToCamelCase`)
30+
- Options: `With{Thing}` for modifiers (`WithLineBreak`), bare name for strategies (`WrapAfterWord`)
31+
32+
## Constraints
33+
34+
- **Zero dependencies.** Stdlib only. No test dependencies either.
35+
- **No panics.** Every function handles degenerate input gracefully.
36+
- **Rune-based counting.** All length/width/index parameters count runes, not bytes.
37+
- **Thread safe.** No shared mutable state.
38+
39+
## Testing
40+
41+
- Table-driven tests using stdlib `testing` only.
42+
- Every function needs tests for edge cases (empty string, zero values, multibyte input).
43+
- Every exported function needs a godoc `Example` test.
44+
- Run `go test -race ./...` before submitting.
45+
46+
## File Organization
47+
48+
One concern per file. Test files mirror source files 1:1. New functions go in the file that matches their category, or get a new file if they don't fit.

0 commit comments

Comments
 (0)