compose-idents
A macro for generating new identifiers (names of variables, functions, traits, etc.) by concatenating one or more arbitrary parts and applying other manipulations.
It was created as an alternative to macro_rules! that doesn't allow creating new identifiers from the macro arguments
and concat_idents! macro from the nightly Rust, which is limited in capabilities and has not been stabilized
since 2015.
compose_idents!;
assert_eq!;
Features
-
Identifier generation
Identifiers can be generated via concatenation of multiple parts. Arguments of the outer macro definitions and literals are supported for identifier definitions.
-
Functions
Functions can be applied when defining new identifiers for changing case and style.
-
String formatting
Strings can be formatted with
%alias%syntax, which is useful for generating doc-attributes. -
Unique identifier generation
Unique identifiers can be deterministically generated by using the
hash()function, which is seeded uniquely for each invocation of the macro. This might be useful for generating unique global variables.
Usage
This section contains various usage examples. For additional examples, see the tests/ directory of the repository.
Quick start
compose_idents! works by accepting definitions of aliases and a code block where aliases
could be used as normal identifiers. When the macro is expanded, the aliases are replaced with their
definitions:
use compose_idents;
// We generate separate const-functions for each type as a workaround
// since Rust doesn't allow us to use `core::ops::Add` in `const fn`.
gen_const_add!; // Expands into `add_u32()` function.
gen_const_add!; // Expands into `add_u64()` function.
assert_eq!;
assert_eq!;
Generating tests for different types
A practical example for how to auto-generate names for macro-generated tests for different data types:
use compose_idents;
// Generates tests for u32 and &'static str types
generate_frobnicate_test!;
generate_frobnicate_test!;
test_frobnicate_u32;
// Notice - "&'static str" has been turned into just "static_str"
test_frobnicate_static_str;
Reference
This is a complete reference to the functionality of this library split into thematic sections.
Basic alias definition
You can define aliases with the syntax alias = concat(arg1, normalize(arg2), ...), alias = lower(ARG),
alias = arg, etc., where args may be identifiers, string literals, integers, underscores, or any arbitrary sequences
of tokens (like &'static str, My::Enum and so on - such values would be recognized as just tokens):
use compose_idents;
compose_idents!;
assert_eq!;
assert_eq!;
Alias reuse
Aliases could also be reused in definitions of other aliases:
use compose_idents;
compose_idents!;
assert_eq!;
assert_eq!;
Functions
Functions can be applied to the arguments used for the alias definitions:
use compose_idents;
compose_idents!;
assert_eq!;
assert_eq!;
You can find a complete description of all functions below under "Functions" heading.
Casing manipulation
There are multiple functions for altering the naming convention of identifiers:
use compose_idents;
compose_idents!;
assert_eq!;
assert_eq!;
assert_eq!;
Token normalization
normalize() function is useful for making valid identifiers out of arbitrary tokens:
use compose_idents;
compose_idents!;
assert_eq!;
String formatting
Aliases could be used in string formatting with %alias% syntax. This is useful for generating doc-attributes:
use compose_idents;
compose_idents!;
assert_eq!;
Generating unique identifiers
hash() function deterministically hashes the input within a single macro invocation. It means that within the same
compose_idents! call hash(foobar) will always produce the same output. But in another call - the output would be
different (but also the same for the same input).
It could be used to avoid conflicts between identifiers of global variables, or any other items that are defined in global scope.
use compose_idents;
create_static!;
create_static!;
This example roughly expands to this:
use compose_idents;
static __5360156246018494022: u32 = 42;
static __1421539829453635175: u32 = 42;
static __17818851730065003648: u32 = 42;
static __10611722954104835980: u32 = 42;
Concatenating multiple arguments
The concat() function takes multiple arguments and concatenates them together. It provides explicit concatenation
that can be either nested within other function calls or to aggregate results of other function calls:
use compose_idents;
compose_idents!;
assert_eq!;
assert_eq!;
assert_eq!;
Functions
| Function | Description |
|---|---|
upper(arg) |
Converts the arg to upper case. |
lower(arg) |
Converts the arg to lower case. |
snake_case(arg) |
Converts the arg to snake_case. |
camel_case(arg) |
Converts the arg to camelCase. |
pascal_case(arg) |
Converts the arg to PascalCase. |
normalize(tokens) |
Transforms a free-form sequence of tokens into a valid identifier. |
hash(arg) |
Hashes the arg deterministically within a single macro invocation. |
concat(arg1, arg2, ...) |
Concatenates multiple arguments into a single identifier. |
Deprecation policy
-
As a general rule old functionality is not removed abruptly, but rather deprecated first and removed after a few releases. This applies to pre-1.0.0 releases as well.
-
Deprecation works by injecting
#[deprecated]attribute to existing syntactic elements of generated code without adding new ones. It might not work in corner cases if there is no place where the attribute could be added.Here is how a deprecation warning might look like:
warning: use of deprecated function `my_function`: compose_idents!: Using semicolons as separators is deprecated, use commas instead
Alternatives
There are some other tools and projects dedicated to identifier manipulation:
- A macro from Nightly Rust that allows to concatenate identifiers. It is limited in functionality and nowhere near to be stabilized: https://doc.rust-lang.org/std/macro.concat_idents.html
- A very similar macro that doesn't support multiple aliases and is not maintained: https://github.com/DzenanJupic/concat-idents
- A macro that allows to define and refer to unique temporary variables: https://crates.io/crates/templest
Development
The following standards are followed to maintain the project: