Conversation
181c1c9 to
b4c332f
Compare
6ed9288 to
9bf8642
Compare
|
Hey, sorry for the late reply, Codama v2 is going to drop the casing requirement for identifiers so perhaps we shouldn't add more casing helpers here? |
Same shape as CamelCaseString: same derives, new<T: AsRef<str>>, the String and &str conversions, Deref and AsRef, and the tests in the same order. Word boundaries deliberately do not follow CamelCaseString. to_camel_case reads a run of uppercase letters as one word, turning tokenAMint into tokenAmint and SPLToken into spltoken. In snake_case that would give token_amint and spltoken, which make poor identifiers, and tokenAMint and tokenBMint show up often enough in real PDA seeds to matter. So an uppercase run splits before its last letter when a lowercase letter follows. Digits carry the case of whatever preceded them, so a digit ends a word only when an uppercase letter follows a lowercase run. seed2Bump gives seed2_bump while mint_2, this123 and 2Mint are left alone. That combination matches heck::ToSnakeCase, checked by differential fuzzing over 1,000,021 ASCII inputs with zero mismatches. On a further 400,000 Unicode inputs the only divergence is a word-final Greek capital sigma, which heck lowercases to the final form while this lowercases per character, as to_camel_case does. So codama-idl#86 can drop the dependency.
9bf8642 to
72e31f0
Compare
|
Agreed, closing. The point of this PR was to match CamelCaseString for consistency, but #129 drops the casing mandate and CamelCaseString is going with it, so adding a second casing helper to the node model is the wrong direction now. Landing it right before the 1.0.0 freeze would also mean maintaining it through 1.x while it's already obsolete in v2. One piece is worth keeping separate though. #129 leaves casing to the renderers, and v2 widens the input: transferTokens, transfer_tokens and TransferTokens all become valid identifiers. A Rust codegen still has to normalize whatever it gets, which is what #86 wanted to_snake_case for. That belongs in codama-syn-helpers next to snake_case_ident, where #86 already had it. If it saves #86 from pulling in heck, the function on this branch works as a private helper there. It matches heck::ToSnakeCase on ASCII, fuzzed over a million inputs with zero mismatches, and the only divergence I hit was a word-final Greek capital sigma. Up to whoever picks up #86. |
Adds
SnakeCaseStringtocodama-nodesso #86 can drop itsheckdependency. Suggested here: #86 (comment)Same shape as
CamelCaseString: same derives,new<T: AsRef<str>>, theStringand&strconversions,DerefandAsRef, and the tests are laid out in the same order.Word boundaries do not follow
CamelCaseString, and that is the one thing worth a close look.to_camel_casetreats a run of uppercase letters as a single word, so it turnstokenAMintintotokenAmintandSPLTokenintospltoken. Carrying that into snake_case would givetoken_amintandspltoken, which are poor Rust identifiers, andtokenAMint/tokenBMintare common enough in real PDA seeds to matter. So an uppercase run splits before its last letter when a lowercase letter follows it:to_camel_casereadingtokenAMinttoken_a_minttoken_amintSPLTokenspl_tokenspltokenpoolNFTMintpool_nft_mintpool_nftmintHTTPServerhttp_serverhttpserverSCREAMING_SNAKEscreaming_snakescreaming_snakeDigits carry the case of whatever preceded them, so a digit ends a word only when an uppercase letter follows and the run started from lowercase.
seed2Bumpgivesseed2_bump, whilemint_2,this123and2Mintare left alone.That combination matches
heck::ToSnakeCase. I checked it by differential fuzzing againstheck: 1,000,021 ASCII inputs over letters, digits,_,-,.and spaces plus an uppercase-heavy pass aimed at the acronym rule, zero mismatches, andto_snake_caseis idempotent over the same corpus. A further 400,000 inputs over an alphabet of expanding-lowercase, titlecase, Greek and CJK characters turned up exactly one divergence class:hecklowercases a word-finalΣtoς, while this lowercases per character, asto_camel_casedoes, and givesσ. Every divergence in that run contained aΣand none contained anything else, and it cannot reach a Rust identifier, so #86 can swapheck::ToSnakeCasefor this without a change in behaviour.One caveat on composition. Convert from the original string, not from a
CamelCaseString.to_camel_casefolds an uppercase run into one word and drops the separator before a digit, so by thentokenAMintis alreadytokenAmintandmint_2is alreadymint2, and no snake-case pass can recover those boundaries. #86 is unaffected because seed names arrive as a rawStringfrom the#[codama(seed(name = "..."))]attribute (SeedDirectiveType::Linked(String)andVariable { name: String, .. }), never as a node name. Anything that walks node names, which are allCamelCaseString, gets the collapsed reading. The doc comment says so.Worth flagging for the record: this does not match
snakeCasefrom@codama/fragmentson uppercase runs, because that helper inserts a break before every uppercase letter and yieldss_p_l_tokenandh_t_t_p_server. It does agree with the JS helper on the single-letter case,tokenAMinttotoken_a_mint. If you would rather this mirror the JS helper exactly, say so and I will switch it.