140 lines
3.2 KiB
Markdown
140 lines
3.2 KiB
Markdown
Copyright (c) 2026 Micha Hoiting
|
||
|
||
# triplex
|
||
|
||
[](https://git.hoiting.org/micha/triplex/actions)
|
||
<!-- TODO: [](https://github.com/micha/triplex/actions/workflows/ci.yml) -->
|
||
|
||
`triplex` is a deterministic, reversible serial number engine for
|
||
high-integrity identifiers. It maps a compact 32-bit integer space to a
|
||
human-readable serial format:
|
||
|
||
```text
|
||
LLL-NNN-LLC
|
||
```
|
||
|
||
Where:
|
||
- `L` = data letter (`A` – `Z` excluding `F`, `I`, `O`, `Q`, `U`)
|
||
- `N` = number (`100`–`999`)
|
||
- `C` = checksum letter (`A` – `Z`)
|
||
|
||
For instance
|
||
|
||
```text
|
||
ABC-123-DEO
|
||
```
|
||
|
||
## What it provides
|
||
|
||
- 5 data letters
|
||
- 3 numeric digits (`100`–`999`)
|
||
- 1 checksum letter
|
||
- Optional forbidden-triplet letter combination filtering on the first three letters
|
||
- Reversible integer encoding/decoding
|
||
- Deterministic checksum generation
|
||
|
||
Total index capacity: **3,675,690,900** (`21^5 * 900`)
|
||
|
||
## Features
|
||
|
||
- Reversible mapping: `Decode(code) ↔ Encode(index)`
|
||
- Deterministic checksum: `C = 'A' + (index % 26)`
|
||
- Compact index space: fits in `uint`
|
||
- Human-readable serial format
|
||
- Useful for manufacturing, logistics, SaaS identifiers, and audit-safe systems
|
||
|
||
## Example
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
|
||
"git.hoiting.org/micha/triplex/serial"
|
||
)
|
||
|
||
func main() {
|
||
idx := uint(123456)
|
||
|
||
code, err := serial.Encode(idx)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
idx2, err := serial.Decode(code)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
fmt.Println(code)
|
||
fmt.Println(idx == idx2) // true
|
||
}
|
||
```
|
||
|
||
## Notes
|
||
|
||
- The current serial format is `LLL-NNN-LLC`.
|
||
- Forbidden-triplet logic is centralized in `serial.IsForbiddenTriplet` and can be tailored to project rules.
|
||
|
||
## API
|
||
|
||
- `serial.Decode(code string) (uint, error)`
|
||
- Parses and validates `LLL-NNN-LLC`, checks checksum, and returns the deterministic index.
|
||
- `serial.Encode(idx uint) (string, error)`
|
||
- Converts a valid index back to `LLL-NNN-LLC`.
|
||
- `serial.CompleteCode(codeWithoutChecksum string) (string, uint, error)`
|
||
- Takes `LLL-NNN-L` and returns the completed `LLL-NNN-LLC` plus index.
|
||
- `serial.RandomCode(isInUse ...func(uint) bool) (string, uint, error)`
|
||
- Generates a random valid `LLL-NNN-LLC` code and its corresponding index.
|
||
- If provided, the callback is used to skip indices already in use by the client.
|
||
- `serial.RandomCodeWithOptions(options serial.RandomCodeOptions) (string, uint, error)`
|
||
- Configurable variant with `MaxAttempts`, `IsInUse`, and custom `RandomIndex` source.
|
||
|
||
Example with options:
|
||
|
||
```go
|
||
// See the runnable command at ./cmd/triplex-example/main.go
|
||
```
|
||
|
||
## Commands
|
||
|
||
Runnable example command:
|
||
|
||
```bash
|
||
go run ./cmd/triplex-example
|
||
```
|
||
|
||
Using Makefile:
|
||
|
||
```bash
|
||
make run-example
|
||
make build-example
|
||
make test
|
||
```
|
||
|
||
Exported errors:
|
||
|
||
- `serial.ErrInvalidFormat`
|
||
- `serial.ErrInvalidFormatNoChecksum`
|
||
- `serial.ErrInvalidLetter`
|
||
- `serial.ErrForbiddenLetterTriplet`
|
||
- `serial.ErrInvalidNumber`
|
||
- `serial.ErrNumberOutOfRange`
|
||
- `serial.ErrInvalidChecksum`
|
||
- `serial.ErrIndexOutOfRange`
|
||
- `serial.ErrRandomSourceFailed`
|
||
- `serial.ErrNoAvailableIndex`
|
||
|
||
## Testing
|
||
|
||
Run all tests:
|
||
|
||
```bash
|
||
go test ./...
|
||
```
|
||
|
||
## License
|
||
|
||
This project is licensed under the MIT License. See [LICENSE](LICENSE).
|