moonpool_transport/rpc/interface.rs
1//! FDB-style Interface pattern support.
2//!
3//! This module provides traits and helpers for working with interfaces
4//! generated by `#[derive(Interface)]`.
5//!
6//! # Overview
7//!
8//! The Interface pattern bundles multiple RPC methods into a single unit:
9//!
10//! - **Server-side**: `{Name}Server<C>` struct with `RequestStream` fields
11//! - **Client-side**: `{Name}Client` struct with method accessors
12//!
13//! # Example
14//!
15//! ```rust,ignore
16//! use moonpool_transport::Interface;
17//!
18//! #[derive(Interface)]
19//! #[interface(id = 0xCA1C_0000)]
20//! struct Calculator {
21//! add: (AddRequest, AddResponse),
22//! sub: (SubRequest, SubResponse),
23//! }
24//!
25//! // Server
26//! let calc = CalculatorServer::init(&transport, JsonCodec);
27//!
28//! // Client
29//! let calc = CalculatorClient::new(server_addr);
30//! send_request(&transport, &calc.add(), request, JsonCodec)?;
31//! ```
32//!
33//! # Serialization
34//!
35//! Client interfaces serialize only the base endpoint. Method endpoints
36//! are derived using `Endpoint::adjusted()`, following FoundationDB's pattern.
37
38use crate::{Endpoint, UID};
39
40/// Generate method endpoint from base endpoint and index.
41///
42/// Uses FDB's adjustment pattern via `Endpoint::adjusted()`.
43///
44/// # Example
45///
46/// ```rust,ignore
47/// let base = Endpoint::new(addr, UID::new(0xCA1C, 0));
48/// let add_endpoint = method_endpoint(&base, 0);
49/// let sub_endpoint = method_endpoint(&base, 1);
50/// ```
51pub fn method_endpoint(base: &Endpoint, method_index: u32) -> Endpoint {
52 base.adjusted(method_index)
53}
54
55/// Generate UID for a specific interface method.
56///
57/// # Example
58///
59/// ```rust,ignore
60/// let add_uid = method_uid(0xCA1C_0000, 0);
61/// let sub_uid = method_uid(0xCA1C_0000, 1);
62/// ```
63pub const fn method_uid(interface_id: u64, method_index: u32) -> UID {
64 UID::new(interface_id, method_index as u64)
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 use crate::NetworkAddress;
71 use std::net::{IpAddr, Ipv4Addr};
72
73 #[test]
74 fn test_method_endpoint() {
75 let addr = NetworkAddress::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 4500);
76 let base = Endpoint::new(addr, UID::new(0xCA1C_0000, 0));
77
78 let e0 = method_endpoint(&base, 0);
79 let e1 = method_endpoint(&base, 1);
80 let e2 = method_endpoint(&base, 2);
81
82 // All should have same address
83 assert_eq!(base.address, e0.address);
84 assert_eq!(base.address, e1.address);
85 assert_eq!(base.address, e2.address);
86
87 // Tokens should be different
88 assert_ne!(e0.token, e1.token);
89 assert_ne!(e1.token, e2.token);
90 assert_ne!(e0.token, e2.token);
91
92 // Method 0 should match base
93 assert_eq!(base.token, e0.token);
94 }
95
96 #[test]
97 fn test_method_uid() {
98 let interface_id = 0xCA1C_0000_u64;
99
100 let uid0 = method_uid(interface_id, 0);
101 let uid1 = method_uid(interface_id, 1);
102 let uid2 = method_uid(interface_id, 2);
103
104 assert_eq!(uid0, UID::new(interface_id, 0));
105 assert_eq!(uid1, UID::new(interface_id, 1));
106 assert_eq!(uid2, UID::new(interface_id, 2));
107
108 // All unique
109 assert_ne!(uid0, uid1);
110 assert_ne!(uid1, uid2);
111 }
112}