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
/*
* Copyright (c) 2024. Govcraft
*
* Licensed under either of
* * Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* * MIT license: http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the applicable License for the specific language governing permissions and
* limitations under that License.
*/
//! Declares which requests can cross a process boundary, enabling
//! [`RemoteActorRef::ask`].
//!
//! [`RemoteActorRef::ask`]: crate::common::ipc::RemoteActorRef::ask
use Serialize;
use DeserializeOwned;
use crateRequest;
/// A [`Request`] that can also be asked of an actor in **another process**.
///
/// [`Request`] alone is enough to [`ask`](crate::traits::ActorHandleInterface::ask) a local
/// actor, because a local reply travels as a `dyn` message over an in-process channel and
/// never needs a wire form. Crossing a process boundary does need one, in both directions —
/// hence the extra bounds here rather than on `Request`, which would tax every local-only
/// caller for a capability they never use.
///
/// # Why this is a separate trait
///
/// So that asking a remote actor for something unserializable is a **compile error** rather
/// than a call that appears to work and cannot. A message that only implements `Request` can
/// be asked of a local handle and nowhere else, and the compiler says so at the call site.
///
/// # The name is the contract
///
/// [`MESSAGE_TYPE`](Self::MESSAGE_TYPE) must match the name the **peer** registered this
/// type under:
///
/// ```rust,ignore
/// // In the server process:
/// runtime.ipc_registry().register::<GetCount>("GetCount");
/// ```
///
/// It is spelled out rather than derived from [`std::any::type_name`] because the two are
/// not the same thing and cannot be kept the same by the compiler. `type_name` yields a
/// path that changes when a type moves between modules, and the peer may be a different
/// binary, a different version of this crate, or not written in Rust at all. A wire name is
/// a protocol constant, so it is written down as one. A mismatch is reported as
/// [`AskError::PeerRejected`](crate::common::AskError::PeerRejected) with the peer's
/// `UNKNOWN_MESSAGE_TYPE` code, not silently ignored.
///
/// The reply type must be registered on the peer too. If it is not, the peer's listener
/// still answers, with a diagnostic fallback payload rather than the real reply; that
/// arrives as [`AskError::UnexpectedReply`](crate::common::AskError::UnexpectedReply)
/// carrying the payload, which names the unregistered type.
///
/// # Example
///
/// ```rust
/// # #[cfg(feature = "ipc")] {
/// use acton_reactive::prelude::*;
///
/// #[acton_message(ipc)]
/// struct GetCount;
///
/// #[acton_message(ipc)]
/// struct Count {
/// value: usize,
/// }
///
/// impl Request for GetCount {
/// type Response = Count;
/// }
///
/// impl RemoteRequest for GetCount {
/// const MESSAGE_TYPE: &'static str = "GetCount";
/// }
/// # }
/// ```
///
/// `#[acton_message(ipc)]` adds the `serde` derives; it does not register the type, which
/// stays an explicit step on whichever side receives the message.