# readable-code-core
Language-agnostic core for [readable-code](https://github.com/semanticist21/readable-code) share-code generators: a small chainable string composer plus numeric and random helpers.
This is the shared foundation. For end-user generators, use [`readable-code-english`](https://crates.io/crates/readable-code-english) or [`readable-code-korean`](https://crates.io/crates/readable-code-korean).
## Install
```sh
cargo add readable-code-core
```
## Usage
```rust
use readable_code_core::{code, OsRandom};
let out = code(OsRandom::new()).add("abc").dash().digits(4).build();
// "abc-4821"
```
`build()` is the finalizer. It joins the accumulated fragments and returns the code string.
## Builder
`CodeBuilder` is a chainable string composer. Methods take and return `self`:
- `add(value)` — append an arbitrary string fragment.
- `dash()` — append `-`.
- `digits(length)` — append `length` random decimal digits.
- `nums(length)` — alias for `digits`.
- `add_with(|rng| ...)` — append a fragment built from the builder's own random source (language crates use this to add generated words without owning a second source).
- `build()` — finalize and return the joined `String`.
## Random sources
`RandomSource` is a trait yielding bounded integers in `0..max_exclusive`:
```rust
pub trait RandomSource {
fn gen_below(&mut self, max_exclusive: u32) -> u32;
}
```
Two implementations ship:
- **`OsRandom`** — cryptographically secure, backed by the OS CSPRNG via `getrandom`. This is the default behind the language crates' `word()` / `hangul()` entry points, mirroring the TypeScript `crypto.getRandomValues` default.
- **`SplitMix64`** — fast, seeded, **reproducible but not cryptographically secure**. Use it for deterministic generation in tests/demos (`SplitMix64::new(seed)` or `SplitMix64::from_entropy()`), and pass it through `word_with` / `hangul_with`.
Both use rejection sampling to remove modulo bias. Implement `RandomSource` yourself for custom test doubles.
## Helpers
```rust
use readable_code_core::{digits, pick, SplitMix64};
let mut rng = SplitMix64::new(1);
digits(4, &mut rng); // "3396" — random decimal digits
pick(&["a", "b", "c"], &mut rng); // one element (panics on empty slice)
```
## Concept
These are readable public share codes, not secret tokens. Uniqueness is kept separate from the readable part — store generated codes under a database `UNIQUE` constraint and retry on collision. The builder only composes string fragments; uniqueness, retry, persistence, and denylist policy live outside it.
## License
MIT