namecode

Punycode for variable names

Reversible encoding from arbitrary Unicode to valid identifiers. O(n). No dependencies beyond unicode-ident.

Inputencode()
foofooAlready valid — identity
cafécaféValid Unicode identifier — identity
hello world_N_helloworld__fa0bSpace is not XID_Continue
foo-bar_N_foobar__da1dHyphen is not XID_Continue
123foo_N_123fooDigit is not XID_Start
_N_test_N__N_testPrefix collision

Identity

Valid identifiers pass through unchanged. encode(s) == s when s is already UAX 31 and does not start with _N_.

Roundtrip

decode(encode(s)) == s whenever s was encoded; a passthrough stands for itself. encode_forced(decode(t)) == t for every canonical encoding t.

Injective

Distinct strings get distinct encodings, so nothing is ever lost. Encoding an already-encoded string encodes it again rather than passing it through.

Cross-language

Output conforms to UAX 31. Valid in Rust, Go, Python, and JavaScript.

cargo add namecode
use namecode::{encode, decode};

let encoded = encode("hello world");
assert_eq!(encoded, "_N_helloworld__fa0b");
assert_eq!(decode(&encoded).unwrap(), "hello world");