Add router-aware endpoint topology APIs

This commit is contained in:
Michael Mikovsky
2026-04-26 16:13:28 -06:00
parent 99d1097f2a
commit 371f3ae492
9 changed files with 669 additions and 54 deletions
+147 -34
View File
@@ -11,7 +11,9 @@ use crate::protocol::{
use super::{
Endpoint, EndpointError, HookKey, Ingress, LocalEvent, ProtocolEndpoint, ProtocolLeaf,
RouteDecision, RouterLeaf,
};
use super::endpoint::ForwardedFrame;
/// One typed incoming `Call` passed to a leaf procedure.
///
@@ -366,6 +368,28 @@ pub struct RuntimeOutcome {
pub dropped: bool,
}
/// Frames emitted by the runtime together with their chosen next hops.
///
/// What it is: the router-oriented variant of [`RuntimeOutcome`], preserving the
/// `RouteDecision` for every emitted frame.
///
/// Why it exists: transport-owning leaves need to know whether each frame should
/// go to the parent or to a specific child, not just the encoded bytes.
///
/// # Example
/// ```rust
/// use unshell::protocol::tree::RoutedRuntimeOutcome;
/// let outcome = RoutedRuntimeOutcome::default();
/// assert!(outcome.forwarded.is_empty());
/// ```
#[derive(Debug, Default, Clone)]
pub struct RoutedRuntimeOutcome {
/// Forwarded frames paired with the route chosen by the endpoint runtime.
pub forwarded: Vec<ForwardedFrame>,
/// Whether the endpoint dropped the incoming packet.
pub dropped: bool,
}
impl<L> LeafRuntime<L> {
/// Builds a runtime from one endpoint and one leaf instance.
#[must_use]
@@ -453,8 +477,32 @@ where
ingress: &Ingress,
frame: FrameBytes,
) -> Result<RuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
let routed = self.receive_routed(ingress, frame)?;
Ok(RuntimeOutcome {
frames: routed
.forwarded
.into_iter()
.map(|forwarded| forwarded.frame)
.collect(),
dropped: routed.dropped,
})
}
/// Delivers one inbound frame while preserving route decisions for emitted traffic.
///
/// # Example
/// ```rust
/// # use unshell::protocol::tree::{LeafRuntime, ProtocolEndpoint};
/// # struct ExampleLeaf;
/// # let _ = core::marker::PhantomData::<LeafRuntime<ExampleLeaf>>;
/// ```
pub fn receive_routed(
&mut self,
ingress: &Ingress,
frame: FrameBytes,
) -> Result<RoutedRuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
let outcome = self.endpoint.receive(ingress, frame)?;
self.process_endpoint_outcome(outcome)
self.process_endpoint_outcome_routed(outcome)
}
/// Polls the leaf for locally-generated hook traffic and routes any emitted frames.
@@ -466,21 +514,45 @@ where
/// # let _ = core::marker::PhantomData::<LeafRuntime<ExampleLeaf>>;
/// ```
pub fn poll(&mut self) -> Result<RuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
let outgoing = self.leaf.poll().map_err(LeafRuntimeError::Leaf)?;
self.emit_outgoing(outgoing)
let routed = self.poll_routed()?;
Ok(RuntimeOutcome {
frames: routed
.forwarded
.into_iter()
.map(|forwarded| forwarded.frame)
.collect(),
dropped: routed.dropped,
})
}
fn process_endpoint_outcome(
/// Polls the leaf while preserving route decisions for emitted traffic.
///
/// # Example
/// ```rust
/// # use unshell::protocol::tree::{LeafRuntime, ProtocolEndpoint};
/// # struct ExampleLeaf;
/// # let _ = core::marker::PhantomData::<LeafRuntime<ExampleLeaf>>;
/// ```
pub fn poll_routed(
&mut self,
) -> Result<RoutedRuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
let outgoing = self.leaf.poll().map_err(LeafRuntimeError::Leaf)?;
self.emit_outgoing_routed(outgoing)
}
fn process_endpoint_outcome_routed(
&mut self,
outcome: crate::protocol::tree::EndpointOutcome,
) -> Result<RuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
) -> Result<RoutedRuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
match outcome {
crate::protocol::tree::EndpointOutcome::Forward { frame, .. } => Ok(RuntimeOutcome {
frames: vec![frame],
dropped: false,
}),
crate::protocol::tree::EndpointOutcome::Dropped => Ok(RuntimeOutcome {
frames: Vec::new(),
crate::protocol::tree::EndpointOutcome::Forward { route, frame } => {
Ok(RoutedRuntimeOutcome {
forwarded: vec![ForwardedFrame { route, frame }],
dropped: false,
})
}
crate::protocol::tree::EndpointOutcome::Dropped => Ok(RoutedRuntimeOutcome {
forwarded: Vec::new(),
dropped: true,
}),
crate::protocol::tree::EndpointOutcome::Local(event) => self.process_local_event(event),
@@ -490,7 +562,7 @@ where
fn process_local_event(
&mut self,
event: LocalEvent,
) -> Result<RuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
) -> Result<RoutedRuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
match event {
LocalEvent::Call { header, message } => self.process_local_call(header, message),
LocalEvent::Data {
@@ -510,7 +582,7 @@ where
&mut self,
header: PacketHeader,
message: CallMessage,
) -> Result<RuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
) -> Result<RoutedRuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
let CallMessage {
procedure_id,
data,
@@ -528,19 +600,16 @@ where
},
};
match self.leaf.dispatch_call(incoming) {
match self.leaf.dispatch_call(&mut self.endpoint, incoming) {
Ok(CallReply::Reply(bytes)) => {
let frames = if let Some(hook) = response_hook {
self.send_reply_data(hook, procedure_id, bytes, true)?
} else {
Vec::new()
RoutedRuntimeOutcome::default()
};
Ok(RuntimeOutcome {
frames,
dropped: false,
})
Ok(frames)
}
Ok(CallReply::NoReply) => Ok(RuntimeOutcome::default()),
Ok(CallReply::NoReply) => Ok(RoutedRuntimeOutcome::default()),
Err(error) => {
// Dispatch failures still emit a protocol fault for the remote caller when a
// response hook exists, even though the local runtime also surfaces the error.
@@ -555,7 +624,7 @@ where
header: PacketHeader,
message: DataMessage,
hook_key: HookKey,
) -> Result<RuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
) -> Result<RoutedRuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
let outgoing = self
.leaf
.on_data(IncomingData {
@@ -564,7 +633,7 @@ where
hook_key,
})
.map_err(LeafRuntimeError::Leaf)?;
self.emit_outgoing(outgoing)
self.emit_outgoing_routed(outgoing)
}
fn process_local_fault(
@@ -572,7 +641,7 @@ where
header: PacketHeader,
message: crate::protocol::FaultMessage,
hook_key: HookKey,
) -> Result<RuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
) -> Result<RoutedRuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
self.leaf
.on_fault(IncomingFault {
header,
@@ -580,14 +649,14 @@ where
hook_key,
})
.map_err(LeafRuntimeError::Leaf)?;
Ok(RuntimeOutcome::default())
Ok(RoutedRuntimeOutcome::default())
}
fn emit_outgoing(
fn emit_outgoing_routed(
&mut self,
outgoing: Vec<OutgoingData>,
) -> Result<RuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
let mut runtime = RuntimeOutcome::default();
) -> Result<RoutedRuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
let mut runtime = RoutedRuntimeOutcome::default();
for packet in outgoing {
let endpoint_outcome = self.endpoint.send_data(
packet.dst_path,
@@ -597,8 +666,8 @@ where
packet.end_hook,
)?;
runtime
.frames
.extend(self.process_endpoint_outcome(endpoint_outcome)?.frames);
.forwarded
.extend(self.process_endpoint_outcome_routed(endpoint_outcome)?.forwarded);
}
Ok(runtime)
}
@@ -609,7 +678,7 @@ where
procedure_id: String,
bytes: Vec<u8>,
end_hook: bool,
) -> Result<Vec<FrameBytes>, LeafRuntimeError<<L as CallLeaf>::Error>> {
) -> Result<RoutedRuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
let endpoint_outcome = self.endpoint.send_data(
hook.return_path,
hook.hook_id,
@@ -617,21 +686,65 @@ where
bytes,
end_hook,
)?;
Ok(self.process_endpoint_outcome(endpoint_outcome)?.frames)
self.process_endpoint_outcome_routed(endpoint_outcome)
}
fn emit_internal_fault_if_possible(
&mut self,
hook: Option<&HookTarget>,
) -> Result<Vec<FrameBytes>, LeafRuntimeError<<L as CallLeaf>::Error>> {
) -> Result<RoutedRuntimeOutcome, LeafRuntimeError<<L as CallLeaf>::Error>> {
let Some(hook) = hook else {
return Ok(Vec::new());
return Ok(RoutedRuntimeOutcome::default());
};
let key = HookKey::new(hook.return_path.clone(), hook.hook_id);
let outcome = self
.endpoint
.emit_fault_if_possible(Some(key), ProtocolFault::INTERNAL_ERROR)?;
Ok(self.process_endpoint_outcome(outcome)?.frames)
self.process_endpoint_outcome_routed(outcome)
}
}
impl<L> LeafRuntime<L>
where
L: CallLeaf + super::CallProcedures<Error = <L as CallLeaf>::Error> + RouterLeaf,
{
/// Sends previously forwarded frames through the router leaf's parent/child links.
///
/// What it is: a small transport bridge from endpoint route decisions to the
/// leaf-owned connections.
///
/// Why it exists: router leaves should be able to reuse the normal protocol
/// runtime and still own the concrete forwarding mechanism.
///
/// # Example
/// ```rust
/// # use unshell::protocol::tree::{LeafRuntime, ProtocolEndpoint};
/// # struct ExampleLeaf;
/// # let _ = core::marker::PhantomData::<LeafRuntime<ExampleLeaf>>;
/// ```
pub fn route_forwarded(
&mut self,
forwarded: Vec<ForwardedFrame>,
) -> Result<(), <L as RouterLeaf>::RouteError> {
for forwarded in forwarded {
match forwarded.route {
RouteDecision::Parent => {
self.leaf
.route_to_parent(self.endpoint.path(), forwarded.frame)?;
}
RouteDecision::Child(index) => {
let child_path = self
.endpoint
.child_routes()
.get(index)
.map(|child| child.path.clone())
.unwrap_or_default();
self.leaf.route_to_child(&child_path, forwarded.frame)?;
}
RouteDecision::Local | RouteDecision::Drop => {}
}
}
Ok(())
}
}
@@ -128,6 +128,7 @@ impl ProtocolEndpoint {
children: Vec<ChildRoute>,
leaves: Vec<LeafSpec>,
) -> Self {
let has_parent = parent_path.is_some();
let registered_child_paths = children
.iter()
.filter(|child| child.registered)
@@ -136,7 +137,8 @@ impl ProtocolEndpoint {
Self {
local_id: None,
routing: CompiledRoutes::new(&path, &registered_child_paths, parent_path.is_some()),
parent_path,
routing: CompiledRoutes::new(&path, &registered_child_paths, has_parent),
path,
children,
leaves: leaves
@@ -194,6 +196,129 @@ impl ProtocolEndpoint {
self.local_id.as_deref()
}
/// Returns the absolute path of this endpoint's direct parent, if one exists.
///
/// What it is: the currently configured one-hop parent boundary for this
/// endpoint.
///
/// Why it exists: router-style leaves need to expose and inspect the tree edge
/// they use for upstream traffic.
///
/// # Example
/// ```rust
/// use unshell::protocol::tree::ProtocolEndpoint;
/// let endpoint = ProtocolEndpoint::new(vec!["worker".into()], Some(Vec::new()), Vec::new(), Vec::new());
/// assert_eq!(endpoint.parent_path(), Some([].as_slice()));
/// ```
pub fn parent_path(&self) -> Option<&[String]> {
self.parent_path.as_deref()
}
/// Returns the direct child routes currently known to this endpoint.
///
/// What it is: the local routing-table inputs for direct descendants.
///
/// Why it exists: management leaves often need to inspect or mirror the child
/// topology they are controlling.
///
/// # Example
/// ```rust
/// use unshell::protocol::tree::{ChildRoute, ProtocolEndpoint};
/// let endpoint = ProtocolEndpoint::new(
/// vec!["root".into()],
/// None,
/// vec![ChildRoute::registered(vec!["root".into(), "child".into()])],
/// Vec::new(),
/// );
/// assert_eq!(endpoint.child_routes().len(), 1);
/// ```
pub fn child_routes(&self) -> &[ChildRoute] {
&self.children
}
/// Replaces the configured direct parent path and recompiles local routing.
///
/// What it is: the supported way to attach or detach this endpoint from its
/// upstream boundary.
///
/// Why it exists: a router leaf should be able to promote or remove its parent
/// connection without rebuilding the entire endpoint.
///
/// # Example
/// ```rust
/// use unshell::protocol::tree::ProtocolEndpoint;
/// let mut endpoint = ProtocolEndpoint::new(vec!["root".into(), "worker".into()], Some(vec!["root".into()]), Vec::new(), Vec::new());
/// endpoint.set_parent_path(None)?;
/// assert!(endpoint.parent_path().is_none());
/// # Ok::<(), unshell::protocol::tree::EndpointError>(())
/// ```
pub fn set_parent_path(
&mut self,
parent_path: Option<Vec<String>>,
) -> Result<(), EndpointError> {
if let Some(path) = parent_path.as_deref() {
self.validate_direct_parent_path(path)?;
}
self.parent_path = parent_path;
self.rebuild_routing();
Ok(())
}
/// Inserts or updates one direct child route and recompiles local routing.
///
/// What it is: the supported mutation API for the endpoint's child list.
///
/// Why it exists: router-management leaves need one invariant-preserving way to
/// reflect child connection changes into path routing.
///
/// # Example
/// ```rust
/// use unshell::protocol::tree::{ChildRoute, ProtocolEndpoint};
/// let mut endpoint = ProtocolEndpoint::new(vec!["root".into()], None, Vec::new(), Vec::new());
/// endpoint.upsert_child_route(ChildRoute::registered(vec!["root".into(), "child".into()]))?;
/// assert_eq!(endpoint.child_routes().len(), 1);
/// # Ok::<(), unshell::protocol::tree::EndpointError>(())
/// ```
pub fn upsert_child_route(&mut self, route: ChildRoute) -> Result<(), EndpointError> {
self.validate_direct_child_path(&route.path)?;
if let Some(existing) = self.children.iter_mut().find(|child| child.path == route.path) {
*existing = route;
} else {
self.children.push(route);
}
self.rebuild_routing();
Ok(())
}
/// Removes one direct child route by absolute path and recompiles local routing.
///
/// What it is: the supported mutation API for pruning a direct descendant.
///
/// Why it exists: connection-management leaves need to tear down routes without
/// mutating the endpoint internals directly.
///
/// # Example
/// ```rust
/// use unshell::protocol::tree::{ChildRoute, ProtocolEndpoint};
/// let mut endpoint = ProtocolEndpoint::new(
/// vec!["root".into()],
/// None,
/// vec![ChildRoute::registered(vec!["root".into(), "child".into()])],
/// Vec::new(),
/// );
/// assert!(endpoint.remove_child_route(&[String::from("root"), String::from("child")]));
/// assert!(endpoint.child_routes().is_empty());
/// ```
pub fn remove_child_route(&mut self, path: &[String]) -> bool {
let original_len = self.children.len();
self.children.retain(|child| child.path != path);
let removed = self.children.len() != original_len;
if removed {
self.rebuild_routing();
}
removed
}
/// Registers a procedure that is handled directly by the endpoint.
///
/// Endpoint-level procedures exist for protocol services that are not attached to one leaf,
@@ -230,6 +355,43 @@ impl ProtocolEndpoint {
self.hooks.allocate_hook_id(&self.path)
}
fn rebuild_routing(&mut self) {
let registered_child_paths = self
.children
.iter()
.filter(|child| child.registered)
.map(|child| child.path.clone())
.collect::<Vec<_>>();
self.routing = CompiledRoutes::new(
&self.path,
&registered_child_paths,
self.parent_path.is_some(),
);
}
fn validate_direct_parent_path(&self, parent_path: &[String]) -> Result<(), EndpointError> {
let Some((_, expected_parent)) = self.path.split_last() else {
return Err(EndpointError::Validation(ValidationError::TopologyInvariant(
"root endpoints cannot declare a parent path",
)));
};
if parent_path != expected_parent {
return Err(EndpointError::Validation(ValidationError::TopologyInvariant(
"parent path must equal the direct path prefix of this endpoint",
)));
}
Ok(())
}
fn validate_direct_child_path(&self, child_path: &[String]) -> Result<(), EndpointError> {
if child_path.len() != self.path.len() + 1 || !child_path.starts_with(&self.path) {
return Err(EndpointError::Validation(ValidationError::TopologyInvariant(
"child path must be one direct descendant of this endpoint",
)));
}
Ok(())
}
/// Encodes a call frame without routing it through the local endpoint.
///
/// This exists for callers that want a fully encoded outbound frame while handling transport
@@ -177,6 +177,33 @@ pub enum EndpointOutcome {
Dropped,
}
/// One framed packet together with the next hop selected by endpoint routing.
///
/// What it is: a transport-ready frame paired with the resolved direction the
/// endpoint chose for it.
///
/// Why it exists: high-level runtimes often flatten forwarded traffic down to raw
/// bytes, but router-host leaves need the route decision so they can choose the
/// correct parent or child connection.
///
/// # Example
/// ```rust
/// use unshell::protocol::FrameBytes;
/// use unshell::protocol::tree::{ForwardedFrame, RouteDecision};
/// let forwarded = ForwardedFrame {
/// route: RouteDecision::Parent,
/// frame: FrameBytes::new(),
/// };
/// assert!(matches!(forwarded.route, RouteDecision::Parent));
/// ```
#[derive(Debug, Clone)]
pub struct ForwardedFrame {
/// The next hop selected by the endpoint runtime.
pub route: RouteDecision,
/// The encoded protocol frame to send over that hop.
pub frame: FrameBytes,
}
/// Error surfaced while validating or encoding protocol frames.
///
/// This exists so endpoint callers can preserve the distinction between malformed wire/archive
@@ -288,6 +315,7 @@ pub trait Endpoint {
pub struct ProtocolEndpoint {
pub(crate) local_id: Option<String>,
pub(crate) path: Vec<String>,
pub(crate) parent_path: Option<Vec<String>>,
pub(crate) children: Vec<ChildRoute>,
pub(crate) routing: CompiledRoutes,
pub(crate) leaves: BTreeMap<String, LeafSpec>,
@@ -11,6 +11,6 @@ mod introspection;
mod receive;
pub use core::{
ChildRoute, Endpoint, EndpointError, EndpointOutcome, Ingress, LeafSpec, LocalEvent,
ChildRoute, Endpoint, EndpointError, EndpointOutcome, ForwardedFrame, Ingress, LeafSpec, LocalEvent,
ProtocolEndpoint,
};
+67 -9
View File
@@ -7,7 +7,9 @@
use alloc::{string::String, vec::Vec};
use super::LeafSpec;
use crate::protocol::FrameBytes;
use super::{ChildRoute, LeafSpec, ProtocolEndpoint};
/// Static metadata for one application-defined protocol leaf.
///
@@ -167,7 +169,7 @@ pub trait LeafBinding: ProtocolLeaf {
/// impl CallProcedures for ExampleLeaf {
/// type Error = core::convert::Infallible;
/// fn procedure_suffixes() -> &'static [&'static str] { &["invoke"] }
/// fn dispatch_call(&mut self, _call: IncomingCall) -> Result<unshell::protocol::tree::CallReply, DispatchError<Self::Error>> {
/// fn dispatch_call(&mut self, _endpoint: &mut unshell::protocol::tree::ProtocolEndpoint, _call: IncomingCall) -> Result<unshell::protocol::tree::CallReply, DispatchError<Self::Error>> {
/// Ok(unshell::protocol::tree::CallReply::NoReply)
/// }
/// }
@@ -188,21 +190,77 @@ pub trait CallProcedures: LeafDeclaration {
/// use unshell::protocol::tree::{CallProcedures, DispatchError, IncomingCall, ProtocolLeaf};
/// struct ExampleLeaf;
/// impl ProtocolLeaf for ExampleLeaf { fn leaf_name() -> String { "org.example.v1.echo".into() } }
/// impl CallProcedures for ExampleLeaf {
/// type Error = core::convert::Infallible;
/// fn procedure_suffixes() -> &'static [&'static str] { &["invoke"] }
/// fn dispatch_call(&mut self, _call: IncomingCall) -> Result<unshell::protocol::tree::CallReply, DispatchError<Self::Error>> {
/// Ok(unshell::protocol::tree::CallReply::NoReply)
/// }
/// }
/// impl CallProcedures for ExampleLeaf {
/// type Error = core::convert::Infallible;
/// fn procedure_suffixes() -> &'static [&'static str] { &["invoke"] }
/// fn dispatch_call(&mut self, _endpoint: &mut unshell::protocol::tree::ProtocolEndpoint, _call: IncomingCall) -> Result<unshell::protocol::tree::CallReply, DispatchError<Self::Error>> {
/// Ok(unshell::protocol::tree::CallReply::NoReply)
/// }
/// }
/// # let _ = ExampleLeaf;
/// ```
fn dispatch_call(
&mut self,
endpoint: &mut ProtocolEndpoint,
call: crate::protocol::tree::IncomingCall,
) -> Result<crate::protocol::tree::CallReply, crate::protocol::tree::DispatchError<Self::Error>>;
}
/// Router-facing transport hooks for leaves that own parent/child connections.
///
/// What it is: an opt-in trait for leaves that want to act as the transport layer
/// for one endpoint's forwarded traffic.
///
/// Why it exists: ordinary leaves only need validated local events, but a router
/// leaf also needs to know its active parent/children and where to physically send
/// frames chosen by the endpoint's routing logic.
///
/// # Example
/// ```rust
/// use unshell::protocol::FrameBytes;
/// use unshell::protocol::tree::{ChildRoute, RouterLeaf};
/// #[derive(Default)]
/// struct DemoRouter {
/// parent: Option<Vec<String>>,
/// children: Vec<ChildRoute>,
/// }
/// impl unshell::protocol::tree::ProtocolLeaf for DemoRouter {
/// fn leaf_name() -> String { "org.example.v1.router".into() }
/// }
/// impl RouterLeaf for DemoRouter {
/// type RouteError = core::convert::Infallible;
///
/// fn parent_path(&self) -> Option<&[String]> { self.parent.as_deref() }
/// fn child_routes(&self) -> &[ChildRoute] { &self.children }
/// fn route_to_parent(&mut self, _local_path: &[String], _frame: FrameBytes) -> Result<(), Self::RouteError> { Ok(()) }
/// fn route_to_child(&mut self, _child_path: &[String], _frame: FrameBytes) -> Result<(), Self::RouteError> { Ok(()) }
/// }
/// ```
pub trait RouterLeaf: ProtocolLeaf {
/// Transport-specific error surfaced while handing a frame to the chosen link.
type RouteError;
/// Returns the currently connected direct parent path, if any.
fn parent_path(&self) -> Option<&[String]>;
/// Returns the currently connected direct child routes.
fn child_routes(&self) -> &[ChildRoute];
/// Sends one routed frame toward the direct parent connection.
fn route_to_parent(
&mut self,
local_path: &[String],
frame: FrameBytes,
) -> Result<(), Self::RouteError>;
/// Sends one routed frame toward the chosen direct child connection.
fn route_to_child(
&mut self,
child_path: &[String],
frame: FrameBytes,
) -> Result<(), Self::RouteError>;
}
/// Builds one canonical dotted leaf id from crate-local metadata plus optional
/// user overrides.
///
+4 -3
View File
@@ -16,8 +16,8 @@ mod routing;
pub use call::{
Call, CallLeaf, CallReply, CallResult, DispatchError, IncomingCall, IncomingData,
IncomingFault, LeafRuntime, LeafRuntimeError, OutgoingData, RuntimeOutcome, decode_call_input,
encode_call_reply,
IncomingFault, LeafRuntime, LeafRuntimeError, OutgoingData, RoutedRuntimeOutcome,
RuntimeOutcome, decode_call_input, encode_call_reply,
};
pub use endpoint::{
ChildRoute, Endpoint, EndpointError, EndpointOutcome, Ingress, LeafSpec, LocalEvent,
@@ -25,7 +25,8 @@ pub use endpoint::{
};
pub use hook::{ActiveHook, HookConflict, HookKey, HookTable, PendingHook};
pub use leaf::{
CallProcedures, LeafBinding, LeafDeclaration, ProtocolLeaf, derive_leaf_name, leaf_spec_of,
CallProcedures, LeafBinding, LeafDeclaration, ProtocolLeaf, RouterLeaf, derive_leaf_name,
leaf_spec_of,
};
pub use procedure::{
Procedure, ProcedureEffect, ProcedureMetadata, ProcedureRuntime, ProcedureRuntimeError,