You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

83 lines
6.7 KiB

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Workspace layout
Go workspace (`go.work`) with two modules:
- Root module `active` (`go.mod`): currently empty placeholder.
- Module `core` at `src/core/` (`src/core/go.mod`): all real code lives here. Imports inside `core` use the bare module name, e.g. `import "core/btes"`, `import "core/errs"`.
Go version is 1.26 (uses generics throughout, including type parameters on slice aliases).
## Commands
All Go commands must be run with the working directory inside `src/core` (or use `-C src/core`), since that is the active module in the workspace.
```bash
# Build everything in the core module
go -C src/core build ./...
# Run all tests across packages
go -C src/core test ./...
# Run tests in one package (note: tests live in the `test` subpackage)
go -C src/core test ./btes/test
go -C src/core test ./errs/test
go -C src/core test ./opts/test
# Run a single test by name
go -C src/core test ./errs/test -run TestValidateCodeAcceptsValidCodes
# Vet / format
go -C src/core vet ./...
gofmt -w src/core
```
Tests use Go's standard `testing` package (no external assertion libraries) and live in a sibling `test/` directory using a separate `package test` — i.e. external (black-box) tests. New tests should follow the same convention rather than `*_test.go` files alongside the code.
## Package architecture
The codebase is structured as a small set of foundational primitives that other packages (and future modules) will build on. Read these in order — `errs` depends on `btes`, and most callers of `errs` will use `opts` for configuration.
### `core/btes` — ASCII byte operations via LUT
`btes` implements zero-allocation, O(1) ASCII classification and case conversion using a 256-entry lookup table. The big-picture design:
- `Attr` (`uint8`) is a bitmask of properties. Predefined bits include `ASCIIAttrControl`, `ASCIIAttrPrintable`, `ASCIIAttrAlpha`, `ASCIIAttrAlphaUpper`, `ASCIIAttrAlphaLower`, `ASCIIAttrDigit`, `ASCIIAttrHex`, plus combined masks like `ASCIIAttrAlphaNum`.
- `Attrs` is a `[256]Attr` LUT. Generic `SetRange` / `AddRange` / `DelRange` / `ContainsAll` / `MatchesAll` (and the `Any` variants) operate on it without allocations.
- `ASCIIAttrs` wraps an initialized `Attrs` table and exposes domain helpers (`IsAlpha`, `IsDigit`, `EqualFold`, `ToUpper`, `Upperize`, etc.). It is immutable after construction and safe to share across goroutines.
- A package-level singleton is exposed via `btes.ASCII()` — use that rather than constructing a new one. Two case-conversion styles exist: `Upperize` / `Lowerize` mutate in place; `ToUpper` / `ToLower` use a pre-scan to return the input slice unchanged when no transformation is needed (zero-copy fast path).
### `core/errs` — hierarchical error codes with a separate cause plane
The error system is built around a single string-typed `Code` with a strict format: `module::segment[.segment...]`, e.g. `auth::user.login.failed`. Key design points:
- **Module + code**: `Module` is the namespace ("auth", "db"). Use `MustRegisterModule("name")` at package init for global uniqueness (it panics on duplicates), or `MustModule` for a non-registered local module. Build codes with `module.MustCode("login")` and extend hierarchies with `code.Sub("failed")`.
- **Validation** (`ValidateCode`, `ValidateModule`) is enforced strictly: only `[a-z0-9_]`, `_` may not touch other separators or segment ends, exactly one `::`, and `.` only allowed after `::`. The validator uses `btes.ASCII()` for fast byte classification.
- **Identity vs lineage**: `Code.Is` is strict equality; `Code.IsDescendantOf` and `IsOrDescendantOf` walk the dotted hierarchy (matching prefix + `.` boundary, so `user_group` is NOT a descendant of `user`). `Matches(err, target)` is the package-level helper that handles wrapping plus lineage in one shot.
- **Cause is a separate plane from Unwrap**. This is the most important invariant in the package:
- `Unwrap` carries the public identity chain — what `errors.Is` / `errors.As` see. It supports both `Unwrap() error` (chain) and `Unwrap() []error` (siblings, e.g. `MultipleError`).
- `Causer.Cause()` carries diagnostic origin information that should NOT change the error's identity. Cause is **always singular**: a single error has a single reason. If several errors arose at the same semantic level, use `MultipleError` (siblings on the public plane), not multiple causes.
- Example: a `user::cant_change_user` wrapping a `database::invalid_credentials` cause should still be `errors.Is(err, user::cant_change_user)`, not the database code. Use `WithCause` / `AddCause` to attach causes; use `GetCause` / `FindCause` / `HasCause` to inspect them (they traverse both `Unwrap() error` and `Unwrap() []error` chains). Keep this separation when adding new error types.
- **MultipleError** is a flat sibling collection (NOT a tree). `Unwrap() []error` returns the slice so `errors.Is`/`As` work across siblings; there is no causal relationship between elements. `MakeMultipleError` cleans nil entries.
- **PanicError** wraps recovered panics; pre-defined codes (`ErrInvalidModule`, `ErrInvalidCode`, `ErrNilError`, etc.) live in `a.go` under the reserved `errs::` module.
- **Adder pattern**: `Add(base, composers...)` applies a chain of `Adder`s to enrich a base error (e.g. `errs.Add(base, errs.AddCause(orig))`). Use this rather than ad-hoc wrappers when composing decorations.
### `core/opts` — functional options
Generic functional-options pattern in two flavors:
- `Option[T]` / `Options[T]` — infallible. `Apply(*T)` mutates; nil options are treated as no-ops to allow conditional composition.
- `TryOption[T]` / `TryOptions[T]` — can return an error (validation, resource acquisition). `TryOptions.Apply` short-circuits on first error WITHOUT rolling back prior mutations — opts are not transactional.
Use these types as the standard signature for configurable constructors instead of inventing per-package option types.
## Conventions
- **Source headers**: every `.go` file starts with the long Spanish/Elastic-License v2.0 header block. When creating new files, copy the header from a sibling file and update only `Fichero` (filename), `Package`, and `Creado` (date). Don't omit it.
- **Doc comments are in Spanish.** Match the existing language and tone when adding new exported docs. Code identifiers remain English.
- **No external dependencies.** The module is currently stdlib-only; do not introduce third-party deps without an explicit reason.
- **`Must...` vs `Validate...`**: by convention, `MustX` panics on invalid input (intended for package init / constants), and `ValidateX` returns an error (intended for runtime input). Preserve this split when adding new constructors.

Powered by TurnKey Linux.