mirror of
https://github.com/Astatin3/unshell.git
synced 2026-06-08 22:38:01 -06:00
Add root-default endpoint creation macro
This commit is contained in:
@@ -10,22 +10,19 @@
|
|||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
|
use unshell::create_endpoint;
|
||||||
use unshell::leaves::remote_shell;
|
use unshell::leaves::remote_shell;
|
||||||
use unshell::protocol::tree::{EndpointOutcome, LocalEvent, ProtocolEndpoint};
|
use unshell::protocol::tree::{Endpoint, EndpointOutcome, LocalEvent, ProtocolEndpoint};
|
||||||
use unshell::protocol::{INTROSPECTION_PROCEDURE_ID, LeafIntrospection};
|
use unshell::protocol::{INTROSPECTION_PROCEDURE_ID, LeafIntrospection};
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut endpoint: ProtocolEndpoint =
|
||||||
|
create_endpoint!("agent", remote_shell::endpoint::RemoteShell::default());
|
||||||
let leaf_spec = remote_shell::endpoint::RemoteShell::protocol_leaf_spec();
|
let leaf_spec = remote_shell::endpoint::RemoteShell::protocol_leaf_spec();
|
||||||
let mut endpoint = ProtocolEndpoint::new(
|
|
||||||
agent_path(),
|
|
||||||
Some(Vec::new()),
|
|
||||||
Vec::new(),
|
|
||||||
vec![leaf_spec.clone()],
|
|
||||||
);
|
|
||||||
|
|
||||||
let hook_id = endpoint.allocate_hook_id();
|
let hook_id = endpoint.allocate_hook_id();
|
||||||
let outcome = endpoint.send_call(
|
let outcome = endpoint.send_call(
|
||||||
agent_path(),
|
Vec::new(),
|
||||||
Some(remote_shell::endpoint::RemoteShell::protocol_leaf_name()),
|
Some(remote_shell::endpoint::RemoteShell::protocol_leaf_name()),
|
||||||
INTROSPECTION_PROCEDURE_ID,
|
INTROSPECTION_PROCEDURE_ID,
|
||||||
Some(hook_id),
|
Some(hook_id),
|
||||||
@@ -41,13 +38,10 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
"remote-shell examples normally listen on {}",
|
"remote-shell examples normally listen on {}",
|
||||||
remote_shell::endpoint::LISTEN_ADDR
|
remote_shell::endpoint::LISTEN_ADDR
|
||||||
);
|
);
|
||||||
println!("endpoint path: {:?}", agent_path());
|
println!("endpoint id: {:?}", endpoint.local_id());
|
||||||
|
println!("endpoint path: {:?}", endpoint.path());
|
||||||
println!("declared leaf: {}", leaf_spec.name);
|
println!("declared leaf: {}", leaf_spec.name);
|
||||||
println!("leaf: {}", payload.leaf_name);
|
println!("leaf: {}", payload.leaf_name);
|
||||||
println!("procedures: {:?}", payload.procedures);
|
println!("procedures: {:?}", payload.procedures);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn agent_path() -> Vec<String> {
|
|
||||||
vec![String::from("agent")]
|
|
||||||
}
|
|
||||||
|
|||||||
+36
@@ -28,4 +28,40 @@ pub use unshell_leaves as leaves;
|
|||||||
|
|
||||||
pub use unshell_macros::{Procedure, leaf, procedures};
|
pub use unshell_macros::{Procedure, leaf, procedures};
|
||||||
|
|
||||||
|
/// Creates a root-assumed endpoint from one local identifier plus any number of leaf hosts.
|
||||||
|
///
|
||||||
|
/// What it is: a convenience macro that builds a `ProtocolEndpoint` whose protocol path starts at
|
||||||
|
/// root, with no parent or children, and whose leaf inventory is inferred from the supplied host
|
||||||
|
/// values.
|
||||||
|
///
|
||||||
|
/// Why it exists: the common bootstrap case should not require callers to manually construct an
|
||||||
|
/// empty path, `Vec<ChildRoute>`, and a `Vec<LeafSpec>` when they already have leaf host values.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```rust
|
||||||
|
/// use unshell::{create_endpoint, leaf};
|
||||||
|
/// use unshell::protocol::tree::Endpoint;
|
||||||
|
///
|
||||||
|
/// #[derive(Default)]
|
||||||
|
/// struct DemoLeaf;
|
||||||
|
///
|
||||||
|
/// #[leaf(id = "org.example.v1.demo", procedures = ["ping"], endpoint_struct = DemoLeaf)]
|
||||||
|
/// struct Demo;
|
||||||
|
///
|
||||||
|
/// let endpoint = create_endpoint!("demo", DemoLeaf::default());
|
||||||
|
/// assert!(endpoint.path().is_empty());
|
||||||
|
/// assert_eq!(endpoint.local_id(), Some("demo"));
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! create_endpoint {
|
||||||
|
($id:expr $(, $leaf:expr )* $(,)?) => {{
|
||||||
|
let mut __unshell_leaf_specs = ::unshell::alloc::vec::Vec::new();
|
||||||
|
$(
|
||||||
|
let __unshell_leaf = $leaf;
|
||||||
|
__unshell_leaf_specs.push(::unshell::protocol::tree::leaf_spec_of(&__unshell_leaf));
|
||||||
|
)*
|
||||||
|
::unshell::protocol::tree::ProtocolEndpoint::root($id, __unshell_leaf_specs)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
// pub use ush_obfuscate as obfuscate;
|
// pub use ush_obfuscate as obfuscate;
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ impl ProtocolEndpoint {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
local_id: None,
|
||||||
routing: CompiledRoutes::new(&path, ®istered_child_paths, parent_path.is_some()),
|
routing: CompiledRoutes::new(&path, ®istered_child_paths, parent_path.is_some()),
|
||||||
path,
|
path,
|
||||||
children,
|
children,
|
||||||
@@ -147,6 +148,52 @@ impl ProtocolEndpoint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// Creates a root-assumed endpoint with one local identifier and predeclared leaves.
|
||||||
|
///
|
||||||
|
/// What it is: a convenience constructor for the common bootstrap state where an endpoint has
|
||||||
|
/// one local name but has not yet been assigned a non-root path by a parent connection.
|
||||||
|
///
|
||||||
|
/// Why it exists: endpoint creation should not require every caller to manually pass an empty
|
||||||
|
/// path, no parent, and no children just to host one or more known leaves.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```rust
|
||||||
|
/// use unshell::protocol::tree::{LeafSpec, ProtocolEndpoint};
|
||||||
|
/// let endpoint = ProtocolEndpoint::root(
|
||||||
|
/// "worker",
|
||||||
|
/// vec![LeafSpec {
|
||||||
|
/// name: "service".into(),
|
||||||
|
/// procedures: vec!["example.service.v1.invoke".into()],
|
||||||
|
/// }],
|
||||||
|
/// );
|
||||||
|
/// assert!(endpoint.path().is_empty());
|
||||||
|
/// assert_eq!(endpoint.local_id(), Some("worker"));
|
||||||
|
/// ```
|
||||||
|
pub fn root(local_id: impl Into<String>, leaves: Vec<LeafSpec>) -> Self {
|
||||||
|
let mut endpoint = Self::new(Vec::new(), None, Vec::new(), leaves);
|
||||||
|
endpoint.local_id = Some(local_id.into());
|
||||||
|
endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// Returns the endpoint's local bootstrap identifier, if one was assigned.
|
||||||
|
///
|
||||||
|
/// What it is: a lightweight label separate from the protocol path.
|
||||||
|
///
|
||||||
|
/// Why it exists: a freshly created endpoint may know its own local identity before a parent
|
||||||
|
/// connection assigns its final tree path.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```rust
|
||||||
|
/// use unshell::protocol::tree::ProtocolEndpoint;
|
||||||
|
/// let endpoint = ProtocolEndpoint::root("worker", Vec::new());
|
||||||
|
/// assert_eq!(endpoint.local_id(), Some("worker"));
|
||||||
|
/// ```
|
||||||
|
pub fn local_id(&self) -> Option<&str> {
|
||||||
|
self.local_id.as_deref()
|
||||||
|
}
|
||||||
|
|
||||||
/// Registers a procedure that is handled directly by the endpoint.
|
/// Registers a procedure that is handled directly by the endpoint.
|
||||||
///
|
///
|
||||||
/// Endpoint-level procedures exist for protocol services that are not attached to one leaf,
|
/// Endpoint-level procedures exist for protocol services that are not attached to one leaf,
|
||||||
|
|||||||
@@ -286,6 +286,7 @@ pub trait Endpoint {
|
|||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct ProtocolEndpoint {
|
pub struct ProtocolEndpoint {
|
||||||
|
pub(crate) local_id: Option<String>,
|
||||||
pub(crate) path: Vec<String>,
|
pub(crate) path: Vec<String>,
|
||||||
pub(crate) children: Vec<ChildRoute>,
|
pub(crate) children: Vec<ChildRoute>,
|
||||||
pub(crate) routing: CompiledRoutes,
|
pub(crate) routing: CompiledRoutes,
|
||||||
|
|||||||
@@ -92,6 +92,34 @@ pub trait LeafDeclaration: ProtocolLeaf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the canonical `LeafSpec` for one concrete leaf host value.
|
||||||
|
///
|
||||||
|
/// What it is: a tiny typed helper that uses a host value only for type inference.
|
||||||
|
///
|
||||||
|
/// Why it exists: endpoint-construction macros can accept ordinary host expressions like
|
||||||
|
/// `RemoteShell::default()` and still derive the compile-time `LeafSpec` without the caller
|
||||||
|
/// spelling the leaf type twice.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```rust
|
||||||
|
/// use unshell::protocol::tree::{LeafDeclaration, ProtocolLeaf, leaf_spec_of};
|
||||||
|
/// struct ExampleLeaf;
|
||||||
|
/// impl ProtocolLeaf for ExampleLeaf {
|
||||||
|
/// fn leaf_name() -> String { "org.example.v1.echo".into() }
|
||||||
|
/// }
|
||||||
|
/// impl LeafDeclaration for ExampleLeaf {
|
||||||
|
/// fn procedure_suffixes() -> &'static [&'static str] { &["invoke"] }
|
||||||
|
/// }
|
||||||
|
/// let spec = leaf_spec_of(&ExampleLeaf);
|
||||||
|
/// assert_eq!(spec.name, "org.example.v1.echo");
|
||||||
|
/// ```
|
||||||
|
pub fn leaf_spec_of<L>(_: &L) -> LeafSpec
|
||||||
|
where
|
||||||
|
L: LeafDeclaration,
|
||||||
|
{
|
||||||
|
L::leaf_spec()
|
||||||
|
}
|
||||||
|
|
||||||
/// Declares that one host struct is bound to one compile-time leaf declaration.
|
/// Declares that one host struct is bound to one compile-time leaf declaration.
|
||||||
///
|
///
|
||||||
/// What it is: a trait that links a concrete host type, such as an endpoint or
|
/// What it is: a trait that links a concrete host type, such as an endpoint or
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ pub use endpoint::{
|
|||||||
ProtocolEndpoint,
|
ProtocolEndpoint,
|
||||||
};
|
};
|
||||||
pub use hook::{ActiveHook, HookConflict, HookKey, HookTable, PendingHook};
|
pub use hook::{ActiveHook, HookConflict, HookKey, HookTable, PendingHook};
|
||||||
pub use leaf::{CallProcedures, LeafBinding, LeafDeclaration, ProtocolLeaf, derive_leaf_name};
|
pub use leaf::{
|
||||||
|
CallProcedures, LeafBinding, LeafDeclaration, ProtocolLeaf, derive_leaf_name, leaf_spec_of,
|
||||||
|
};
|
||||||
pub use procedure::{
|
pub use procedure::{
|
||||||
Procedure, ProcedureEffect, ProcedureMetadata, ProcedureRuntime, ProcedureRuntimeError,
|
Procedure, ProcedureEffect, ProcedureMetadata, ProcedureRuntime, ProcedureRuntimeError,
|
||||||
ProcedureRuntimeOutcome, ProcedureStore, StatefulProcedureMetadata,
|
ProcedureRuntimeOutcome, ProcedureStore, StatefulProcedureMetadata,
|
||||||
|
|||||||
Reference in New Issue
Block a user