# aliased
Give long values (like keys, hashes, IDs, or binary blobs) a short, human-friendly alias which will be used in all Debug output.
When you `format!("{:?}", thing)` and `thing` contains long opaque values, the output becomes noisy and hard to read.
`aliased` lets you register aliases for specific values up front, then rewrites the `Debug` (or `{:#?}`) output to substitute those values with the aliases.
See this [example](examples/structs.rs) for a compelling use case.
## Examples
### Basic usage
```rust
use aliased::*;
#[derive(Debug)]
struct Key([u8; 32]);
let a = Key([1; 32]);
let b = Key([2; 32]);
let c = Key([3; 32]);
// The default Debug output is noisy.
assert_eq!(format!("{:?}", a), "Key([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])");
// Without applying any aliases, the `aliased()` output is unchanged.
assert_eq!(format!("{:?}", a.aliased()), "Key([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])");
// You can apply a numbered alias:
a.alias_numbered();
b.alias_numbered();
c.alias_numbered();
assert_eq!(format!("{:?}", (a, b, c).aliased()), "(⟪K|#000⟫, ⟪K|#001⟫, ⟪K|#002⟫)");
// Or you can apply a named alias (even after already applying a numbered alias)
a.alias_named("alice");
b.alias_named("bob");
c.alias_named("carol");
assert_eq!(format!("{:?}", (a, b, c).aliased()), "(⟪K|alice⟫, ⟪K|bob⟫, ⟪K|carol⟫)");
// Works for pretty-printed output too.
// Without `aliased()`, this output would be 34 lines long!
assert_eq!(
format!("{:#?}", (a, b, c).aliased()),
"
(
⟪K|alice⟫,
⟪K|bob⟫,
⟪K|carol⟫,
)
".trim()
);
```
## Two flavors
Selected via Cargo features. With neither enabled, the whole API still
compiles but becomes a [no-op](#no-op-builds).
### `global` (default)
A process-wide static `AliasContext` is used implicitly — nothing to thread
through call sites.
```rust
use aliased::*;
#[derive(Debug)]
struct Key([u8; 32]);
Key::alias_prefix("K");
let a = Key([1; 32]);
let b = Key([2; 32]);
a.alias_named("alice");
b.alias_named("bob");
assert_eq!(format!("{:?}", (a, b).aliased()), "(⟪K|alice⟫, ⟪K|bob⟫)");
```
### `contextual`
You pass an explicit `&AliasContext` to every call. Useful when you want
isolated registries — for example, one per test.
```rust
use aliased::AliasContext;
use aliased::contextual::*;
// ...same API as global, but each method takes `&ctx`.
let ctx = AliasContext::new();
#[derive(Debug)]
struct Key([u8; 32]);
Key::alias_prefix(&ctx, "K");
let a = Key([1; 32]);
let b = Key([2; 32]);
a.alias_named(&ctx, "alice");
b.alias_named(&ctx, "bob");
assert_eq!(format!("{:?}", (a, b).aliased(&ctx)), "(⟪K|alice⟫, ⟪K|bob⟫)");
```
Enable in `Cargo.toml`:
```toml
[dependencies]
aliased = { version = "0.1", default-features = false, features = ["contextual"] }
# or both:
aliased = { version = "0.1", features = ["contextual"] }
```
## How it works
`alias_named` / `alias_numbered` store a mapping from the value's
`format!("{:?}", v)` string to a chosen alias. When you print
`value.aliased(..)`, the crate formats `value` with `Debug`, then runs
string substitution to replace each registered debug fragment with its
alias.
All registered values are combined into a single matcher so each print
scans the output once rather than once per registered alias: plain `{:?}`
output uses an [Aho–Corasick](https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm)
automaton, and pretty `{:#?}` output uses one alternation regex whose
patterns tolerate the indentation `{:#?}` introduces, so nested values
still get aliased. The matcher is rebuilt lazily after a registration and
reused across prints. Matches resolve longest-first to reduce the chance
that a shorter registered value clobbers a longer one that contains it.
## Features
| `global` | yes | exposes `aliased::Aliasing`, `Aliased`, `global_ctx` |
| `contextual` | no | exposes `aliased::contextual::{Aliasing, Aliased}` |
| `tracing` | yes | emits `tracing::warn!` for misuse / collisions |
### No-op builds
Building with neither `global` nor `contextual` is **not** an error. The whole
API — both the global-shaped methods and `aliased::contextual::*` — still
compiles, but every call is a no-op: `value.aliased(..)` formats with plain
`Debug` (the `{:#?}` alternate form still pretty-prints) and registration calls
do nothing. The substitution machinery and its `aho-corasick` / `regex`
dependencies are not compiled.
This lets you keep `aliased` call sites in a production app while turning the
feature off:
```toml
aliased = { version = "0.1", default-features = false }
```
## When to use this
This is a debugging / logging aid. Each print makes a single O(m) pass over
the formatted string (after a one-time matcher build amortized across
prints) — fine for logs, not for hot paths.
## License
MIT OR Apache-2.0