typeway_client/methods.rs
1//! The `client_api!` macro for generating named client wrapper structs.
2//!
3//! Instead of calling `client.call::<EndpointType>(args)` with turbofish
4//! syntax, this macro generates a wrapper struct with a named method for each
5//! endpoint.
6//!
7//! # Example
8//!
9//! ```ignore
10//! use typeway_client::client_api;
11//! use typeway_core::*;
12//! use typeway_macros::*;
13//!
14//! typeway_path!(type UsersPath = "users");
15//! typeway_path!(type UserByIdPath = "users" / u32);
16//!
17//! client_api! {
18//! pub struct UserClient;
19//!
20//! /// List all users.
21//! list_users => GetEndpoint<UsersPath, Vec<User>>;
22//!
23//! /// Get a user by ID.
24//! get_user => GetEndpoint<UserByIdPath, User>;
25//!
26//! /// Create a new user.
27//! create_user => PostEndpoint<UsersPath, CreateUser, User>;
28//! }
29//!
30//! // Use the generated struct:
31//! let client = UserClient::new("http://localhost:3000").unwrap();
32//! let users = client.list_users(()).await.unwrap();
33//! let user = client.get_user((42u32,)).await.unwrap();
34//! ```
35
36/// Generate a named client wrapper struct with a method per endpoint.
37///
38/// See the [module-level documentation](self) for usage examples.
39#[macro_export]
40macro_rules! client_api {
41 // Entry point: parse struct definition + methods.
42 (
43 $(#[$struct_meta:meta])*
44 $vis:vis struct $Name:ident;
45
46 $(
47 $(#[$method_meta:meta])*
48 $method_name:ident => $Endpoint:ty;
49 )*
50 ) => {
51 $(#[$struct_meta])*
52 $vis struct $Name {
53 inner: $crate::Client,
54 }
55
56 impl $Name {
57 /// Create a new client pointing at the given base URL with default
58 /// config.
59 $vis fn new(base_url: &str) -> ::core::result::Result<Self, $crate::ClientError> {
60 ::core::result::Result::Ok(Self {
61 inner: $crate::Client::new(base_url)?,
62 })
63 }
64
65 /// Create a client with a custom [`ClientConfig`](crate::ClientConfig).
66 $vis fn with_config(
67 base_url: &str,
68 config: $crate::ClientConfig,
69 ) -> ::core::result::Result<Self, $crate::ClientError> {
70 ::core::result::Result::Ok(Self {
71 inner: $crate::Client::with_config(base_url, config)?,
72 })
73 }
74
75 /// Create a client wrapping an existing [`Client`](crate::Client).
76 $vis fn from_client(client: $crate::Client) -> Self {
77 Self { inner: client }
78 }
79
80 /// Returns a reference to the inner [`Client`](crate::Client).
81 $vis fn inner(&self) -> &$crate::Client {
82 &self.inner
83 }
84
85 $(
86 $(#[$method_meta])*
87 $vis async fn $method_name(
88 &self,
89 args: <$Endpoint as $crate::CallEndpoint>::Args,
90 ) -> ::core::result::Result<
91 <$Endpoint as $crate::CallEndpoint>::Response,
92 $crate::ClientError,
93 > {
94 self.inner.call::<$Endpoint>(args).await
95 }
96 )*
97 }
98 };
99}