Skip to content

Commit 00c5cf5

Browse files
author
Joe Grund
committed
Fixup Macro
Signed-off-by: Joe Grund <[email protected]>
1 parent ecc8d9b commit 00c5cf5

File tree

1 file changed

+62
-23
lines changed

1 file changed

+62
-23
lines changed

commandeer-macros/src/lib.rs

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,61 @@
11
use proc_macro::TokenStream;
22
use quote::quote;
3-
use syn::{Expr, Ident, ItemFn, parse_macro_input, parse_quote};
3+
use syn::{
4+
Expr, Ident, ItemFn, Result,
5+
parse::{Parse, ParseStream},
6+
parse_macro_input, parse_quote,
7+
};
8+
9+
struct CommandeerArgs {
10+
mode: Ident,
11+
commands: Vec<String>,
12+
}
13+
14+
const RECORD: &str = "Record";
15+
const REPLAY: &str = "Replay";
16+
17+
impl Parse for CommandeerArgs {
18+
fn parse(input: ParseStream) -> Result<Self> {
19+
let mut commands = vec![];
20+
21+
let ident: Ident = input.parse()?;
22+
23+
let mode = match ident.to_string().as_str() {
24+
x if [RECORD, REPLAY].contains(&x) => Ident::new(x, proc_macro2::Span::call_site()),
25+
_ => {
26+
return Err(syn::Error::new(
27+
ident.span(),
28+
format!("Expected '{RECORD}' or '{REPLAY}'"),
29+
));
30+
}
31+
};
32+
33+
input.parse::<syn::Token![,]>()?;
34+
35+
while !input.is_empty() {
36+
if input.peek(syn::LitStr) {
37+
let lit: syn::LitStr = input.parse()?;
38+
39+
commands.push(lit.value());
40+
} else {
41+
return Err(input.error("Expected a command string"));
42+
}
43+
44+
if input.peek(syn::Token![,]) {
45+
input.parse::<syn::Token![,]>()?;
46+
}
47+
}
48+
49+
if commands.is_empty() {
50+
return Err(syn::Error::new(
51+
input.span(),
52+
"Expected at least one command string",
53+
));
54+
}
55+
56+
Ok(CommandeerArgs { mode, commands })
57+
}
58+
}
459

560
/// Procedural macro for setting up commandeer test environment
661
///
@@ -9,35 +64,17 @@ use syn::{Expr, Ident, ItemFn, parse_macro_input, parse_quote};
964
/// This expands to code that creates a Commandeer instance and mocks the specified commands
1065
#[proc_macro_attribute]
1166
pub fn commandeer(args: TokenStream, input: TokenStream) -> TokenStream {
67+
let args = parse_macro_input!(args as CommandeerArgs);
1268
let mut input_fn = parse_macro_input!(input as ItemFn);
1369

14-
// Parse arguments manually since AttributeArgs was removed in syn 2.0
15-
let args_str = args.to_string();
1670
let fn_name = &input_fn.sig.ident;
1771

18-
// Generate test file name from function name
1972
let test_file_name = format!("cmds_{fn_name}.json");
2073

21-
// Simple parsing - look for Record/Replay and string literals
22-
let mut mode_str = "Record"; // default
23-
let mut commands = vec![];
24-
2574
// Split by commas and parse each part
26-
for part in args_str.split(',') {
27-
let part = part.trim();
28-
if part == "Record" || part == "Replay" {
29-
mode_str = part;
30-
} else if part.starts_with('"') && part.ends_with('"') {
31-
// Remove quotes and add to commands
32-
let cmd = &part[1..part.len() - 1];
33-
commands.push(cmd.to_string());
34-
}
35-
}
36-
37-
let mode_ident = Ident::new(mode_str, proc_macro2::Span::call_site());
3875

39-
// Generate mock_command calls
40-
let mock_commands: Vec<Expr> = commands
76+
let mock_commands: Vec<Expr> = args
77+
.commands
4178
.iter()
4279
.map(|cmd| {
4380
parse_quote! {
@@ -46,9 +83,11 @@ pub fn commandeer(args: TokenStream, input: TokenStream) -> TokenStream {
4683
})
4784
.collect();
4885

86+
let mode = args.mode;
87+
4988
// Create the setup statements
5089
let setup_stmts: Vec<syn::Stmt> = vec![parse_quote! {
51-
let commandeer = commandeer_test::Commandeer::new(#test_file_name, commandeer_test::Mode::#mode_ident);
90+
let commandeer = commandeer_test::Commandeer::new(#test_file_name, commandeer_test::Mode::#mode);
5291
}];
5392

5493
let mock_stmts: Vec<syn::Stmt> = mock_commands

0 commit comments

Comments
 (0)