Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

canonical.gno

2.07 Kb · 63 lines
 1package users
 2
 3import (
 4	"strings"
 5
 6	"gno.land/p/nt/bptree/v0"
 7)
 8
 9// canonicalStore maps canonicalized full names to the original name
10// that was registered. Keyed by the result of Canonicalize.
11//
12// Multiple controllers (namereg/v0, future registries, governance,
13// genesis bootstrapping) all share this single store via RegisterUser
14// and the bypass variant RegisterUserIgnoreCanonical. Cross-controller
15// canonical-collision detection is uniform and atomic with the
16// nameStore write.
17var canonicalStore = bptree.NewBPTree32()
18
19// Canonicalize returns the canonical form of a name. The substitutions
20// collapse single-character visual confusables that arise across the
21// allowed [a-z0-9] character set, and strip the three separators that
22// can sneak between identical alphanumeric runs.
23//
24//   - {l, i, 1} → i
25//   - {0, o}    → o
26//   - {-, ., _} → stripped
27//   - all other characters unchanged
28//
29// CONTRACT: stable. Future controllers that want to share this
30// canonical namespace MUST use this exact function — do not roll your
31// own. Adding new substitutions later is a breaking change because it
32// would silently re-key existing entries in canonicalStore.
33//
34// Input contract: ASCII-only. r/sys/users.validateName already rejects
35// non-ASCII at the registration boundary, so by the time a name reaches
36// Canonicalize through the standard write path it is guaranteed to be
37// ASCII. Direct callers from other realms must honor this contract;
38// non-ASCII bytes are passed through as-is and will produce undefined
39// collision behavior.
40//
41// Multi-char confusables (m↔rn, nn↔m, cl↔d) are NOT canonicalized.
42// They require fixed-point substring substitution rounds, which is out
43// of scope for the unified store.
44//
45// Pure: no state access. Safe to call from anywhere.
46func Canonicalize(name string) string {
47	var b strings.Builder
48	b.Grow(len(name))
49	for i := 0; i < len(name); i++ {
50		c := name[i]
51		switch c {
52		case 'l', '1':
53			b.WriteByte('i')
54		case '0':
55			b.WriteByte('o')
56		case '-', '.', '_':
57			// strip
58		default:
59			b.WriteByte(c)
60		}
61	}
62	return b.String()
63}