37 lines
1.3 KiB
Python
37 lines
1.3 KiB
Python
"""Rust identifier hygiene."""
|
|
|
|
from __future__ import annotations
|
|
|
|
# Strict and reserved-for-future-use keywords from the Rust reference's
|
|
# keyword table, 2021 edition.
|
|
_RUST_KEYWORDS = frozenset({
|
|
"as", "break", "const", "continue", "crate", "else", "enum", "extern",
|
|
"false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod",
|
|
"move", "mut", "pub", "ref", "return", "self", "Self", "static", "struct",
|
|
"super", "trait", "true", "type", "unsafe", "use", "where", "while",
|
|
"async", "await", "dyn",
|
|
"abstract", "become", "box", "do", "final", "macro", "override", "priv",
|
|
"typeof", "unsized", "virtual", "yield",
|
|
"try",
|
|
"union", # contextual, but escaping it costs nothing
|
|
})
|
|
|
|
|
|
def to_rust_ident(name: str) -> str:
|
|
"""`name`, or `r#name` when it collides with a keyword.
|
|
|
|
Field casing is left alone — the caller owns that convention.
|
|
"""
|
|
if name in _RUST_KEYWORDS:
|
|
return f"r#{name}"
|
|
return name
|
|
|
|
|
|
def to_rust_variant_ident(python_member_name: str) -> str:
|
|
"""`SONNET` -> `Sonnet`, `MY_OPTION` -> `MyOption`.
|
|
|
|
Enum declarations and enum-valued defaults are emitted from different
|
|
places and both call this; two implementations would disagree.
|
|
"""
|
|
return "".join(part.capitalize() for part in python_member_name.split("_") if part)
|