Skip to main content

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}