Skip to main content

mecha10_core/
prelude.rs

1pub use crate::context::{
2    Context, InfrastructureConfig, MinioConfig, MlflowConfig, PostgresConfig, Receiver, RedisConfig,
3};
4
5#[cfg(feature = "messaging")]
6pub use crate::stream::ReceiverExt;
7
8#[cfg(feature = "messaging")]
9pub use crate::publish::ContextPublishExt;
10
11#[cfg(feature = "messaging")]
12pub use crate::rpc::{Request, Response, RpcExt};
13
14#[cfg(feature = "messaging")]
15pub use crate::rpc_streaming::{
16    StreamError, StreamMessage, StreamPayload, StreamReceiver, StreamSender, StreamingRpcExt,
17};
18
19#[cfg(feature = "messaging")]
20pub use crate::discovery::DiscoveryExt;
21pub use crate::discovery::{NodeMetadata, NodeStatus};
22
23pub use crate::schema::{
24    CompatibilityCheck, CompatibilityMode, MessageSchema, MessageVersionInfo, SchemaEvolution, SchemaField,
25    SchemaRegistryExt, SchemaVersioningExt,
26};
27
28pub use crate::auth::{AuthExt, Permission, Policy, Rule};
29
30pub use crate::secrets::{
31    EnvironmentBackend, RedisBackend, SecretBackend, SecretsConfig, SecretsManager, VaultBackend,
32};
33
34#[cfg(feature = "messaging")]
35pub use crate::health::{
36    HealthMonitor, HealthReport, HealthReporter, HealthReportingExt, NodeHealthInfo, SystemHealth,
37};
38
39#[cfg(feature = "messaging")]
40pub use crate::behavior_interrupt::{
41    BehaviorControl, BehaviorInterruptConfig, BehaviorInterruptTrigger, InterruptMode,
42};
43
44pub use crate::metrics::{Counter, Gauge, Histogram, MetricsRegistry, NodeMetrics};
45
46pub use crate::rate_limit::{MultiKeyRateLimiter, RateLimit, RateLimiter, RateLimiterManager};
47
48pub use crate::circuit_breaker::{CircuitBreaker, CircuitBreakerConfig, CircuitState};
49
50#[cfg(feature = "messaging")]
51pub use crate::dead_letter::{DeadLetterConfig, DeadLetterQueue, FailedMessage};
52
53#[cfg(feature = "schema-validation")]
54pub use crate::schema_validation::{
55    export_project_schema, get_project_schema, load_and_validate_project, validate_config_file,
56    validate_node_config_file, validate_node_config_json, validate_project_config, validate_project_json,
57};
58
59pub use crate::dependency::{create_graph, DependencyGraph, ShutdownPlan, StartupPlan};
60
61#[cfg(feature = "messaging")]
62pub use crate::lifecycle::{
63    get_timestamp_ms, topic_node_lifecycle, topic_node_status, topic_node_trigger, ModeChanged, ModeError, ModeRequest,
64    NodeLifecycleAction, NodeLifecycleEvent, NodeLifecycleStatus, NodeStatusEvent, NodeTriggerEvent,
65    TOPIC_MODE_CHANGED, TOPIC_MODE_ERROR, TOPIC_MODE_REQUEST,
66};
67
68pub use crate::topic_registry::{TopicInfo, TopicRegistry, TopologyStats};
69
70pub use crate::topic_utils::{extract_category, format_elapsed_time, TopicMetadata, Topics};
71
72pub use crate::state::{
73    FilesystemStateManager, MemoryStateManager, StateManager, StateOptions, StateSnapshot, VersionedState,
74};
75
76pub use crate::recovery::{
77    BackoffStrategy, DegradationMode, RecoveryAction, RecoveryAttempt, RecoveryHistory, RecoveryManager,
78    RecoveryPolicy, RecoveryPolicyBuilder, RecoveryStats,
79};
80
81#[cfg(feature = "messaging")]
82pub use crate::tracing_otel::{
83    JaegerExporter, SpanBuilder, SpanData, TraceContext, TraceExporter, TracedMessage, TracingConfig, TracingExt,
84    TracingHandle, ZipkinExporter,
85};
86
87pub use crate::profiling::{
88    measure_latency, ExecutionStats, LatencyStats, MemoryStats, PerformanceMetrics, PerformanceReport, ProfilingExt,
89    ThroughputStats, TimerGuard,
90};
91
92pub use crate::service::Service;
93#[cfg(feature = "messaging")]
94pub use crate::service_manager::ServiceManager;
95
96pub use crate::node::{
97    fmt_node_with_controller, run_node, HealthPriority, HealthReportingConfig, HealthStatusExt, Node, NodeImpl,
98    NodeTag, NodeType, RateConfig,
99};
100
101pub use crate::config::{
102    env_or_default, load_and_validate, load_config, load_config_from_env, substitute_env_vars, Validate,
103};
104
105pub use crate::model::{load_labels, load_model_config, CustomLabelsConfig, ModelConfig, PreprocessingConfig};
106
107pub use crate::builder_setter;
108pub use crate::enum_str_method;
109
110pub use crate::messages::Message;
111
112pub use crate::messages::Timestamped;
113
114pub use crate::types::*;
115
116pub use crate::error::{Mecha10Error, Result};
117
118pub use async_trait::async_trait;
119
120pub use tokio::{
121    self, select,
122    time::{interval, sleep, timeout, Duration, Instant},
123};
124
125pub use tokio::task::JoinHandle;
126
127pub use serde::{Deserialize, Serialize};
128pub use serde_json::{self, json, Value as JsonValue};
129
130pub use schemars::JsonSchema;
131
132pub use tracing::{debug, error, info, instrument, trace, warn, Level};
133
134#[cfg(feature = "macros")]
135pub use mecha10_macros::Node;
136
137#[cfg(feature = "macros")]
138pub use mecha10_macros::Config;
139
140#[cfg(feature = "macros")]
141pub use mecha10_macros::ConfigDefaults;
142
143/// Shared implementation behind `now_micros`/`now_secs`/`now_millis`, which differ only in
144/// which `Duration` unit they read off the epoch offset.
145fn since_epoch() -> std::time::Duration {
146    std::time::SystemTime::now()
147        .duration_since(std::time::UNIX_EPOCH)
148        .expect("Time went backwards")
149}
150
151/// Get current time in microseconds since UNIX epoch
152pub fn now_micros() -> u64 {
153    since_epoch().as_micros() as u64
154}
155
156/// Get current time in whole seconds since UNIX epoch
157pub fn now_secs() -> u64 {
158    since_epoch().as_secs()
159}
160
161/// Create a periodic interval from a frequency in Hz
162///
163/// This is a convenience helper that converts frequency (Hz) to a tokio interval.
164/// Commonly used for periodic publishing in drivers and nodes.
165///
166/// # Arguments
167///
168/// * `hz` - Frequency in Hertz (cycles per second)
169///
170/// # Returns
171///
172/// A tokio::time::Interval that ticks at the specified frequency
173///
174/// # Example
175///
176/// Marked `no_run`: the `loop` below never breaks, so running it to completion
177/// would hang the doctest forever.
178/// ```rust,no_run
179/// use mecha10_core::prelude::*;
180///
181/// # async fn example() {
182/// // Instead of:
183/// // let interval_duration = Duration::from_secs_f32(1.0 / 30.0);
184/// // let mut interval = tokio::time::interval(interval_duration);
185///
186/// // Simply use:
187/// let mut interval = interval_at_hz(30.0);
188///
189/// loop {
190///     interval.tick().await;
191///     // Publish at 30 Hz
192/// }
193/// # }
194/// ```
195pub fn interval_at_hz(hz: f32) -> tokio::time::Interval {
196    let duration = std::time::Duration::from_secs_f32(1.0 / hz);
197    tokio::time::interval(duration)
198}
199
200/// Initialize logging with sensible defaults
201///
202/// This function is safe to call multiple times - it will silently succeed if
203/// a tracing subscriber is already set (e.g., when running bundled in the CLI).
204///
205/// The logging system will automatically include node context (node_id) in all
206/// log messages when using the Context's logging methods or the `in_node_context!` macro.
207///
208/// Under the `messaging` feature, this also attaches
209/// [`crate::log_publisher::RedisLogLayer`], the layer that ships captured
210/// log events to Redis on `/system/log` once `crate::log_publisher::attach`
211/// has been called with a live `Context` (done by `run_node`). It's
212/// attached here - unconditionally, before any node-specific state exists -
213/// rather than deferred, because `init_logging()` is what every node
214/// binary's `main.rs` calls *first* (before `run_node` ever runs): `tracing`
215/// only lets the first `try_init()` call in a process actually install a
216/// subscriber, so the redis layer has to ride along with whichever call
217/// wins that race, not assume it'll be `run_node`'s.
218pub fn init_logging() {
219    use tracing_subscriber::EnvFilter;
220
221    let base = std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string());
222    let curated = format!("{base},ort=warn,rtc_dtls=error,rtc_ice=error,rtc::peer_connection::handler=error");
223    let env_filter = EnvFilter::try_new(&curated).unwrap_or_else(|_| {
224        EnvFilter::new("info,ort=warn,rtc_dtls=error,rtc_ice=error,rtc::peer_connection::handler=error")
225    });
226
227    #[cfg(feature = "messaging")]
228    {
229        use tracing_subscriber::prelude::*;
230
231        let _ = tracing_subscriber::registry()
232            .with(env_filter)
233            .with(tracing_subscriber::fmt::layer())
234            .with(crate::log_publisher::RedisLogLayer::new())
235            .try_init();
236    }
237
238    #[cfg(not(feature = "messaging"))]
239    {
240        let _ = tracing_subscriber::fmt().with_env_filter(env_filter).try_init();
241    }
242}
243
244#[macro_export]
245macro_rules! in_node_context {
246    ($ctx:expr, $body:block) => {{
247        let _span = tracing::info_span!("node", node_id = $ctx.node_id()).entered();
248        $body
249    }};
250}
251
252pub use in_node_context;
253
254#[macro_export]
255macro_rules! node_binary_main {
256    ($node_crate:ident) => {
257        #[tokio::main]
258        async fn main() -> anyhow::Result<()> {
259            $crate::prelude::init_logging();
260
261            $node_crate::run().await.map_err(|e| anyhow::anyhow!(e))
262        }
263    };
264}
265
266pub use node_binary_main;
267
268/// Get the current time in milliseconds since UNIX epoch
269///
270/// This is useful when microsecond precision is excessive (logs, metrics, etc.).
271///
272/// # Example
273///
274/// ```rust
275/// use mecha10_core::prelude::*;
276///
277/// let timestamp_ms = now_millis();
278/// println!("Current time: {} ms", timestamp_ms);
279/// ```
280pub fn now_millis() -> u64 {
281    since_epoch().as_millis() as u64
282}
283
284/// Calculate time elapsed in microseconds since a given timestamp
285///
286/// # Arguments
287///
288/// * `timestamp_micros` - A timestamp in microseconds (from `now_micros()`)
289///
290/// # Returns
291///
292/// Elapsed time in microseconds
293///
294/// # Example
295///
296/// ```rust
297/// use mecha10_core::prelude::*;
298///
299/// let start = now_micros();
300/// // ... do work ...
301/// let elapsed = elapsed_micros(start);
302/// println!("Operation took {} μs", elapsed);
303/// ```
304pub fn elapsed_micros(timestamp_micros: u64) -> u64 {
305    now_micros().saturating_sub(timestamp_micros)
306}
307
308/// Convert a microsecond timestamp to seconds (as f64)
309///
310/// Useful for displaying timestamps in human-readable format.
311///
312/// # Example
313///
314/// ```rust
315/// use mecha10_core::prelude::*;
316///
317/// let timestamp = now_micros();
318/// let seconds = timestamp_as_secs(timestamp);
319/// println!("Time: {:.6} seconds", seconds);
320/// ```
321pub fn timestamp_as_secs(timestamp_micros: u64) -> f64 {
322    timestamp_micros as f64 / 1_000_000.0
323}
324
325/// Create a Duration from a frequency in Hz
326///
327/// # Example
328///
329/// ```rust
330/// use mecha10_core::prelude::*;
331///
332/// let duration = duration_from_hz(30.0);  // 33.33ms for 30Hz
333/// ```
334pub fn duration_from_hz(hz: f32) -> Duration {
335    Duration::from_secs_f32(1.0 / hz)
336}
337
338/// Normalize an angle in radians to the range [-π, π]
339///
340/// This is essential for angle calculations to avoid unwanted wraparound effects.
341///
342/// # Example
343///
344/// ```rust
345/// use mecha10_core::prelude::*;
346///
347/// let angle = normalize_angle(7.0);  // Returns ~0.717 (7.0 - 2π)
348/// let angle2 = normalize_angle(-4.0); // Returns ~2.283 (-4.0 + 2π)
349/// ```
350pub fn normalize_angle(angle: f32) -> f32 {
351    use std::f32::consts::PI;
352    let mut normalized = angle;
353    while normalized > PI {
354        normalized -= 2.0 * PI;
355    }
356    while normalized < -PI {
357        normalized += 2.0 * PI;
358    }
359    normalized
360}
361
362/// Normalize an angle in degrees to the range [-180, 180]
363///
364/// # Example
365///
366/// ```rust
367/// use mecha10_core::prelude::*;
368///
369/// let angle = normalize_angle_degrees(270.0);  // Returns -90.0
370/// ```
371pub fn normalize_angle_degrees(angle: f32) -> f32 {
372    let mut normalized = angle;
373    while normalized > 180.0 {
374        normalized -= 360.0;
375    }
376    while normalized < -180.0 {
377        normalized += 360.0;
378    }
379    normalized
380}
381
382/// Calculate the shortest angular difference between two angles in radians
383///
384/// Returns the angular distance in the range [-π, π].
385///
386/// # Example
387///
388/// ```rust
389/// use mecha10_core::prelude::*;
390///
391/// let diff = angle_difference(0.1, 6.2);  // Shortest path wrapping around
392/// ```
393pub fn angle_difference(from: f32, to: f32) -> f32 {
394    normalize_angle(to - from)
395}
396
397/// Convert degrees to radians
398///
399/// # Example
400///
401/// ```rust
402/// use mecha10_core::prelude::*;
403///
404/// let radians = degrees_to_radians(180.0);  // Returns π
405/// ```
406pub fn degrees_to_radians(degrees: f32) -> f32 {
407    degrees * std::f32::consts::PI / 180.0
408}
409
410/// Convert radians to degrees
411///
412/// # Example
413///
414/// ```rust
415/// use mecha10_core::prelude::*;
416///
417/// let degrees = radians_to_degrees(std::f32::consts::PI);  // Returns 180.0
418/// ```
419pub fn radians_to_degrees(radians: f32) -> f32 {
420    radians * 180.0 / std::f32::consts::PI
421}
422
423/// Safely divide two numbers, returning a default value if divisor is zero
424///
425/// # Example
426///
427/// ```rust
428/// use mecha10_core::prelude::*;
429///
430/// let result = safe_divide(10.0, 2.0, 0.0);   // Returns 5.0
431/// let result = safe_divide(10.0, 0.0, 99.0);  // Returns 99.0 (default)
432/// ```
433pub fn safe_divide(numerator: f32, denominator: f32, default: f32) -> f32 {
434    if denominator.abs() < f32::EPSILON {
435        default
436    } else {
437        numerator / denominator
438    }
439}
440
441/// Clamp a value between a minimum and maximum
442///
443/// This is available in std but this wrapper makes it more discoverable.
444///
445/// # Example
446///
447/// ```rust
448/// use mecha10_core::prelude::*;
449///
450/// let value = clamp(15.0, 0.0, 10.0);  // Returns 10.0
451/// ```
452pub fn clamp<T: PartialOrd>(value: T, min: T, max: T) -> T {
453    if value < min {
454        min
455    } else if value > max {
456        max
457    } else {
458        value
459    }
460}
461
462/// Linear interpolation between two values
463///
464/// # Arguments
465///
466/// * `a` - Start value
467/// * `b` - End value
468/// * `t` - Interpolation factor [0.0, 1.0]
469///
470/// # Example
471///
472/// ```rust
473/// use mecha10_core::prelude::*;
474///
475/// let mid = lerp(0.0, 10.0, 0.5);  // Returns 5.0
476/// ```
477pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
478    a + (b - a) * t
479}
480
481pub use std::collections::{HashMap, HashSet};
482
483pub use std::sync::{Arc, Mutex};
484
485pub use std::path::{Path, PathBuf};
486
487pub type Shared<T> = Arc<T>;
488
489pub type SharedMut<T> = Arc<Mutex<T>>;