1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
//! Free functions.

//---------------------------------------------------------------------------------------------------- Use
use axum::Router;

use crate::{
    route::{bin, fallback, json_rpc, other},
    rpc_handler::RpcHandler,
};

//---------------------------------------------------------------------------------------------------- RouterBuilder
/// Generate the `RouterBuilder` struct.
macro_rules! generate_router_builder {
    ($(
        // Syntax:
        // $BUILDER_FUNCTION_NAME =>
        // $ACTUAL_ENDPOINT_STRING =>
        // $ENDPOINT_FUNCTION_MODULE::$ENDPOINT_FUNCTION =>
        // ($HTTP_METHOD(s))
        $endpoint_ident:ident =>
        $endpoint_string:literal =>
        $endpoint_module:ident::$endpoint_fn:ident =>
        ($($http_method:ident),*)
    ),* $(,)?) => {
        /// Builder for creating the RPC router.
        ///
        /// This builder allows you to selectively enable endpoints for the router,
        /// and a [`fallback`](RouterBuilder::fallback) route.
        ///
        /// The [`default`](RouterBuilder::default) is to enable [`all`](RouterBuilder::all) routes.
        ///
        /// # Routes
        /// Functions that enable routes are separated into 3 groups:
        /// - `json_rpc` (enables all of JSON RPC 2.0)
        /// - `other_` (e.g. [`other_get_height`](RouterBuilder::other_get_height))
        /// - `bin_` (e.g. [`bin_get_blocks`](RouterBuilder::bin_get_blocks))
        ///
        /// For a list of all `monerod` routes, see
        /// [here](https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.h#L97-L189),
        /// or the source file of this type.
        ///
        /// # Aliases
        /// Some routes have aliases, such as [`/get_height`](RouterBuilder::other_get_height)
        /// and [`/getheight`](RouterBuilder::other_getheight).
        ///
        /// These both route to the same handler function, but they do not enable each other.
        ///
        /// If desired, you can enable `/get_height` but not `/getheight`.
        ///
        /// # Example
        /// ```rust
        /// use cuprate_rpc_interface::{RouterBuilder, RpcHandlerDummy};
        ///
        /// // Create a router with _only_ `/json_rpc` enabled.
        /// let only_json_rpc = RouterBuilder::<RpcHandlerDummy>::new()
        ///     .json_rpc()
        ///     .build();
        ///
        /// // Create a router with:
        /// // - `/get_outs.bin` enabled
        /// // - A fallback enabled
        /// let get_outs_bin_and_fallback = RouterBuilder::<RpcHandlerDummy>::new()
        ///     .bin_get_outs()
        ///     .fallback()
        ///     .build();
        ///
        /// // Create a router with all endpoints enabled.
        /// let all = RouterBuilder::<RpcHandlerDummy>::new()
        ///     .all()
        ///     .build();
        /// ```
        #[derive(Clone)]
        pub struct RouterBuilder<H: RpcHandler> {
            router: Router<H>,
        }

        impl<H: RpcHandler> RouterBuilder<H> {
            /// Create a new [`Self`].
            #[must_use]
            pub fn new() -> Self {
                Self {
                    router: Router::new(),
                }
            }

            /// Build [`Self`] into a [`Router`].
            ///
            /// All endpoints enabled in [`RouterBuilder`]
            /// will be enabled in this [`Router`].
            pub fn build(self) -> Router<H> {
                self.router
            }

            /// Enable all endpoints, including [`Self::fallback`].
            #[must_use]
            pub fn all(mut self) -> Self {
                $(
                    self = self.$endpoint_ident();
                )*

                self.fallback()
            }

            /// Enable the catch-all fallback route.
            ///
            /// Any unknown or disabled route will route here, e.g.:
            /// - `get_info`
            /// - `getinfo`
            /// - `asdf`
            #[must_use]
            pub fn fallback(self) -> Self {
                Self {
                    router: self.router.fallback(fallback::fallback),
                }
            }

            $(
                #[doc = concat!(
                    "Enable the `",
                    $endpoint_string,
                    "` endpoint.",
                )]
                #[must_use]
                pub fn $endpoint_ident(self) -> Self {
                    Self {
                        router: self.router.route(
                            $endpoint_string,
                            ::axum::routing::method_routing::MethodRouter::new()
                                $(.$http_method($endpoint_module::$endpoint_fn::<H>))*
                        ),
                    }
                }
            )*
        }
    };
}

generate_router_builder! {
    // JSON-RPC 2.0 route.
    json_rpc => "/json_rpc" => json_rpc::json_rpc => (get, post),

    // Other JSON routes.
    other_get_height                  => "/get_height"                  => other::get_height                  => (get, post),
    other_getheight                   => "/getheight"                   => other::get_height                  => (get, post),
    other_get_transactions            => "/get_transactions"            => other::get_transactions            => (get, post),
    other_gettransactions             => "/gettransactions"             => other::get_transactions            => (get, post),
    other_get_alt_blocks_hashes       => "/get_alt_blocks_hashes"       => other::get_alt_blocks_hashes       => (get, post),
    other_is_key_image_spent          => "/is_key_image_spent"          => other::is_key_image_spent          => (get, post),
    other_send_raw_transaction        => "/send_raw_transaction"        => other::send_raw_transaction        => (get, post),
    other_sendrawtransaction          => "/sendrawtransaction"          => other::send_raw_transaction        => (get, post),
    other_start_mining                => "/start_mining"                => other::start_mining                => (get, post),
    other_stop_mining                 => "/stop_mining"                 => other::stop_mining                 => (get, post),
    other_mining_status               => "/mining_status"               => other::mining_status               => (get, post),
    other_save_bc                     => "/save_bc"                     => other::save_bc                     => (get, post),
    other_get_peer_list               => "/get_peer_list"               => other::get_peer_list               => (get, post),
    other_get_public_nodes            => "/get_public_nodes"            => other::get_public_nodes            => (get, post),
    other_set_log_hash_rate           => "/set_log_hash_rate"           => other::set_log_hash_rate           => (get, post),
    other_set_log_level               => "/set_log_level"               => other::set_log_level               => (get, post),
    other_set_log_categories          => "/set_log_categories"          => other::set_log_categories          => (get, post),
    other_get_transaction_pool        => "/get_transaction_pool"        => other::get_transaction_pool        => (get, post),
    other_get_transaction_pool_hashes => "/get_transaction_pool_hashes" => other::get_transaction_pool_hashes => (get, post),
    other_get_transaction_pool_stats  => "/get_transaction_pool_stats"  => other::get_transaction_pool_stats  => (get, post),
    other_set_bootstrap_daemon        => "/set_bootstrap_daemon"        => other::set_bootstrap_daemon        => (get, post),
    other_stop_daemon                 => "/stop_daemon"                 => other::stop_daemon                 => (get, post),
    other_get_net_stats               => "/get_net_stats"               => other::get_net_stats               => (get, post),
    other_get_limit                   => "/get_limit"                   => other::get_limit                   => (get, post),
    other_set_limit                   => "/set_limit"                   => other::set_limit                   => (get, post),
    other_out_peers                   => "/out_peers"                   => other::out_peers                   => (get, post),
    other_in_peers                    => "/in_peers"                    => other::in_peers                    => (get, post),
    other_get_outs                    => "/get_outs"                    => other::get_outs                    => (get, post),
    other_update                      => "/update"                      => other::update                      => (get, post),
    other_pop_blocks                  => "/pop_blocks"                  => other::pop_blocks                  => (get, post),

    // Binary routes.
    bin_get_blocks                  => "/get_blocks.bin"                  => bin::get_blocks                  => (get, post),
    bin_getblocks                   => "/getblocks.bin"                   => bin::get_blocks                  => (get, post),
    bin_get_blocks_by_height        => "/get_blocks_by_height.bin"        => bin::get_blocks_by_height        => (get, post),
    bin_getblocks_by_height         => "/getblocks_by_height.bin"         => bin::get_blocks_by_height        => (get, post),
    bin_get_hashes                  => "/get_hashes.bin"                  => bin::get_hashes                  => (get, post),
    bin_gethashes                   => "/gethashes.bin"                   => bin::get_hashes                  => (get, post),
    bin_get_o_indexes               => "/get_o_indexes.bin"               => bin::get_o_indexes               => (get, post),
    bin_get_outs                    => "/get_outs.bin"                    => bin::get_outs                    => (get, post),
    bin_get_transaction_pool_hashes => "/get_transaction_pool_hashes.bin" => bin::get_transaction_pool_hashes => (get, post),
    bin_get_output_distribution     => "/get_output_distribution.bin"     => bin::get_output_distribution     => (get, post),
}

impl<H: RpcHandler> Default for RouterBuilder<H> {
    /// Uses [`Self::all`].
    fn default() -> Self {
        Self::new().all()
    }
}