mecha10_behavior_runtime/
behavior.rs

1//! Core BehaviorNode trait and types
2//!
3//! This module defines the fundamental `BehaviorNode` trait that all behaviors must implement.
4
5use crate::NodeStatus;
6use async_trait::async_trait;
7use mecha10_core::Context;
8use std::fmt::Debug;
9
10/// Core trait that all behavior nodes must implement.
11///
12/// This is the fundamental building block of the behavior system. Everything - from simple
13/// actions to complex AI models to composition primitives - implements this trait.
14///
15/// # Design Principles
16///
17/// 1. **Simplicity** - One method: `tick()`
18/// 2. **Async-first** - All behaviors are async by default
19/// 3. **Composable** - Nodes can be freely combined
20/// 4. **Type-safe** - Rust's type system ensures correctness
21///
22/// # Example
23///
24/// ```rust
25/// use mecha10_behavior_runtime::prelude::*;
26///
27/// #[derive(Debug)]
28/// struct PrintMessage {
29///     message: String,
30/// }
31///
32/// #[async_trait]
33/// impl BehaviorNode for PrintMessage {
34///     async fn tick(&mut self, ctx: &Context) -> anyhow::Result<NodeStatus> {
35///         println!("{}", self.message);
36///         Ok(NodeStatus::Success)
37///     }
38///
39///     fn name(&self) -> &str {
40///         "print_message"
41///     }
42/// }
43/// ```
44#[async_trait]
45pub trait BehaviorNode: Send + Sync + Debug {
46    /// Execute one tick of the behavior.
47    ///
48    /// This method is called repeatedly by the execution engine. It should:
49    /// - Perform one unit of work
50    /// - Return quickly (non-blocking)
51    /// - Return `NodeStatus` to indicate progress
52    ///
53    /// # Arguments
54    ///
55    /// * `ctx` - Execution context with access to messaging, sensors, etc.
56    ///
57    /// # Returns
58    ///
59    /// * `NodeStatus::Success` - Behavior completed successfully
60    /// * `NodeStatus::Failure` - Behavior failed (irrecoverable error)
61    /// * `NodeStatus::Running` - Behavior is still executing (call again)
62    ///
63    /// # Errors
64    ///
65    /// Return an error for exceptional conditions that should halt execution.
66    /// For recoverable failures, return `Ok(NodeStatus::Failure)` instead.
67    async fn tick(&mut self, ctx: &Context) -> anyhow::Result<NodeStatus>;
68
69    /// Get the name of this behavior node.
70    ///
71    /// Used for debugging, logging, and monitoring. The default implementation
72    /// returns the type name.
73    fn name(&self) -> &str {
74        std::any::type_name::<Self>()
75    }
76
77    /// Reset the behavior to its initial state.
78    ///
79    /// Called when the behavior needs to be restarted (e.g., in a loop or
80    /// after a failure). The default implementation does nothing.
81    async fn reset(&mut self) -> anyhow::Result<()> {
82        Ok(())
83    }
84
85    /// Called when the behavior is first initialized.
86    ///
87    /// Use this for one-time setup like loading models, establishing connections, etc.
88    /// The default implementation does nothing.
89    async fn on_init(&mut self, _ctx: &Context) -> anyhow::Result<()> {
90        Ok(())
91    }
92
93    /// Called when the behavior is being terminated.
94    ///
95    /// Use this for cleanup like closing connections, saving state, etc.
96    /// The default implementation does nothing.
97    async fn on_terminate(&mut self, _ctx: &Context) -> anyhow::Result<()> {
98        Ok(())
99    }
100}
101
102/// Type alias for boxed behavior nodes.
103///
104/// This is used for storing heterogeneous collections of behaviors.
105pub type BoxedBehavior = Box<dyn BehaviorNode>;
106
107// ============================================================================
108// Helper Implementations
109// ============================================================================
110
111/// Extension trait for BehaviorNode to provide convenient helpers.
112///
113/// This trait is automatically implemented for all types that implement `BehaviorNode`.
114#[async_trait]
115pub trait BehaviorNodeExt: BehaviorNode {
116    /// Run this behavior until it completes (Success or Failure).
117    ///
118    /// This is a convenience method for testing and simple execution.
119    ///
120    /// # Example
121    ///
122    /// ```rust
123    /// use mecha10_behavior_runtime::prelude::*;
124    ///
125    /// # async fn example(mut behavior: impl BehaviorNode, ctx: &Context) -> anyhow::Result<()> {
126    /// let status = behavior.run_until_complete(ctx).await?;
127    /// assert_eq!(status, NodeStatus::Success);
128    /// # Ok(())
129    /// # }
130    /// ```
131    async fn run_until_complete(&mut self, ctx: &Context) -> anyhow::Result<NodeStatus> {
132        loop {
133            match self.tick(ctx).await? {
134                NodeStatus::Success => return Ok(NodeStatus::Success),
135                NodeStatus::Failure => return Ok(NodeStatus::Failure),
136                NodeStatus::Running => continue,
137            }
138        }
139    }
140
141    /// Run this behavior with a maximum number of ticks.
142    ///
143    /// Returns `Err` if the behavior doesn't complete within `max_ticks`.
144    ///
145    /// # Example
146    ///
147    /// ```rust
148    /// use mecha10_behavior_runtime::prelude::*;
149    ///
150    /// # async fn example(mut behavior: impl BehaviorNode, ctx: &Context) -> anyhow::Result<()> {
151    /// let status = behavior.run_with_limit(ctx, 100).await?;
152    /// # Ok(())
153    /// # }
154    /// ```
155    async fn run_with_limit(&mut self, ctx: &Context, max_ticks: usize) -> anyhow::Result<NodeStatus> {
156        for _tick_count in 0..max_ticks {
157            match self.tick(ctx).await? {
158                NodeStatus::Success => return Ok(NodeStatus::Success),
159                NodeStatus::Failure => return Ok(NodeStatus::Failure),
160                NodeStatus::Running => continue,
161            }
162        }
163        anyhow::bail!("Behavior exceeded maximum ticks ({})", max_ticks)
164    }
165}
166
167// Blanket implementation for all BehaviorNode types
168impl<T: BehaviorNode + ?Sized> BehaviorNodeExt for T {}