Skip to main content

appcore_capabilities/
peer_rpc_invoker.rs

1// =============================================================================
2//        #######
3//     ###       ###     F: peer_rpc_invoker.rs
4//    ##   ## ##   ##    P: AppCore-Runtime
5//         ## ##
6//                       C: 2026/07/22 15:41:18 by dnettoRaw
7//    ##   ## ##   ##    U: 2026/07/22 15:41:18 by dnettoRaw
8//      ###########      S: 1.0.1-rc.8
9// =============================================================================
10
11use crate::{
12    CapabilityError, CapabilityRequest, CapabilityResponse, CapabilityResult,
13    RemoteCapabilityInvoker,
14};
15use appcore_core::CapabilityMode;
16use appcore_distributed_contracts::{
17    PeerRecord, PeerRpcCallKind, PeerRpcClientExecutor, PeerRpcOutboundRequest,
18};
19
20/// Remote capability invoker backed by the stable peer RPC client contract.
21pub struct PeerRpcRemoteCapabilityInvoker<C> {
22    client: C,
23}
24
25impl<C> PeerRpcRemoteCapabilityInvoker<C> {
26    /// Creates an invoker using the supplied peer RPC executor.
27    pub fn new(client: C) -> Self {
28        Self { client }
29    }
30}
31
32impl<C> RemoteCapabilityInvoker for PeerRpcRemoteCapabilityInvoker<C>
33where
34    C: PeerRpcClientExecutor,
35{
36    fn invoke_remote(
37        &self,
38        peer: &PeerRecord,
39        request: &CapabilityRequest,
40    ) -> CapabilityResult<CapabilityResponse> {
41        let endpoint_url = peer_rpc_endpoint(peer).ok_or_else(|| {
42            CapabilityError::RemoteEndpointUnavailable(request.capability.clone())
43        })?;
44        let kind = match request.mode {
45            CapabilityMode::Query => PeerRpcCallKind::Query,
46            CapabilityMode::Command => PeerRpcCallKind::Command,
47            CapabilityMode::Stream => {
48                return Err(CapabilityError::HandlerRejected(
49                    "stream_remote_invocation_not_supported".to_string(),
50                ));
51            }
52        };
53        let response = self
54            .client
55            .call_peer(
56                endpoint_url,
57                kind,
58                PeerRpcOutboundRequest::new(
59                    request.request_id.clone(),
60                    peer.identity.core_id.clone(),
61                    request.capability.clone(),
62                    request.payload.clone(),
63                    request.idempotency_key.clone(),
64                    request.trace.clone(),
65                ),
66            )
67            .map_err(|error| CapabilityError::RemoteInvocationFailed(format!("{error:?}")))?;
68        if response.ok {
69            return Ok(CapabilityResponse::accepted(
70                response.payload,
71                Some(peer.identity.core_id.clone()),
72            ));
73        }
74        Ok(CapabilityResponse::rejected(
75            response
76                .error
77                .unwrap_or_else(|| "remote_rejected".to_string()),
78        ))
79    }
80}
81
82fn peer_rpc_endpoint(peer: &PeerRecord) -> Option<&str> {
83    peer.endpoints
84        .iter()
85        .find(|endpoint| {
86            endpoint.name == "peer-rpc"
87                || endpoint.name == "peer_rpc"
88                || endpoint.protocol == "appcore-peer-rpc"
89        })
90        .map(|endpoint| endpoint.url.as_str())
91}