pub struct Context { /* private fields */ }Implementations§
Source§impl Context
impl Context
Sourcepub fn get_redis_url() -> Result<String>
pub fn get_redis_url() -> Result<String>
Get Redis URL from infrastructure configuration
Sourcepub fn get_control_plane_redis_url() -> Result<Option<String>>
pub fn get_control_plane_redis_url() -> Result<Option<String>>
Get control plane Redis URL from infrastructure configuration
Source§impl Context
impl Context
Sourcepub async fn new(node_id: &str) -> Result<Self>
pub async fn new(node_id: &str) -> Result<Self>
Create a new context
§Arguments
node_id- Unique identifier for this node (fallback if NODE_NAME env var not set)
§Environment Variables
NODE_NAME- If set, overrides thenode_idparameter. This allows node-runner to set full identifiers like@mecha10/speakerfor consistent naming across the CLI, dashboard, and health reporting.
§Example
let ctx = Context::new("my-node").await?;Sourcepub fn with_instance(self, instance: &str) -> Self
pub fn with_instance(self, instance: &str) -> Self
Set an instance name for this context (builder pattern)
This allows the same node implementation to run multiple instances with different names. The node_id becomes “{node_name}/{instance}”.
§Example
// Single instance (no instance name)
let ctx = Context::new("camera_fake").await?;
// node_id: "camera_fake"
// Multi-instance (with instance names)
let left_ctx = Context::new("camera_fake").await?.with_instance("left");
// node_id: "camera_fake/left"
let right_ctx = Context::new("camera_fake").await?.with_instance("right");
// node_id: "camera_fake/right"Sourcepub fn instance(&self) -> Option<&str>
pub fn instance(&self) -> Option<&str>
Get the instance name if set
Returns None for single-instance nodes, or Some(instance) for
multi-instance nodes.
Sourcepub async fn is_shutdown(&self) -> bool
pub async fn is_shutdown(&self) -> bool
Check if shutdown has been requested
Sourcepub async fn wait_for_shutdown(&self)
pub async fn wait_for_shutdown(&self)
Wait for shutdown signal
Sourcepub fn logging_span(&self) -> Span
pub fn logging_span(&self) -> Span
Enter a logging span with this node’s context
This returns a guard that, when entered, adds the node_id to all log messages. The span is automatically exited when the guard is dropped.
§Example
use mecha10_core::prelude::*;
let _guard = ctx.logging_span().entered();
info!("This message includes node_id automatically");
debug!("So does this one");Source§impl Context
impl Context
Sourcepub async fn load_node_config<T>(&self, node_name: &str) -> Result<T>where
T: DeserializeOwned + Default,
pub async fn load_node_config<T>(&self, node_name: &str) -> Result<T>where
T: DeserializeOwned + Default,
Load node configuration with environment-aware file-level fallback
This method implements the V2 configuration system with:
- Environment-specific configs (dev/staging/production), selected within a config
file’s
{"dev": {...}, "production": {...}}envelope (seeSelf::load_environment_aware_config) - File-level fallback (no key-by-key merging)
- Automatic project-root discovery via
crate::fs_utils::find_project_root
Tier order (mirrors mecha10_cli_core::paths::config::node_config_candidates, the
canonical 2-tier resolution order for node configs – this method can’t call that helper
directly since mecha10-core is a dependency of mecha10-cli-core, not the reverse,
but it must stay in sync with it):
configs/nodes/@mecha10/{node_name}/config.json- Framework nodesconfigs/nodes/{node_name}/config.json- Project nodes (preferred)T::default()- Default implementation, if neither tier has a config file (or no project root could be found)
Environment Detection:
- Uses
MECHA10_ENVenv var (default: “dev”) - Supported values: “dev”, “staging”, “production”, or custom
Semantic Validation:
- Not run here. This method is generic over
Twith only aDeserializeOwned + Defaultbound, and stable Rust has no specialization – a function generic overTcan’t conditionally callcrate::config::Validate::validateonly whenThappens to implement it. Node types that need semantic validation should callvalidate()explicitly on the config returned here.
§Type Parameters
T- Configuration type that implementsDeserializeOwned+Default
§Arguments
node_name- Name of the node (e.g., “simulator”, “camera-streamer”)
§Returns
The loaded configuration of type T, using the first available source
in the fallback chain.
§Example
use mecha10_core::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
struct SimBridgeConfig {
camera_width: u32,
camera_height: u32,
camera_fps: u32,
}
let config: SimBridgeConfig = ctx.load_node_config("simulator").await?;Sourcepub async fn load_node_config_for_instance<T>(
&self,
node_name: &str,
instance_id: Option<&str>,
) -> Result<T>where
T: DeserializeOwned + Default,
pub async fn load_node_config_for_instance<T>(
&self,
node_name: &str,
instance_id: Option<&str>,
) -> Result<T>where
T: DeserializeOwned + Default,
Like Self::load_node_config, but with an explicit instance_id instead of this
context’s own Self::instance.
Node-runner (the supervisor process) is the main reason this is split out: it loads a
node’s config.json on that node’s behalf, through a context that represents
node-runner itself (not the node), so it can’t rely on self.instance() - it passes
the target node’s --instance-id through explicitly instead. Every actual node binary
(via node_main!/#[derive(Node)]) should just call Self::load_node_config, which
already does the right thing from its own instance().
§Instance-Keyed Config Files
For a multi-instance node (two camera nodes - one real, one simulated - both declared
in mecha10.json via the {"name": "@mecha10/camera", "instance_id": "real"} node-list
entry shape, LAB-4131), the resolved config file’s per-environment section may itself be
a flat config object (today’s behavior, unchanged, used when instance_id is None) or
a JSON object keyed by instance id:
{
"dev": {
"real": { "source": { "type": "v4l2", "device_path": "/dev/video0" }, "id": "real" },
"sim": { "source": { "type": "redis", "topic": "/camera/sim/raw" }, "id": "sim" }
}
}When instance_id is Some(id), the sub-object at that key is used as the node’s
config (same shape/validation as today’s flat config). An instance_id that isn’t
present as a key is an error, rather than silently falling back to T::default() - a
multi-instance node whose config is missing an instance’s entry has a real
misconfiguration, not an absent config file.
Sourcepub async fn load_and_validate_node_config<T>(
&self,
node_name: &str,
) -> Result<T>
pub async fn load_and_validate_node_config<T>( &self, node_name: &str, ) -> Result<T>
Same as Self::load_node_config, but also validates the loaded JSON against T’s own
schemars-derived JSON Schema before deserializing into it (LAB-4066).
This is the load-at-startup counterpart to mecha10_core::schema_validation’s preflight
validation (validate_project_config/mecha10 config validate), using the same “every
node supplies its own schema” principle that validation’s declared-schema lookup is
built on – except here there is no lookup and no “missing schema” case to hard-error on:
T: JsonSchema means T’s own derived schema always exists, enforced by the type system
at compile time rather than by a runtime registry/convention check. node_main! and
#[derive(Node)]’s generated run() call this (not Self::load_node_config) so every
node – built-in or custom – gets this check for free the moment its Config struct
derives JsonSchema (already required to export a schema for it at all, see
mecha10-schema-registry).
Not used for Self::load_node_config callers that read another node’s config file
generically through a deliberately-partial/lenient struct (e.g.
mecha10-node-runner’s launcher-level NodeConfig) – validating a partial struct’s
derived schema against another node’s full config would reject fields the reader
never claimed to model, which isn’t this node’s schema to enforce.
§Errors
Returns an error if the config file exists but fails to parse, fails schema validation,
or fails to deserialize into T.
Source§impl Context
impl Context
Sourcepub async fn state_manager(&self) -> Result<Arc<ConcreteStateManager>>
pub async fn state_manager(&self) -> Result<Arc<ConcreteStateManager>>
Get or create a state manager for this node
The state manager provides persistent storage for node state across restarts.
By default, it uses filesystem storage in ./state/ directory.
§Example
use mecha10_core::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
struct MyState {
counter: u64,
position: (f64, f64),
}
let state_mgr = ctx.state_manager().await?;
// Load previous state
let mut state = state_mgr
.load::<MyState>("my_node")
.await?
.unwrap_or(MyState {
counter: 0,
position: (0.0, 0.0),
});
// Update state
state.counter += 1;
// Save state
state_mgr.save("my_node", &state).await?;Sourcepub async fn set_state_manager(&self, manager: Arc<ConcreteStateManager>)
pub async fn set_state_manager(&self, manager: Arc<ConcreteStateManager>)
Set a custom state manager for this context
This allows using alternative backends (Redis, PostgreSQL, etc.) or custom implementations.
§Example
use mecha10_core::prelude::*;
use mecha10_core::state::ConcreteStateManager;
use std::sync::Arc;
// Use in-memory state manager for testing
let mem_mgr = Arc::new(ConcreteStateManager::Memory(MemoryStateManager::new()));
ctx.set_state_manager(mem_mgr).await;Trait Implementations§
Source§impl AuthExt for Context
impl AuthExt for Context
Source§async fn get_policy(&self, role: &str) -> Result<Option<Policy>>
async fn get_policy(&self, role: &str) -> Result<Option<Policy>>
Source§async fn assign_role(&self, node_id: &str, role: &str) -> Result<()>
async fn assign_role(&self, node_id: &str, role: &str) -> Result<()>
Source§async fn get_role(&self, node_id: &str) -> Result<Option<String>>
async fn get_role(&self, node_id: &str) -> Result<Option<String>>
Source§async fn check_permission(
&self,
node_id: &str,
permission: Permission,
topic: &str,
) -> Result<bool>
async fn check_permission( &self, node_id: &str, permission: Permission, topic: &str, ) -> Result<bool>
Source§impl SchemaRegistryExt for Context
impl SchemaRegistryExt for Context
Source§async fn register_schema(&self, schema: &MessageSchema) -> Result<()>
async fn register_schema(&self, schema: &MessageSchema) -> Result<()>
Source§async fn get_schema(
&self,
name: &str,
version: u32,
) -> Result<Option<MessageSchema>>
async fn get_schema( &self, name: &str, version: u32, ) -> Result<Option<MessageSchema>>
Source§async fn get_latest_schema(&self, name: &str) -> Result<Option<MessageSchema>>
async fn get_latest_schema(&self, name: &str) -> Result<Option<MessageSchema>>
Source§async fn list_schemas(&self) -> Result<Vec<MessageSchema>>
async fn list_schemas(&self) -> Result<Vec<MessageSchema>>
Source§async fn get_schema_versions(&self, name: &str) -> Result<Vec<MessageSchema>>
async fn get_schema_versions(&self, name: &str) -> Result<Vec<MessageSchema>>
Source§impl SchemaVersioningExt for Context
impl SchemaVersioningExt for Context
Source§async fn check_compatibility(
&self,
name: &str,
old_version: u32,
new_version: u32,
mode: CompatibilityMode,
) -> Result<CompatibilityCheck>
async fn check_compatibility( &self, name: &str, old_version: u32, new_version: u32, mode: CompatibilityMode, ) -> Result<CompatibilityCheck>
Source§async fn register_versioned_schema(
&self,
schema: &MessageSchema,
mode: CompatibilityMode,
) -> Result<CompatibilityCheck>
async fn register_versioned_schema( &self, schema: &MessageSchema, mode: CompatibilityMode, ) -> Result<CompatibilityCheck>
Source§async fn get_version_info(
&self,
name: &str,
) -> Result<Option<MessageVersionInfo>>
async fn get_version_info( &self, name: &str, ) -> Result<Option<MessageVersionInfo>>
Auto Trait Implementations§
impl !RefUnwindSafe for Context
impl !UnwindSafe for Context
impl Freeze for Context
impl Send for Context
impl Sync for Context
impl Unpin for Context
impl UnsafeUnpin for Context
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more