Expand description
Compile-time case conversion for keys and variant names.
A declaration may name a case rule, and every key it does not spell out
explicitly is then converted from the Rust name during const evaluation.
macro_rules! cannot manipulate a string, but a const fn can, and the
rest of the machinery already takes a computed key: Keys::KEYS is an
ordinary const expression and KeyMap::build runs in const context.
§The rule
Conversion is not “snake to camel”. A name is split into words and the
words are respelled, which is what lets one rule serve both a field name
(byte_offset) and a variant name (ByteOffset) without being told which
it is looking at. A word begins:
- after one or more
_, which are separators and are never emitted; - at a capital that follows a lower-case letter or a digit, so
byteOffsetsplits asbyte+Offset; - at the last capital of a run that is followed by a lower-case letter, so
HTTPUrlsplits asHTTP+Urlrather than at every capital.
Two consequences are worth stating outright, because they are the ones that surprise people:
A leading or trailing _ is dropped. In Rust those are the “unused”
marker and the keyword escape, so type_ converts to type and _scratch
to scratch. Neither underscore is part of the name the wire knows.
A run of capitals loses its capitals. HTTPUrl under "camelCase" is
httpUrl, not hTTPUrl and not httpURL, because the rule respells whole
words. A format that really wants httpURL should say so with an explicit
"httpURL" => http_url, which is the escape hatch for every name the rule
reads differently than you do.
A byte above ASCII passes through untouched, since this rule has no case
for it, and a non-ASCII identifier therefore keeps its spelling and stays
valid UTF-8. It never begins a word on its own account, only a capital and
a separator do that, but it does end one: caféBar splits as
café + Bar, so the B keeps its case rather than being run into
the word before it.
Two names whose converted keys collide are a compile error, from the
duplicate check KeyMap::build already performs.
A raw identifier loses its r# before the rule sees it, so a field
written r#type has the key type, and a rule respells type. The
prefix is how Rust spells a name that collides with a keyword, not part of
the name the wire knows.
§Coming from serde
The eight spellings are serde’s. The rule behind them is not, and three differences will change your bytes:
"lowercase"and"UPPERCASE"keep serde’s underscores and these do not. Serde’s field rules take the name to be snake_case already, solowercaseis the identity andUPPERCASEisto_ascii_uppercase:byte_offsetstaysbyte_offsetand becomesBYTE_OFFSET. Here they mean what they say, and givebyteoffsetandBYTEOFFSET.- Acronyms in a variant name. Serde’s variant rules break at every
capital, so
HTTPProxyunder"snake_case"ish_t_t_p_proxy. Here it ishttp_proxy. - Serde has two rules and this has one. Which of serde’s applies depends on whether the name is a field or a variant, so a field that is not snake_case, or a variant that is not PascalCase, is converted by a rule that was not written for it. One rule over words has no such seam.
Everything else agrees: for a snake_case field and an acronym-free PascalCase variant, the other six rules land on the string serde lands on.
Enums§
- Style
- How a converted name is spelled.
Functions§
- as_str
- The key macros’ const
from_utf8, which is how a computed key reaches the&'static strthe rest of the crate wants without anunsafeblock. - cased
nameconverted tostyle.- cased_
len - The length of
nameconverted tostyle. - style
- The
Stylea declaration named. - unraw
- A field or variant name with the
r#of a raw identifier removed.