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(),
    }
}