It is certainly possible, but the goal (and effectiveness) is highly questionable. Why are you trying to do this? Are you trying to store some sort of a password or encryption key in the code?
It won't work like that. The Arguments::new_v1 works with &'static str, so those strings would need to (be leaked or) exist in the executed binary (in not obfuscated form) anyways. And furthermore, perhaps more importantly, there is no way to modify the output of the println macro anyways. A proc macro can only do something about the printlnbefore its called.
Approaches that might work:
A proc macro obfuscate!("my string") that obfuscates the string at compile time and returns a value of some type struct ObfuscatedLiteral(&'static str) wrapping the obfuscated literal, and having a Display impl that would de-obfuscate the contents again.
Finally, of you want, you could also write another macro that makes this println! call easier to write by parsing the format string and factoring out the literal parts for you accordingly. I would only do this as a subsequent step, once you are sure the approach works as intended (with the indented effects on the string literals inside the binary) in the first place.
That's understandable, and a noble goal, but it's pretty ineffective against anyone but the least skilled attackers. (At least it doesn't incur a security vulnerability, like storing a password directly would be.)
Anyway, what I would do is create an equivalent to format_args!(). You can then use it to build equivalents of all the other macros as well. I don't see an easy way to re-use the existing format_args!() mechanism (at least not without parsing the format string for yourself).
Note that what I suggested above only required partially parsing the format string in order to be fully usable like print/format_args. It would only be necessary to find and separate the {…} braces (while ignoring the {{ and }} escapes), because the original format_args macro would still be used to a large part, just the literal string sections would be factored out into arguments, too.
error: there is no argument named `str`
--> src/main.rs:4:13
|
4 | println!(concat!("{str", "} {}"), "test");
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: did you intend to capture a variable `str` from the surrounding scope?
= note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro
= note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info)
error: could not compile `playground` due to previous error
Consider using the defmt crate, which outputs numeric identifiers for the string constants and provides a separate lookup table for the receiving program to use.
Hi, apologies for bumping this thread but if you (or anyone else) is looking for an answer to this question I have some crates that solve this specific problem (disclaimer: I am the author of these crates):
obfstr - General string obfuscation library. fmtools - Extended formatting syntax for Rust with optional obfstr support by enabling the obfstr feature.
fmtools = { version = "0.1", features = ["obfstr"] }
The code in the OP would be written like this:
fmtools::println!("aaa "{s}" bbb "{s2}" ccc");
When the obfstr feature on fmtools is enabled you will not find the formatting string literals by simply looking at the binary.