I'm making a web API client. I want to create functions that correspond to the available API endpoints.
In some cases, the URL is always the same. Then, manually calling the API looks something like this:
let things_list_url = "https://example.com/api/things/list"; let things_list: Vec<SomeThing> = make_request(GET, thing_list_url).into(); The macro I'm using for this looks like:
macro_rules! api_request { ($name: ident, $method: ident, $path: expr, $return_type: ty) => { pub fn $name() -> $return_type { let action_url = format!("https://example.com/api{}", $path); let action_result = make_request($method, action_url); action_result.into() } }; } api_request!(get_things_list, GET, "/things/list", Vec<SomeThing>); fn main() { println!("{:?}", get_things_list()); } A similar pattern works for when the API call has a body, as long as the URL remains the same.
Some other endpoints have parameters in their URL. Manually calling them looks like:
let thing = SomeThing { id: 123, ...}; let thing_status_url = format!("https://example.com/api/things/{}/status", thing.id); let thing_status: SomeThingStatus = make_request(GET, thing_status_url).into(); However, my attempt at making a macro for this does not work. For simplicity, let's assume there is only one argument to the format! call:
macro_rules! api_request_with_path { ($name: ident, $method: ident, $request_type: ty, $return_type: ty, $path_format_string: expr, $path_format_arg: expr) => { pub fn $name( arg: $request_type ) -> $return_type { let action_url_fragment = format!($path_format_string, $path_format_arg); let action_url = format!("https://example.com/api{}", action_url_fragment); let action_result = make_request($method, action_url); action_result.into() } }; } api_request_with_path!(get_thing_status, GET, SomeThing, SomeThingStatus, "things/{}/status", arg.id); This fails, because I'm passing an expression that includes arg -- the argument to the generated function -- but the arg does not exist at the scope where the macro call is. How can I provide the macro with a way to turn the argument of type $request_type into a URL string?