emoun / duplicate Goto Github PK
View Code? Open in Web Editor NEWEasy code duplicate with substitution for Rust
License: Apache License 2.0
Easy code duplicate with substitution for Rust
License: Apache License 2.0
I use the duplicate
macro to generate a sync and a async version of a function. I would like to include the async version only if the feature async
is enabled. Is there any way to achieve that?
The following code, does not work. I guess this is because the cfg
macro has a "higher" priority as the duplicate_item
macro and therefore it just says "code is inactive due to #[cfg] directives: cfg_filter is disabled"
#[duplicate_item(
fn_name cfg_filter async;
[my_function] [all()] [];
[my_function_async] [feature="async"] [async];
)]
#[cfg(cfg_filter)]
async fn fn_name() {
todo!()
}
Thanks a lot in advance!
Introduce a function-like procedural macro with the same functionality as duplicate
.
duplicate
can only be used where attributes are allowed. Notably, attributes are currently not allowed on statements. This is a significant limitation. Therefore, we can alleviate this by introducing a function-like version of duplicate
that can be used where the original is not allowed.
Example:
fn main() {
duplicate_inline!{
#[
name value;
[var1] [1usize];
[var2] [2usize];
[var3] [2usize]
]
let name = value;
}
}
Expands to:
fn main() {
let var1 = 1usize;
let var2 = 2usize;
let var3 = 3usize;
}
Decide on a "Minimum Supported Rust Version" (MSRV).
#17 proposes changes to the implementation to allow the crate to work on rust 1.34.
This is a clear indication of a need for an official MSRV from a direct user of the crate.
The reason for using 1.34 specifically is that apparently the current Debian stable has this version installed.
This could be a good starting point for an MSRV as its backwards compatible with the current implementation, and it can be lowered even more in the future, without having to bump the major version. And from #17 it doesn't seem like we depend on important features of newer rust versions.
Commit to an official MSRV of 1.34
When trying to use parametric substitution but forgetting to encapsulate the argument to a parametrized identifier in []
, the resulting error message is confusing.
A simple example is to try and create a duplicate with and without &
:
#[duplicate(refs(T); [& T]; [T])]
fn from(x: refs(Bits<1, false>)) -> bool {
x.value == 1
}
An error message pointing out that the argument to refs
is not encapsulated in []
.
The error message:
message: Error substituting identifier with arguments: ()
Only tested on 0.3.0
Using default features
When I try to build the project on any of machines, I get the following error. My guess is that an import was misplaced but I don't know why the CI builds wouldn't catch it.
Downloaded version_check v0.9.2
Downloaded proc-macro-error v1.0.3
Downloaded proc-macro-error-attr v1.0.3
Downloaded quote v1.0.7
Downloaded syn-mid v0.5.0
Downloaded unicode-xid v0.2.1
Downloaded syn v1.0.33
Downloaded 7 crates (301.2 KB) in 0.66s
Compiling proc-macro2 v1.0.18
Compiling unicode-xid v0.2.1
Compiling version_check v0.9.2
Compiling syn v1.0.33
Compiling proc-macro-error-attr v1.0.3
Compiling proc-macro-error v1.0.3
Compiling quote v1.0.7
Compiling syn-mid v0.5.0
Compiling duplicate v0.0.0 (/tmp/duplicate)
error[E0603]: crate `proc_macro` is private
--> src/parse_utils.rs:3:2
|
3 | proc_macro::{Punct, Spacing},
| ^^^^^^^^^^ private crate
|
note: the crate `proc_macro` is defined here
--> /home/eli/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro-error-1.0.3/src/lib.rs:256:1
|
256 | extern crate proc_macro;
| ^^^^^^^^^^^^^^^^^^^^^^^^
warning: unused import: `proc_macro_error::*`
--> src/parse.rs:6:5
|
6 | use proc_macro_error::*;
| ^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
warning: unused import: `*`
--> src/parse_utils.rs:4:2
|
4 | *,
| ^
error: aborting due to previous error; 2 warnings emitted
For more information about this error, try `rustc --explain E0603`.
error: could not compile `duplicate`.
To learn more, run the command again with --verbose.
This patch should fix it. I'll submit a PR after I test it and also replace the wildcard imports.
index 6a7fd06..96061a7 100644
--- a/src/parse_utils.rs
+++ b/src/parse_utils.rs
@@ -1,8 +1,5 @@
-use proc_macro::{Delimiter, Group, Span, TokenTree};
-use proc_macro_error::{
- proc_macro::{Punct, Spacing},
- *,
-};
+use proc_macro::{Delimiter, Group, Punct, Spacing, Span, TokenTree};
+use proc_macro_error::*;
/// Tries to parse a valid group from the given token stream iterator, returning
/// the group if successful.
Make it possible to add substitution variables without adding more duplication of the code.
A useful feature of this crate is the ability to reduce repetition without adding duplications:
#[duplicate(
typ; [i32]
)]
fn some_func(in: &typ) -> typ{
..
}
Here we see how by only defining 1 duplication, we can reuse typ
in the function.
This can be very useful where some piece of code is repeated many times in the same item.
Duplication is not needed, but we want to be able to insert some piece of code in many places.
While the above is currently possible, it cannot be used if duplication is also wanted.
In that case we would either need multiple calls to duplicate
or to repeat the body of the substitution variable for each duplication.
This proposal introduces first class support for global substitutions, which are substitutions that don't require duplication.
An insertion variable is a substitution variable that is only used for global substitution .
We declare an insertion variable using first an identifier, possibly followed by a parameter list, followed by a substitution and ended with semicolon:
#[duplicate(
reference(type) [ & type];
)]
fn some_func(in: reference([i32])) -> reference([i64]){
..
}
This syntax is identical to how a substitution is declared in the verbose syntax and should therefore be natural to use and understand.
To increase the distinction between global substitution and duplication, we could enforce that all insertions must come at the beginning followed by the duplication declarations. If deemed unnecessary, this could be relaxed in the future without needing a major version bump.
While this seems like a useful feature, allowing the use of insertion variables directly in the duplication declarations, it could also lead to confusion if the user doesn't intend to do it. Additionally, it also disallows the use of insertion variable identifiers in the duplication substitutions where insertions aren't wanted.
If we don't have this, reusing insertion variables would still be possible by putting all the needed insertions in a preceding call. This way the user can choose themselves whether they want to reuse the identifiers or the insertions.
Conclusion: No.
While my original use case for nested invocations could be make use of insertions instead, nested invocations do have a power that cannot be replicated. Nested invocations allow exponential increases in duplication numbers by having one nested substitution group expand to multiple substitution groups in the parent.
I've found out that because this crate uses rust edition 2018, the code that we write inside of duplicate! {}
macro is also forced to use edition 2018.
For example the following code panics with the message {a}
instead of using the format args interpolation that was added in edition 2021
:
fn main() {
duplicate::duplicate! {
[foo; [];]
let a = 42;
panic!("{a}");
}
}
Also, the compiler warns about this when compiling:
warning: panic message contains an unused formatting placeholder
--> crates/scratch/src/main.rs:7:17
|
7 | panic!("{a}");
| ^^^
|
= note: `#[warn(non_fmt_panics)]` on by default
= note: this message is not used as a format string when given without arguments, but will be in Rust 2021
help: add the missing argument
|
7 | panic!("{a}", ...);
| +++++
help: or add a "{}" format string to use the message literally
|
7 | panic!("{}", "{a}");
| +++++
I've stumbled with this behavior and had to think really hard to deduce where the 2018
edition comes from because my crate where I use this macro is of edition 2021
๐คจ.
Anyway, I suppose this is a good reason to migration to the new edition.
Change the syntax to lock down which bracket types are allowed in various syntactic positions.
The current syntax doesn't distinguish between bracket types anywhere. This was meant to allow users to choose their preferred brackets in various positions.
After doing a survey of project on Github using duplicate
, it seems no one makes use of the bracket flexibility. They all simply seem to follow the examples of the documentation.
The flexibility does have its downsides. First, it makes future updates to the crate harder (see e.g. #23). Second, it allows fragmentation in syntax, where some code bases might use one set of brackets in a given position and another code bases could use another set of brackets in the same position. This makes it more difficult to parse for users. It also makes it more difficult for people without knowledge of this crate to figure out if the given set of brackets means anything special (which it doesn't current, which is counterintuitive).
We define a specific bracket type for each position in the syntax. We will use the brackets as they are currently used in the documentation examples. This means even though the change is technically breaking, most users wont notice, as they are already using the right brackets.
Syntax Position | Bracket Type | Example |
---|---|---|
Substitution | [] |
[ code to be substituted ] |
Substitution Parameter List | () |
sub_ident(param1, param2) |
Substitution Application Argument | [] |
sub_ident([arg1], [arg2]) |
Nested Invocation | [] |
#[ .. ][ .. ] |
Verbose syntax substitution group | [] |
[sub1 [ .. ] sub2 [ .. ]] |
duplicate_inline! invocation |
[] |
duplicate_inline!( [ .. ] .. ) |
How can I use duplicate
to use either ref
or ref mut
for pattern matching?
This doesn't work:
#[duplicate(
method reference(type) ref_;
[get] [& type] [ref];
[get_mut] [&mut type] [ref mut];
)]
fn method(v: reference([(u64, u64)])) -> reference([u64]) {
match v {
(ref_ a, b) => a
}
}
Duplicating it manually works:
fn get(v: &(u64, u64)) -> &u64 {
match v {
(ref a, b) => a
}
}
fn get_mut(v: &mut (u64, u64)) -> &mut u64 {
match v {
(ref mut a, b) => a
}
}
I know that in this case the code actually compiles without the ref
/ref mut
, but in more complex cases it might not.
Using duplicate_item
, it is possible to substitute things in a nested (or lower) duplicate_item
's invocation:
#[duplicate::duplicate_item(
vals [val; [41]; [42];];
)]
#[duplicate::duplicate_item(
vals
)]
impl SomeType<val> for (){}
Here, vals
substitutes into a valid invocation val; [41]; [42];
. This is then used in the next duplicate_item
as the invocation. This works fine and as expected, however, trying to do the same using duplicate
doesn't work
duplicate::duplicate!{
[vals [val; [41]; [42];];]
duplicate!{
[vals]
impl SomeType<v2> for (){}
}
}
Expand to:
impl SomeType<41> for () {}
impl SomeType<42> for () {}
Error is thrown:
error: Unexpected end of code. Expected substitution identifier or ';' instead.
--> tests\tests.rs:16:1
|
16 | / duplicate::duplicate!{
17 | | [vals [val; [41]; [42];];]
18 | | duplicate!{
19 | | [vals]
20 | | impl SomeType<v2> for (){}
21 | | }
22 | | }
| |_^
|
= note: this error originates in the macro `duplicate::duplicate` (in Nightly builds, run with -Z macro-backtrace for more info)
0.4.1 and likely all since nested invocation was introduced.
Release a version 1.0.0
Releasing a stable version of the crate signals to users that the crate is generally usable and can be depended upon.
The following work is open and should be finished before 1.0.0:
pretty_errors
feature also needs to be more complete and tested.
Duplicate v0.2.3 has a regression where substitutions that includes any bracket type result in the wrong expansion:
#[duplicate(
fn_name var;
[fn_name_1] [ Struct() ];
[fn_name_2] [ array[42] ];
[fn_name_3] [ Struct{} ];
)]
fn fn_name() {
let _ = var;
}
Expands to:
fn fn_name_1() {
let _ = ();
Struct;
}
fn fn_name_2() {
let _ = [42];
array;
}
fn fn_name_3() {
let _ = {};
Struct;
}
Instead of:
fn fn_name_1() {
let _ = Struct();
}
fn fn_name_2() {
let _ = array[42];
}
fn fn_name_3() {
let _ = Struct{};
}
Following global substitutions with a substitutions variable list for short syntax, but not following that with any substitution groups, will successfully compile.
Even though current behavior is workable, this will probably either be a mistake or lead to confusion about what happens to the substitution variable that don't have substitutions.
Giving an error also make the behavior match the old behavior before global substitutions (i.e. an error was thrown).
#[duplicate::duplicate(
ty [i16];
name;
)]
pub struct name(ty);
Fail to compile with a message about having no substitutions groups in the short syntax.
pub struct name(ty);
0.3.0
Based on this comment.
Change the short syntax so that substitution groups are on rows instead of columns:
#[duplicate(
ident1 ident2; // All substitution identifiers on one line (ending with ';' )
[ident1_subs1] [ident2_subst1];// group1
[ident1_subs2] [ident2_subst2];// group2
)]
Hi,
First of all, I'd like to thank you for making this great library, it saves so much time when binding to C FFI with many similar functions!
Now, one thing I'd like to do is to have access the "token" in the implementation block as a static str.
#duplicate([
_val_type [f32]
_storage [StorageType::Float]
_get [HAPI_GetAttributeFloatData]
_set [HAPI_SetAttributeFloatData]
_get_array [HAPI_GetAttributeFloatArrayData]
_set_array [HAPI_SetAttributeFloatArrayData]
]
impl AttribAccess for _val_type {
fn set(...) {
// be able to use `_set` ident as `&'static str` since some APIs require it.
}
}
Maybe it's already possible today? I couldn't figure out, since those tokens are not literals, I couldn't convert them to a string.
Could duplicate
provide a way to achieve this? Thanks!
Enable nested invocations inside substitution blocks.
Take #26, where we have many struct members needing to be instantiated to the same value:
impl Example {
fn inline_new() -> Self {
Example { one: 0, two: 0, .. }
}
fn attr_new() -> Self {
Example {one: 0, two: 0, ..}
}
}
We would like to be able to make a single invocation that inserts the instantiations many places. The closest we get today is:
#[duplicate(
members [one: 0, two: 0, ..]
)]
impl Example {
fn inline_new() -> Self {
Example {members}
}
fn attr_new() -> Self {
Example {members}
}
}
However, this repeats the :0 ,
bit for every member.
Nested invocation would be able to remove this repetition. However, it is not available inside substitutions. Enabling it, would allow us to do:
#[duplicate(
members [ #duplicate[
mem; [one]; [two]
][
mem: 0,
]]
)]
impl Example {
fn inline_new() -> Self {
Example {members}
}
fn attr_new() -> Self {
Example {members}
}
}
The first problem with enabling nested invocations inside substitutions is how to handle user attributes. Since we currently use the syntax #[..][..]
to denote nested invocation, this prohibits users from using attributes inside substitutions, as they would be assumed to be nested invocations.
To alleviate this, we change the syntax of nested invocations to #duplicate[..][..]
. This is generally illegal Rust syntax, so no user would write this outside specifically when nested invocations are wanted. However, it is valid syntax, allowing us to parse it.
This is, if course, a breaking change, as it will also be the syntax use for existing nested invocations.
#[duplicate( .. )]
: This mimics top-level invocations. However, it means we cannot distinguish between nested invocations and new top-level invocations that happen to be in the substitutions.Since procedural macros will soon support hygiene, it would be appropriate to implement implicit module name duplication.
if duplicate
is applied to a module, the user is currently required to make sure that the module has a unique name for every duplicate. This is tedious and must be done every time. To improve the user experience, duplicate
could use hygiene to allow all duplicates to keep the same module name, but with different hygiene. This means the user no longer has to manually assign names to each module duplicate.
The rule could be that if duplicate
is applied to a module directly, hygienic renaming will kick in, however, if the module is already renamed by a substitution identifier, the hygienic renaming won't happen. This should therefore be a non-breaking change.
Allows the use of cfg
attributes on substitution groups, such that they can be enabled and disabled at build time.
See this code.
Here, they use two cfg_attr
s to remove one substitution group based on whether a specific feature is enabled.
This is a bit ugly, as the two invocations of duplicate
are identical except for the second substitution group not being present on the second invocation.
Therefore, the first substitution group is actually copied between the two invocations.
It would be better to be able to, within the invocation itself, be able to disable a substitution group using a method akin to cfg_attr
.
This could be one way of designing it (this is using verbose syntax to match that of the above example):
duplicate_item(
[
module_type [non_blocking]
maybe_async_attr [maybe_async::must_be_async]
File [tokio::fs::File]
HttpBody [reqwest::Body]
HttpClient [reqwest::Client]
]
#[cfg_attr(feature = "non_blocking")]
[
module_type [blocking]
maybe_async_attr [maybe_async::must_be_sync]
File [std::fs::File]
HttpBody [reqwest::blocking::Body]
HttpClient [reqwest::blocking::Client]
]
))]
This will disable the second substitution group when the non_blocking
feature is disabled, exactly like the current semantics of the above example.
This would work the same way for the short syntax and global substitutions (where each attribute would work on just one substitution group or substitution).
Open questions:
cfg_attr
directly from duplicate
, such that it can return an empty string when the cfg
is false?cfg
or cfg_attr
?In the last days I made this repo https://github.com/Rimpampa/variants which I later found out it does the same thing as this crate but instead of using a procedural macro it uses a macro-by-example (or declarative macro).
Now instead of making a new crate I was wondering if there was any interest in adding this version of the macro to this crate.
There are some differences in the syntax, to give an example here is the examples in the docs made with my macro:
// 1st example
variants!([$] int_type | max_value: u8 | 255, u16 | 65_535, u32 | 4_294_967_295 => {
impl IsMax for $int_type {
fn is_max(&self) -> bool {
*self == $max_value
}
}
});
// 2nd example
impl<T> VecWrap<T> {
variants!([$] method: get, get_mut => {
pub fn $method<'a>(
self: select!(get: {&'a Self}, get_mut: {&'a mut Self}),
idx: usize,
) -> Option<select!(get: {&'a T}, get_mut: {&'a mut T}> {
self.0.$method(idx)
}
});
}
The main difference is the use of $var
syntax for substitution instead of directly using the declared identifier and the usage of the select!
macro.
Naturally it's not as clean as using a procedural macro and it currently doesn't support nested invocation but by making the syntax similar to the one used by your macro, it could be possible to make it more flexible.
Using your syntax and adding nesting, the 2nd and 3rd examples could be written like this:
// 2nd example
impl<T> VecWrap<T> {
variants!(
[$] [method] [ref];
[get] [&'a];
[get_mut] [&'a mut];
{
pub fn $method<'a>(self: $ref Self, idx: usize) -> Option<$ref T> {
self.0.$method(idx)
}
}
);
}
// 3rd example
variants!(
[d:$] [types] [impl];
[[u8]; [u16]; [u32]] [false];
[[i8]; [i16]; [i32]] [*self < 0];
{
variants!([$d] [type]; $types; {
impl IsNegative for $d type {
fn is_negative(&self) -> bool {
$impl
}
}
});
}
);
This is my proposal, I don't have a strong point as to why this should be included so I understand if you don't find this feature useful, but if there is an interest in adding this to the crate, let me know your opinions about it and I'll be willing to work on this to make it suitable for this crate.
Increase the flexibility of substitution by allowing identifiers to take "arguments" that change what is substituted.
Example:
#[duplicate(
let_var( var_name ); [ let var_name = Default::default();]
)]
fn func(){
let_var(some_name);
let_var(some_other_name);
}
Will expand to:
fn func(){
let some_name = Default::default();
let some_other_name = Default::default();
}
This issue will manage the feature for the short syntax only. A later issue will manage the same for the verbose syntax.
As reported in #42, transitive dependencies were not adequately vetted for compliance.
Specific versions of transitive dependencies should be used where not compliant with duplicate
's MSRV
Even though directly dependencies are pinned, transitives are not.
0.2.9, 0.3.0
I want to use duplicate
to create an asynchronous and a non-asynchronous version of a public method. Since this is a public API, I would like to have a proper doc comment and example. Currently both functions get the same comment, but I would like to have some small differences (especially in the example). Is there any way to achieve this?
Based on this comment.
Maybe allow the omission of brackets in some situations, like if the substitution is just an identifier or punctuation. Should be possible the new short syntax and existing verbose.
When using module disambiguation, the module identifier is also substituted if found anywhere in the module body.
This is a problems, since that might not be the desired behavior all the time.
E.g. we might have a struct using the same identifier that we wouldn't want to substitute.
In essence, the feature should disambiguate the module declaration and nothing else.
If users want to use the final modules' names, then they should need to use explicit substitutions.
#[duplicate(
value; [value1]; [value2]
)]
mod StructIdent {
fn some_fn() -> StructIdent {
StructIdent(value)
}
}
mod StructIdent_value1 {
fn some_fn() -> StructIdent_value1 {
StructIdent(value1)
}
}
mod StructIdent_value2 {
fn some_fn() -> StructIdent_value2 {
StructIdent(value2)
}
}
mod StructIdent_value1 {
fn some_fn() -> StructIdent_value1 {
StructIdent_value1(value1)
}
}
mod StructIdent_value2 {
fn some_fn() -> StructIdent_value2 {
StructIdent_value2(value2)
}
}
Enable substitution within string literals.
Substitution within the contents of string literals fits well with the overall functionality of duplicate
.
E.g. it could be used to change the wording on some strings depending on the duplicate.
Additionally, it enables the substitution of documentation comments. Currently, to do so can only be done using the doc attribute and substituting the whole string. Not being able to substitute in docs means our examples of generating different versions of functions for e.g. mutability or async don't work well if you need the documentation to be changed too. See #54. Enabling substitution within strings should (hopefully, needs more investigation) enable substitution within ///
comments directly.
Lastly, it's not immediately intuitive that substitution wouldn't work within string literals, making it a pain point for users.
During substition, if a string literal is encountered, run the substitutions within it too.
To parse the contents of strings, the litrs crate can be used. See also this stackoverflow answer.
Open questions:
&str
and not TokenStream
(if using littrs
), meaning the normal method of identifying substitution identifiers wouldn't work. This is especially difficult with parametric substitution.Change the crate we use for case conversions to heck
.
The reason for changing is because heck
is a more established and widely used crate that is also being maintained by a know, respected figure. This makes this crate a safer choice.
This change should not be seen as being caused by problems with the convert_case
crate or their maintainers.
I was hoping to have a way to substitute a value in to the name of a token. For example:
#[duplicate_item(
int_type max_value;
[ u8 ] [ 255 ];
[ u16 ] [ 65_535 ];
[ u32 ] [ 4_294_967_295 ];
)]
fn is_[int_type]_max(&self) -> bool {
*self == max_value
}
// Generates...
fn is_u8_max(&self) -> bool {
*self == 255
}
fn is_u16_max(&self) -> bool {
*self == 65_535
}
// etc.
I've read through the docs and can't seem to find a way to do so without restoring to using something like paste!
. Is that possible?
I have a use case exactly for this crate, I was happy I found it, but I can' make it work.
For each numeric type I need to implement a custom trait with calls to different ffi functions:
I can't figure out a correct syntax. Should the methods be each in a []
or all in one []
? The docs examples all use a single substitution for a single type.
use duplicate::duplicate;
#[duplicate(
val_type storage get set;
[i32] [StorageType::Int] [get_int_data] [set_int_data];
)]
impl AttribDataType for val_type {
fn read(...) {
crate::ffi::get(..)
}
fn read(...) {
crate::ffi::set(..)
}
fn storage() -> StorageType {
storage
}
}
error: expected one of `(` or `<`, found `::`
--> ...\src\attribute.rs:248:26
|
246 | / #[duplicate(
247 | | val_type storage get set array;
248 | | [i32] [StorageType::Int] [get_int_data] [set_int_data];
| | ^^ expected one of `(` or `<`
249 | | )]
| | -
| | |
| |__while parsing this item list starting here
| the item list ends here
Then I tried this:
#[duplicate(
[
val_type [i32]
storage [StorageType::Int]
get [get_int_data]
set [set_int_data]
]
)]
And get this error:
error: expected one of `(` or `<`, found `::`
--> ...\src\attribute.rs:254:29
|
251 | / #[duplicate(
252 | | [
253 | | val_type [i32]
254 | | storage [StorageType::Int]
| | ^^ expected one of `(` or `<`
... |
258 | | ]
259 | | )]
Would appreciate any help, thanks!
Now that #1 has changed the short syntax, it should be possible to allow nested invocation in it:
#[duplicate(
int_type implementation;
[i8] [ *self < 0 ];
#[
int_type ; [u8]; [u16]; [u32]
][
[ int_type ] [ false ];
]
)]
This is a test description of a bug, using the bug report template.
All of them or none, we'll see.
Not bad.
Awesome crate! Part of my needs include a situation where a number of structs have a large number of fields all instantiated the same way. The struct is simply the glue between two libraries so it's all fairly repetitive and I want to keep it as dry as possible.
I can't seem to figure out how to/if it's possible to instantiate structs using the macro though. Take this highly contrived example:
struct Example {
one: usize,
two: usize,
}
impl Example {
fn inline_new() -> Self {
Example {
duplicate_inline! {
[
key;
[ one ];
[ two ];
]
key: 0,
}
}
}
fn attr_new() -> Self {
Example {
#[duplicate(
key;
[ one ];
[ two ];
)]
key: 0,
}
}
}
It fails with the following:
error: expected one of `,` or `}`, found `!`
--> src/example.rs:57:23
|
56 | Example {
| ------- while parsing this struct
57 | duplicate_inline! {
| ^ expected one of `,` or `}`
error: expected non-macro attribute, found attribute macro `duplicate`
--> src/example.rs:70:9
|
70 | #[duplicate(
| ^^^^^^^^^ not a non-macro attribute
error[E0063]: missing fields `one` and `two` in initializer of `Example`
--> src/example.rs:56:5
|
56 | Example {
| ^^^^^^^ missing `one` and `two`
error[E0560]: struct `Example` has no field named `key`
--> src/example.rs:75:7
|
75 | key: 0,
| ^^^ `Example` does not have this field
|
= note: available fields are: `one`, `two`
It seems like the macros can't operate on struct fields, is that indeed the case? Is it simply a limitation of the macro system currently?
proc-macro-error
which is used for pretty errors seems to be unmaintained and also still depends on Syn 1.x.
The latter is my main driver for opening this issue as I'm working on cutting down on duplicate dependencies in @janhohenheim's Bevy game template Foxtrot. His template is quite representative of the kinds of dependencies that you'll find in Bevy games.
I propose replacing it with proc-macro2-diagnostics. Doing so is quite easy (I have a PR ready).
We use proc_macro_error
to print nice errors when using duplicate
. As #11 has shown, this is not without its risks.
Therefore, we should make it a feature that can be disabled, so that even if build fail when using the feature, turning the feature off can resolve the issue. This feature should be enabled by default.
This could also be a win in other areas, as published crates that use duplicate
do not need this feature (as they hopefully will have no errors.)
Rename the macros:
duplicate_inline
-> duplicate
(new),
duplicate
(old) -> duplicate_item
.
#28 decided that duplicate_inline
would be used in nested substitution.
Therefore, the current name is a bit long for something that would probably be embedded inside other code.
duplicate
(old) on the other hand is often on its own, or as part of shorter lines, so giving it a longer name wouldn't be an issue.
duplicate_inline
is also the more general of the macros (everywhere can use duplicate
(old) you can also use duplicate_inline
, but not vice versa).
Therefore, if it has the shorter name, new users trying it out (without knowing the difference) would have faster success.
Simple rename.
Initially, we will simply rename everything in the documentation. However, since duplicate_inline
will become the new default macro, we might want to refocus the documentation to account for this.
Find a better example for parameterized substitution in the documentation.
Increase base MSRV to 1.42
Significant effort must be continuously spent to keep the code working on older version.
The original reason for choosing 1.34 as MSRV came from #17, which used Rust on Debian stable, which in turn only had version 1.34 installed.
The current LTS of Debian is scheduled to reach end-of-life in June of this year, meaning the need for rust 1.34 is reduced. The next LTS uses run 1.48.
Therefore, since we are getting ready to ship version 0.4 with significant breaking changes, we might as well increase the MSRV while we are at it.
Increase base MSRV to 1.42 to match the MSRV of the module_disambiguation
feature.
When using parameterized substitution, if a substitution variable is used inside the argument to a substitution, it is not substituted.
Example:
#[duplicate::duplicate(
refs(type) fn_name;
[&type] [fn_const];
[&mut type] [fn_mut];
)]
fn fn_name(arg: refs([refs([i32])])){}
fn fn_const(arg: &&i32){}
fn fn_mut(arg: &mut &mut i32){}
fn fn_const(arg: &refs([i32])){}
fn fn_mut(arg: &mut refs([i32])){}
0.2.9 and below
Provide substitute
and substitute_item
which only provide the global substitution feature of the main macros.
Sometimes, duplicate
may be used only to avoid repeating something within the code, but not to duplicate the whole thing.
Providing these macro would make the intent more clear for readers of the code that no duplication is happening.
Provide substitute!
and substitute_item
as synonyms for duplicate!
and duplicate_item
respectively, where only global substitutions are allowed.
Just wanted to drop a big thank you for this crate! It saved me from some real nasty stuff. <3
Provide a way to easily convert an invocation of duplicate
from one syntax to another.
We currently have two syntaxes: short and verbose.
The reason to use the verbose syntax is that it looks clearer when the substitutions get complex. However, complexity usually evolves from something simple. Therefore, users would probably often start using the short syntax, but at some point realize that their use case has become too complex and would want to switch to the verbose syntax.
At that point it might be tedious to switch to the verbose syntax. Therefore, some automated way of producing a verbose version of a short syntax invocation would be nice.
The firs thing to realize is that this feature is not something that "stays" in the code. I.e. as soon as the conversion is done, we wouldn't want to see that it was ever using the other syntax. Therefore, this is more of a tool than a library feature. Therefore it should be easy to take an invocation, copy-paste the short syntax and get the verbose syntax that can then be manually switched for the short one. Then the code doing the conversion is just deleted.
An alternative could also be an actual command line tool that does the conversion. However, whether creating a whole executable for such a specific task might not be worth it.
Hey guys, I was wondering when would be next release for this package and if you are going to upgrade heck to v0.3.3 or more. As of now sqlx-macros Crate has a dependency of heck v0.3.3 or more and Duplicate crate requires v0.3.2 which creates conflicts.
Provide a way for reusing duplicate!
invocation code for multiple invocations
If we want to reuse the same invocation in multiple calls to different duplicate!
s:
some_invocations_code!{
ReUsableId
[SomeInvocations] [That are repeated]
}
#[duplicate_item(ReUsableId)]
{...}
#[duplicate_item(ReUsableId)]
{...}
It should be possible to define some code that can be used as duplicate
invocation repeatedly.
It should also be possible for an invocation to use some code from a different file.
Combine duplicate
with another dependency that depends on heck ^0.3.3
.
$ cargo new foo
Created binary (application) `foo` package
$ cd foo/
$ cargo add duplicate@=0.3.0 heck@=0.3.3
Updating 'https://github.com/rust-lang/crates.io-index' index
Adding duplicate v=0.3.0 to dependencies
Adding heck v=0.3.3 to dependencies
$ cargo b
Updating crates.io index
error: failed to select a version for `heck`.
... required by package `duplicate v0.3.0`
... which satisfies dependency `duplicate = "=0.3.0"` of package `foo v0.1.0 (/home/teo/junk/foo)`
versions that meet the requirements `=0.3.2` are: 0.3.2
all possible versions conflict with previously selected packages.
previously selected package `heck v0.3.3`
... which satisfies dependency `heck = "=0.3.3"` of package `foo v0.1.0 (/home/teo/junk/foo)`
failed to select a version for `heck` which could resolve this conflict
$ cat Cargo.toml
[package]
name = "foo"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
duplicate = "=0.3.0"
heck = "=0.3.3"
duplicate is built using the semver-compatible heck 0.3.3 instead.
The build fails because Cargo refuses to make a build graph that combines both heck 0.3.2 and heck 0.3.3.
0.3.0
cargo 1.56.0 (4ed5d137b 2021-10-04)
rustc 1.56.1 (59eed8a2a 2021-11-01)
Submitted a PR as #41.
#5 implemented substitution identifiers with arguments for the short syntax. As soon as the feature is mature for the short syntax, we should look into implementing the feature for the verbose syntax.
Inspired by a reddit comment, the module_disambiguation
should be edited to include the signed integer types and a subtitution for the body.
Relax MSRV policy to exclude any guarantees about the MSRV of duplicate
's dependencies.
#42 showed that the current MSRV policy implementation of pinning any dependencies that don't have a compatible MSRV policy puts undue limits on users who want to use the same dependencies as duplicate
.
The discussion in the same issue shows the problem isn't likely to be fixed by rust tooling, meaning pinning versions must be avoided by duplicate
As such, the MSRV is practically too restrictive and should be relaxed.
The original vision of the MSRV was to promise that increasing it would always follow with a major version bump.
While this can be maintained by duplicate
itself, it is unlikely to be a policy that is generally used by dependencies.
Therefore, to maintain as much of the promise as we can, while avoiding having to pin dependencies, the new policy is that dependencies don't necessarily have a compatible MSRV policy.
Instead, we will promise that the lowest version used by duplicate
works for the MSRV. The user must then independently pin any dependencies they need that releases a version with a higher MSRV.
Currently we have a feature called pretty_errors
that is intended to give better error messages than without the feature.
This is currently only haphazardly implemented and not automatically tested.
We should therefore add a setup that tests the error messages provided by this feature.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.