easify_macros/lib.rs
1use proc_macro::TokenStream;
2// someting goes here
3#[doc(hidden)]
4use quote::quote;
5#[doc(hidden)]
6use syn::{
7 parse::Parse, parse::ParseStream, parse_macro_input, Expr, Ident, LitInt, LitStr,
8 Result, Token,
9};
10
11struct DynamicTupleParams(Expr, LitInt);
12struct SmartSplitParams(Ident, LitStr, LitInt);
13
14impl Parse for DynamicTupleParams {
15 fn parse(input: ParseStream) -> Result<Self> {
16 let value = input.parse()?;
17 input.parse::<Token![,]>()?;
18 let count = input.parse()?;
19 Ok(DynamicTupleParams(value, count))
20 }
21}
22
23impl Parse for SmartSplitParams {
24 fn parse(input: ParseStream) -> Result<Self> {
25 let text = input.parse()?;
26 input.parse::<Token![,]>()?;
27 let sep = input.parse()?;
28 input.parse::<Token![,]>()?;
29 let count = input.parse()?;
30 Ok(SmartSplitParams(text, sep, count))
31 }
32}
33/// A procedural macro to create a tuple with dynamic number of arguments.
34///
35/// This macro takes exactly two arguments, both of which should be valid Rust expressions
36/// that evaluate to integer types. The macro will expand to the tuple, including value of first argument
37/// repeated N times, where N is a value of second argument.
38///
39/// # Examples
40///
41/// Use the macro to sum two integers:
42///
43/// ```
44/// use easify_macros::dynamic_tuple;
45/// let result = dynamic_tuple!(2, 1);
46/// assert_eq!(result, (2, ));
47/// ```
48///
49/// ```
50/// use easify_macros::dynamic_tuple;
51/// let result = dynamic_tuple!(5, 3);
52/// assert_eq!(result, (5, 5, 5));
53/// ```
54///
55/// # Panics
56///
57/// The macro will cause arguments_number compile-time error if not exactly two arguments are provided.
58///
59/// ```
60/// # use easify_macros::dynamic_tuple;
61/// // This will fail to compile
62/// // let result = dynamic_tuple!(5);
63/// ```
64
65#[proc_macro]
66pub fn dynamic_tuple(input: TokenStream) -> TokenStream {
67 let DynamicTupleParams(value, count) = parse_macro_input!(input as DynamicTupleParams);
68
69 // Convert value to TokenStream and then to string
70 let value_ts = quote! { #value }.to_string();
71 let count = count
72 .base10_parse::<usize>()
73 .expect("Invalid count of arguments");
74
75 // Generate the string representation of the tuple
76 let tuple_elements = std::iter::repeat(value_ts)
77 .take(count)
78 .collect::<Vec<_>>()
79 .join(", ");
80 let tuple_string = format!("({})", tuple_elements);
81
82 // Convert the string to arguments_number TokenStream and return
83 tuple_string.parse().unwrap()
84}
85
86/// A procedural macro to split the string and return tuple, where you know the number of
87/// arguments, thus making it easy to unpack.
88///
89/// This macro takes exactly two arguments, both of which should be valid Rust expressions
90/// that evaluate to str types. The macro will expand to the tuple, including value of first argument
91/// repeated N times, where N is arguments_number value of second argument.
92///
93/// # Examples
94///
95/// Use the macro to split the string:
96///
97/// ```
98/// use easify_macros::unpack_split;
99/// let result = unpack_split!("hello,my,name", ",");
100/// assert_eq!(result, ("hello", "my", "name"));
101/// ```
102///
103/// ```
104/// use easify_macros::unpack_split;
105/// let some_text = "hello,world";
106/// let result = unpack_split!(some_text, 2);
107/// assert_eq!(result, ("hello", "world"));
108/// ```
109///
110/// # Panics
111///
112/// The macro will cause arguments_number compile-time error if not exactly two arguments are provided.
113///
114/// ```
115/// # use easify_macros::unpack_split;
116/// // This will fail to compile
117/// // let result = unpack_split!("hello,world");
118/// ```
119
120#[proc_macro]
121pub fn unpack_split(input: TokenStream) -> TokenStream {
122 let SmartSplitParams(text, sep, count) = parse_macro_input!(input as SmartSplitParams);
123 let len = count.base10_parse::<usize>().unwrap();
124
125 let indices = 0..len;
126 let tuple_elems = indices.map(|_| quote! {tuple_elements.next().unwrap()});
127 let output = quote! {
128 {
129
130 let mut tuple_elements = #text.split(#sep);
131 (#( #tuple_elems, )*)
132 }
133 };
134 output.into()
135}
136
137// Define arguments_number struct to hold the parsed elements
138struct LetStatement {
139 vars: Vec<proc_macro2::TokenStream>,
140 astrix: usize,
141 expr: Ident,
142}
143
144// Implement custom parsing for the LetStatement
145impl Parse for LetStatement {
146 fn parse(input: ParseStream) -> Result<Self> {
147 let mut vars = Vec::new();
148
149 let mut count: usize = 0;
150 let mut astrix: usize = 0;
151 // Parse variables
152 loop {
153 if input.peek(Token![*]) {
154 input.parse::<Token![*]>()?; // Parse the '*' if present
155 astrix = count;
156
157 }
158
159 let is_mut = input.peek(Token![mut]);
160 if is_mut {
161 input.parse::<Token![mut]>()?; // Parse the '*' if present
162 }
163 let var: Ident = input.parse()?;
164
165 if is_mut {
166 vars.push(quote!(mut #var));
167 } else {
168 vars.push(quote!(#var));
169
170 }
171
172 if input.peek(Token![,]) {
173 input.parse::<Token![,]>()?;
174 } else {
175 break;
176 }
177 count += 1;
178 }
179
180 input.parse::<Token![=]>()?; // Parse the '='
181
182 // Parse the right-hand side expression
183 let expr: Ident = input.parse()?;
184
185 Ok(LetStatement { vars, astrix, expr })
186 }
187}
188
189/// A procedural macro to unpack like in Python programming language, where you expect array of
190/// known size, thus making it easy to unpack.
191///
192/// This macro consist of specifying initial variables that your given array will be unpacked to.
193/// You can also make your variable mutable, by simply adding `mut` before the variable name.
194/// The astrix `*` is used to unpack the rest of the array, resulting new array.
195/// Note, that you can use * only once in any position. The macro will expand to the
196/// let statements and named to variables you have specified.
197/// This macro can result to unexpected behavior and panicks if you don't know the Python unpacking
198/// Use it with caution.
199/// And remember, with great power comes great responsibility.
200/// # Examples
201///
202///
203///
204/// Use the macro to split the string:
205///
206/// ```
207/// use easify_macros::let_unpack;
208/// let unpacking = vec![1, 2, 3];
209/// let result = let_unpack!(*mut a, b, c = unpacking);
210/// a.push(10);
211/// assert_eq!(a, vec![1, 10]);
212/// assert_eq!(b, 2);
213/// assert_eq!(c, 3);
214/// ```
215///
216/// ```
217/// use easify_macros::let_unpack;
218/// let unpacking = vec![2, 3]
219/// let result = let_unpack!(a, *b, c = unpacking);
220/// a.push(10);
221/// assert_eq!(a, 2);
222/// assert_eq!(b, vec![]);
223/// assert_eq!(c, 3);
224/// ```
225///
226/// # Panics
227///
228/// The macro will cause arguments_number compile-time error if not exactly two arguments are provided.
229///
230/// ```
231/// # use easify_macros::let_unpack;
232/// // This will fail to compile
233/// // let result = let_unpack!("hello,world");
234/// ```
235// Procedural macro
236
237#[proc_macro]
238pub fn let_unpack(input: TokenStream) -> TokenStream {
239 let LetStatement { vars, astrix, expr} = parse_macro_input!(input as LetStatement);
240 let mut values = Vec::new();
241 let arguments_number = vars.len();
242 for index in 0..arguments_number {
243 if index < astrix {
244 values.push(quote! {#expr[#index]});
245 } else if index == astrix {
246 values.push(
247 quote! {&#expr[#index..#expr.len() - #arguments_number + #index + 1].to_vec()}
248 );
249 } else {
250 values.push(quote! {#expr[#expr.len() - #arguments_number + #index]});
251 }
252 }
253 TokenStream::from(quote! {
254 #( let #vars = #values; )*;
255 drop(#expr);
256 })
257
258}