Skip to content

Commit 7ee32be

Browse files
committed
chore: adding AGENTS.md+CLAUDE.md
1 parent fc7be90 commit 7ee32be

File tree

2 files changed

+143
-0
lines changed

2 files changed

+143
-0
lines changed

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CLAUDE.md

CLAUDE.md

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
`samber/ro` is a Go implementation of the ReactiveX spec — a library for reactive/stream programming with Observables, Observers, and Operators. It uses Go 1.18+ generics extensively. The library is v0 and follows SemVer strictly.
8+
9+
## Build & Test Commands
10+
11+
```bash
12+
make build # Build all modules
13+
make test # Run all tests with race detector
14+
make lint # Run golangci-lint + license header check
15+
make lint-fix # Auto-fix lint issues
16+
make bench # Run benchmarks
17+
make coverage # Generate coverage report (cover.html)
18+
```
19+
20+
Run a single test:
21+
```bash
22+
go test -race -run TestFunctionName ./...
23+
```
24+
25+
Run tests for a specific plugin:
26+
```bash
27+
cd plugins/encoding/json && go test -race ./...
28+
```
29+
30+
## Multi-Module Architecture
31+
32+
This is a **Go workspace** (`go.work`) with many independent modules. The root module is `github.com/samber/ro`. Each plugin under `plugins/` has its own `go.mod`. Some plugins are commented out in `go.work` because they require newer Go versions.
33+
34+
The SIMD plugin (`plugins/exp/simd`) requires `GOEXPERIMENT=simd` and `GOWORK=off` to build/test.
35+
36+
## Code Layout
37+
38+
- **Root package (`ro`)** — Core types and all built-in operators
39+
- **`internal/`** — Internal helpers: `xsync` (mutex wrappers), `xatomic`, `xrand`, `xtime`, `xerrors`, `constraints`
40+
- **`testing/`** — Package `rotesting` with `AssertSpec[T]` interface for fluent test assertions
41+
- **`plugins/`** — Each plugin is a separate Go module with its own `go.mod`. Categories: encoding, observability, rate limiting, I/O, data manipulation, etc.
42+
- **`ee/`** — Enterprise Edition (separate license). Contains `otel` and `prometheus` plugins, plus licensing infrastructure
43+
- **`examples/`** — Working example applications (each is its own module)
44+
- **`docs/`** — Docusaurus documentation site. Has its own `CLAUDE.md` for doc-writing conventions
45+
46+
## Operator Pattern
47+
48+
All chainable operators follow this signature pattern:
49+
```go
50+
func OperatorName[T any](params) func(Observable[T]) Observable[R]
51+
```
52+
53+
Example:
54+
55+
```go
56+
func Filter[T any](predicate func(item T) bool) func(Observable[T]) Observable[T] {
57+
return func(source Observable[T]) Observable[T] {
58+
return NewUnsafeObservableWithContext(func(subscriberCtx context.Context, destination Observer[T]) Teardown {
59+
sub := source.SubscribeWithContext(
60+
subscriberCtx,
61+
NewObserverWithContext(
62+
func(ctx context.Context, value T) {
63+
ok := predicate(value)
64+
if ok {
65+
destination.NextWithContext(ctx, value)
66+
}
67+
},
68+
destination.ErrorWithContext,
69+
destination.CompleteWithContext,
70+
),
71+
)
72+
73+
return sub.Unsubscribe
74+
})
75+
}
76+
}
77+
```
78+
79+
They return a function that transforms one Observable into another, enabling composition via `Pipe()`.
80+
81+
### Operator Variant Suffixes
82+
83+
Most operators have variants created by combining these suffixes:
84+
85+
- **`I`** — Adds an `index int64` parameter to the callback (e.g., `FilterI`, `MapI`)
86+
- **`WithContext`** — Adds `context.Context` to the callback signature (e.g., `FilterWithContext`, `MapWithContext`)
87+
- **`Err`** — The callback can return an `error` that terminates the stream (e.g., `MapErr`)
88+
89+
These suffixes combine in a fixed order: **`Err` + `I` + `WithContext`**. Examples:
90+
- `Map``MapI``MapWithContext``MapIWithContext`
91+
- `MapErr``MapErrI``MapErrWithContext``MapErrIWithContext`
92+
93+
Other naming patterns:
94+
- **Numbered suffixes** (2, 3, 4, 5...) — Arity variants for multi-observable operators (e.g., `CombineLatest2`, `Zip3`, `MergeWith1`). Also used for type-safe pipe: `Pipe1` through `Pipe11`
95+
- **`Op`** — Operator version of a creation function, for use inside `Pipe()` (e.g., `PipeOp`)
96+
97+
## Core Operators vs Plugins
98+
99+
**Core operators** live in the root `ro` package and have no external dependencies beyond `samber/lo`. They cover the standard ReactiveX operator categories (creation, transformation, filtering, combining, etc.) and are imported as `github.com/samber/ro`.
100+
101+
**Plugins** are separate Go modules under `plugins/`, each with its own `go.mod` and third-party dependencies. They follow the same `func(Observable[T]) Observable[R]` signature pattern and compose with core operators via `Pipe()`. Plugins wrap external libraries (e.g., `zap`, `sentry`, `fsnotify`) or provide domain-specific operators (e.g., JSON encoding, CSV I/O, rate limiting). Import them separately, e.g., `github.com/samber/ro/plugins/encoding/json`.
102+
103+
## Testing Conventions
104+
105+
- Tests use `testify` assertions and `go.uber.org/goleak` for goroutine leak detection
106+
- Test files follow Go convention: `foo_test.go` alongside `foo.go`
107+
- Example tests use `_example_test.go` suffix
108+
- The `plugins/testify` plugin provides reactive stream assertion helpers
109+
110+
Typical test pattern — use `Collect()` to gather all emitted values and assert:
111+
112+
```go
113+
func TestOperatorTransformationMap(t *testing.T) {
114+
t.Parallel()
115+
is := assert.New(t)
116+
117+
values, err := Collect(
118+
Map(func(v int) int { return v * 2 })(Just(1, 2, 3)),
119+
)
120+
is.Equal([]int{2, 4, 6}, values)
121+
is.NoError(err)
122+
123+
// Test error propagation
124+
values, err = Collect(
125+
Map(func(v int) int { return v * 2 })(Throw[int](assert.AnError)),
126+
)
127+
is.Equal([]int{}, values)
128+
is.EqualError(err, assert.AnError.Error())
129+
}
130+
```
131+
132+
Always test edge cases with `Empty[T]()` (empty source) and `Throw[T](assert.AnError)` (error source). Also test early unsubscription, context propagation, and context cancellation where applicable.
133+
134+
## Contributing Conventions
135+
136+
- **Operator naming**: Must be self-explanatory and respect ReactiveX/RxJS standards. Inspired by https://reactivex.io/documentation/operators.html and https://rxjs.dev/api
137+
- **Context propagation**: Operators must not break the context chain. Always use `SubscribeWithContext`, `NextWithContext`, `ErrorWithContext`, `CompleteWithContext`. The `WithContext` variant callbacks receive and return a `context.Context`
138+
- **Callback naming**: `predicate` for bool-returning callbacks, `transform`/`project` for value-transforming callbacks, `callback` for void callbacks
139+
- **Variadic operators**: Some operators accept `...Observable[T]` for flexibility (e.g., `Zip`, `Merge`, `MergeWith`)
140+
- **Type aliases**: Some operators use `~[]T` constraints to accept named slice types, not just `[]T` (e.g., `Flatten`)
141+
- **Documentation**: Each operator needs a Go Playground link in its comment, a markdown doc in `docs/data/`, an example in `ro_example_test.go`, and an entry in `docs/static/llms.txt`
142+
- **License headers**: All `.go` files require license headers (`licenses/header.apache.txt` for open source, `licenses/header.ee.txt` for `ee/`). Run `make lint` to verify

0 commit comments

Comments
 (0)