diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a36f4ab..30f7cd2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,6 +17,9 @@ jobs: version: 3.x repo-token: ${{ secrets.GITHUB_TOKEN }} - uses: pre-commit/action@v3.0.1 + - uses: pkgxdev/setup@v3 + with: + +: gomplate.ca - name: Set up Rust toolchain run: rustup update stable && rustup default stable - name: Lint @@ -26,4 +29,6 @@ jobs: - name: Run tests run: task test - name: Build docs - run: cargo doc --verbose + run: | + task render + cargo doc --verbose diff --git a/README.md b/README.md index 9a75bde..30ec05a 100644 --- a/README.md +++ b/README.md @@ -32,29 +32,49 @@ Here is how the macro works: ```rust use compose_idents::compose_idents; - compose_idents!( - my_fn = [foo, _, "baz"]; - MY_UPPER_STATIC = [upper(spam), _, upper("eggs")]; - MY_LOWER_STATIC = [lower(GORK), _, lower(BORK)]; - MY_SNAKE_CASE_STATIC = [snake_case(snakeCase)]; - MY_CAMEL_CASE_STATIC = [camel_case(camel_case)]; { - fn my_fn() -> u32 { + my_fn_1 = [foo, _, "baz"]; + my_fn_2 = [spam, _, eggs]; + my_const = [upper(foo), _, lower(BAR)]; + my_static = [upper(lower(BAR))]; + MY_SNAKE_CASE_STATIC = [snake_case(snakeCase)]; + MY_CAMEL_CASE_STATIC = [camel_case(camel_case)]; { + fn my_fn_1() -> u32 { 123 } - static MY_UPPER_STATIC: u32 = 321; - static MY_LOWER_STATIC: u32 = 321123; - static MY_SNAKE_CASE_STATIC: u32 = 123321; - static MY_CAMEL_CASE_STATIC: u32 = 333333; + fn my_fn_2() -> u32 { + 321 + } + + const my_const: u32 = 42; + static my_static: u32 = 42; + static MY_SNAKE_CASE_STATIC: u32 = 42; + static MY_CAMEL_CASE_STATIC: u32 = 42; }); +macro_rules! outer_macro { + ($name:tt) => { + compose_idents!(my_nested_fn = [nested, _, $name]; { + fn my_nested_fn() -> u32{ + 42 + } + }); + }; +} + +outer_macro!(foo); + +fn main() { + assert_eq!(foo_baz(), 123); + assert_eq!(spam_eggs(), 321); + assert_eq!(nested_foo(), 42); + assert_eq!(FOO_bar, 42); + assert_eq!(BAR, 42); + assert_eq!(snake_case, 42); + assert_eq!(camelCase, 42); +} -assert_eq!(foo_baz(), 123); -assert_eq!(SPAM_EGGS, 321); -assert_eq!(gork_bork, 321123); -assert_eq!(snake_case, 123321); -assert_eq!(camelCase, 333333); ``` Here is a more practical example for how to auto-generate names for macro-generated tests for different data types: diff --git a/README.md.tpl b/README.md.tpl new file mode 100644 index 0000000..f8130fc --- /dev/null +++ b/README.md.tpl @@ -0,0 +1,87 @@ +![Build](https://github.com/AndreiPashkin/compose-idents/actions/workflows/build.yml/badge.svg) +[![Crates.io Version](https://img.shields.io/crates/v/compose-idents)](https://crates.io/crates/compose-idents) +[![docs.rs](https://img.shields.io/docsrs/compose-idents)](https://docs.rs/compose-idents) + +# compose-idents + +A procedural macro that allows to construct identifiers from one or more arbitrary parts. + +## Motivation + +Rust's declarative macros do not allow generating new identifiers, because they are designed to operate on +the syntactic level (as opposed to the lexical level) using simple pattern matching. + +For example the following code won't work: +```rust,compile_fail +macro_rules! my_macro { + ($name:ident) => { + my_$name_fn() -> u32 { + 42 + } + }; +} + +my_macro!(foo); +``` + +This is why there is a need for a macro that allows to construct new identifiers. + +## Usage + +Here is how the macro works: +```rust +{{ file.Read "examples/usage.rs" }} +``` + +Here is a more practical example for how to auto-generate names for macro-generated tests for different data types: +```rust +use std::ops::Add; +use compose_idents::compose_idents; + +fn add>(x: T, y: T) -> T { + x + y +} + +macro_rules! generate_add_tests { + ($($type:ty),*) => { + $( + compose_idents!(test_fn = [test_add_, $type]; { + fn test_fn() { + let actual = add(2 as $type, 2 as $type); + let expected = (2 + 2) as $type; + + assert_eq!(actual, expected); + } + }); + )* + }; +} + +generate_add_tests!(u8, u32, u64); + +test_add_u8(); +test_add_u32(); +test_add_u64(); +``` + +For more usage examples look into `examples/` and `tests/` directories of the repository. + +## Alternatives + +There 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: + +- A very similar macro that doesn't support multiple aliases and is not maintained: + +- A macro that allows to define and refer to unique temporary variables: + + +## Development + +The following standards are followed to maintain the project: +- +- +- +- diff --git a/Taskfile.yml b/Taskfile.yml index b321058..ea401a9 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -11,3 +11,6 @@ tasks: lint: cmds: - pre-commit run --all + render: + cmds: + - gomplate -f README.md.tpl -o README.md