1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
//! This crate provides the proc-macro part of the crate [`zalgo-codec`](https://docs.rs/zalgo-codec/latest/zalgo_codec/)
//! by defining the procedural macro [`zalgo_embed!`].
//!
//! It lets you take source code that's been converted into a single grapheme cluster by the
//! [`zalgo-codec-common`](https://docs.rs/zalgo-codec-common/latest/zalgo_codec_common/) crate
//! and compile it as if it was never zalgo-ified.
//!
//! # Example
//!
//! If we run [`zalgo_encode`] on the text
//! `fn add(x: i32, y: i32) -> i32 {x + y}` we can add the `add` function to our program
//! by putting the resulting grapheme cluster inside [`zalgo_embed!`]:
//! ```
//! # use zalgo_codec_macro::zalgo_embed;
//! zalgo_embed!("E͎͉͙͉̞͉͙͆̀́̈́̈́̈̀̓̒̌̀̀̓̒̉̀̍̀̓̒̀͛̀̋̀͘̚̚͘͝");
//! assert_eq!(add(10, 20), 30);
//! ```
#![no_std]
#![forbid(unsafe_code)]
extern crate alloc;
use alloc::format;
use proc_macro::TokenStream;
use syn::{parse_macro_input, spanned::Spanned, Error, LitStr};
use zalgo_codec_common::{zalgo_decode, zalgo_encode};
/// This macro decodes a string that has been encoded with [`zalgo_encode`](https://docs.rs/zalgo-codec-common/latest/zalgo_codec_common/fn.zalgo_encode.html)
/// and passes the results on to the compiler.
///
/// To generate the encoded string used as input you can use the provided program. It can be installed with `cargo install zalgo-codec --features binary`.
///
/// # Examples
///
/// We can use a function created in encoded source code:
/// ```
/// # use zalgo_codec_common::zalgo_encode;
/// # use zalgo_codec_macro::zalgo_embed;
/// // This line expands to the code
/// // `fn add(x: i32, y: i32) -> i32 {x + y}`
/// zalgo_embed!("E͎͉͙͉̞͉͙͆̀́̈́̈́̈̀̓̒̌̀̀̓̒̉̀̍̀̓̒̀͛̀̋̀͘̚̚͘͝");
///
/// // Now the `add` function is available
/// assert_eq!(add(10, 20), 30);
/// ```
///
/// It works on expressions too!
/// ```
/// # use zalgo_codec_common::zalgo_encode;
/// # use zalgo_codec_macro::zalgo_embed;
/// let x = 20;
/// let y = -10;
///
/// // This macro is expanded to the code
/// // `x + y`
/// let z = zalgo_embed!("È͙̋̀͘");
/// assert_eq!(z, x + y);
/// ```
///
/// A more complex example is this program which expands to code that reads the
/// command line input, encodes it, and prints out the result.
/// ```
/// use zalgo_codec_common::{zalgo_encode, Error};
/// use zalgo_codec_macro::zalgo_embed;
///
/// fn main() -> Result<(), Error> {
/// // This macro expands to
/// // let input = std::env::args().collect::<Vec<_>>()[1..].join(" ");
/// // let output = zalgo_encode(&input)?;
/// // println!("{}", output);
/// zalgo_embed!("E͔͉͎͕͔̝͓͔͎͖͇͓͌̀͐̀̀̈́́͒̈̉̎̓̚̚̚̚ͅͅ͏̶͔̜̜̞̞̻͌͌̓̓̿̈̉̑̎̎̽̎͊̚̚ͅͅ͏̛͉͎͔̈̂̀̂̉ͯ͌̀ͅ͏͕͔͕͔̝͚͇͐̀̀́͌͏͎̿̓ͅ͏̛͉͎͕͔̟͉͎͔͎̼͎̼͎̈́̈̆͐̉ͯ͐͒͌́̈̂͛̂̌̀͝ͅ͏̛͕͔͕͔͐̉");
/// Ok(())
/// }
/// ```
/// Do the opposite of [`obfstr`](https://docs.rs/obfstr/latest/obfstr/): obfuscate a string while coding and deobfuscate it during compile time
/// ```
/// # use zalgo_codec_macro::zalgo_embed;
/// const SECRET: &str = zalgo_embed!("Ê̤͏͎͔͔͈͉͓͍̇̀͒́̈́̀̀ͅ͏͍́̂");
///
/// assert_eq!(SECRET, "Don't read this mom!");
/// ```
/// To do something more like `obfstr`, use [`zalgofy!`].
///
/// # Errors
///
/// This macro utilizes [`zalgo_decode`] internally, and converts its errors
/// into compile errors. E.g. it will most likely result in a compile error if you
/// use this macro on a string that was not generated by [`zalgo_encode`]:
/// ```compile_fail
/// # use zalgo_codec_macro::zalgo_embed;
/// // compile error: the given string decodes into an invalid utf-8 sequence of 1 bytes from index 0
/// zalgo_embed!("Zalgo");
/// ```
#[proc_macro]
pub fn zalgo_embed(encoded: TokenStream) -> TokenStream {
let encoded = parse_macro_input!(encoded as LitStr).value();
match zalgo_decode(&encoded) {
Ok(decoded) => match decoded.parse() {
Ok(token_stream) => token_stream,
Err(e) => Error::new(encoded.span(), e).into_compile_error().into(),
},
Err(e) => Error::new(
encoded.span(),
format!("the given string decodes into an {e}"),
)
.into_compile_error()
.into(),
}
}
/// At compile time this proc-macro encodes the given string literal
/// as a single grapheme cluster.
///
/// # Example
///
/// Basic usage:
/// ```
/// # use zalgo_codec_macro::zalgofy;
/// const ZS: &str = zalgofy!("Zalgo");
/// assert_eq!(ZS, "É̺͇͌͏");
/// ```
///
/// # Errors
///
/// This macro uses [`zalgo_encode`] internally and converts its errors into compile errors.
/// As such it gives a compile error if any character in the string is not either
/// a printable ACII or newline character.
///
/// ```compile_fail
/// # use zalgo_codec_macro::zalgofy;
/// // compile error: "can not encode '€' character at string index 4, on line 2 at column 3"
/// const ZS: &str = zalgofy!(
/// r"a
/// ae€"
/// );
/// ```
#[proc_macro]
pub fn zalgofy(string: TokenStream) -> TokenStream {
let string = parse_macro_input!(string as LitStr).value();
match zalgo_encode(&string) {
Ok(encoded) => {
let string = format!("\"{encoded}\"");
match string.parse() {
Ok(token_stream) => token_stream,
Err(e) => Error::new(string.span(), e).into_compile_error().into(),
}
}
Err(e) => Error::new(string.span(), e).to_compile_error().into(),
}
}