1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//! The agent module contains the core agent abstraction for the Arbiter Engine.

use std::{fmt::Debug, sync::Arc};

use arbiter_core::middleware::ArbiterMiddleware;
use serde::{de::DeserializeOwned, Serialize};

use super::*;
use crate::{
    machine::{Behavior, Engine, StateMachine},
    messager::Messager,
};

/// An agent is an entity capable of processing events and producing actions.
/// These are the core actors in simulations or in onchain systems.
/// Agents can be connected of other agents either as a dependent, or a
/// dependency.
///
/// # How it works
/// When the [`World`] that owns the [`Agent`] is ran, it has each [`Agent`] run
/// each of its [`Behavior`]s `startup()` methods. The [`Behavior`]s themselves
/// will return a stream of events that then let the [`Behavior`] move into the
/// `State::Processing` stage.
#[derive(Debug)]
pub struct Agent {
    /// Identifier for this agent.
    /// Used for routing messages.
    pub id: String,

    /// The messager the agent uses to send and receive messages from other
    /// agents.
    pub messager: Messager,

    /// The client the agent uses to interact with the blockchain.
    pub client: Arc<ArbiterMiddleware>,

    /// The engines/behaviors that the agent uses to sync, startup, and process
    /// events.
    pub(crate) behavior_engines: Vec<Box<dyn StateMachine>>,
}

impl Agent {
    /// Creates a new [`AgentBuilder`] instance with a specified identifier.
    ///
    /// This method initializes an [`AgentBuilder`] with the provided `id` and
    /// sets the `behavior_engines` field to `None`. The returned
    /// [`AgentBuilder`] can be further configured using its methods before
    /// finalizing the creation of an [`Agent`].
    ///
    /// # Arguments
    ///
    /// * `id` - A string slice that holds the identifier for the agent being
    ///   built.
    ///
    /// # Returns
    ///
    /// Returns an [`AgentBuilder`] instance that can be used to configure and
    /// build an [`Agent`].
    pub fn builder(id: &str) -> AgentBuilder {
        AgentBuilder {
            id: id.to_owned(),
            behavior_engines: None,
        }
    }
}

/// [`AgentBuilder`] represents the intermediate state of agent creation before
/// it is converted into a full on [`Agent`]
pub struct AgentBuilder {
    /// Identifier for this agent.
    /// Used for routing messages.
    pub id: String,
    /// The engines/behaviors that the agent uses to sync, startup, and process
    /// events.
    behavior_engines: Option<Vec<Box<dyn StateMachine>>>,
}

impl AgentBuilder {
    /// Appends a behavior onto an [`AgentBuilder`]. Behaviors are initialized
    /// when the agent builder is added to the [`crate::world::World`]
    pub fn with_behavior<E: DeserializeOwned + Serialize + Send + Sync + Debug + 'static>(
        mut self,
        behavior: impl Behavior<E> + Serialize + DeserializeOwned + 'static,
    ) -> Self {
        let engine = Engine::new(behavior);
        if let Some(engines) = &mut self.behavior_engines {
            engines.push(Box::new(engine));
        } else {
            self.behavior_engines = Some(vec![Box::new(engine)]);
        };
        self
    }

    /// Adds a state machine engine to the agent builder.
    ///
    /// This method allows for the addition of a custom state machine engine to
    /// the agent's behavior engines. If the agent builder already has some
    /// engines, the new engine is appended to the list. If no engines are
    /// present, a new list is created with the provided engine as its first
    /// element.
    ///
    /// # Parameters
    ///
    /// - `engine`: The state machine engine to be added to the agent builder.
    ///   This engine must
    /// implement the `StateMachine` trait and is expected to be provided as a
    /// boxed trait object to allow for dynamic dispatch.
    ///
    /// # Returns
    ///
    /// Returns the `AgentBuilder` instance to allow for method chaining.
    pub(crate) fn with_engine(mut self, engine: Box<dyn StateMachine>) -> Self {
        if let Some(engines) = &mut self.behavior_engines {
            engines.push(engine);
        } else {
            self.behavior_engines = Some(vec![engine]);
        };
        self
    }

    /// Constructs and returns a new [`Agent`] instance using the provided
    /// `client` and `messager`.
    ///
    /// This method finalizes the building process of an [`Agent`] by taking
    /// ownership of the builder, and attempting to construct an `Agent`
    /// with the accumulated configurations and the provided `client` and
    /// `messager`. The `client` is an [`Arc<RevmMiddleware>`] that represents
    /// the connection to the blockchain or environment, and `messager` is a
    /// communication layer for the agent.
    ///
    /// # Parameters
    ///
    /// - `client`: A shared [`Arc<RevmMiddleware>`] instance that provides the
    ///   agent with access to the blockchain or environment.
    /// - `messager`: A [`Messager`] instance for the agent to communicate with
    ///   other agents or systems.
    ///
    /// # Returns
    ///
    /// Returns a `Result` that, on success, contains the newly created
    /// [`Agent`] instance. On failure, it returns an
    /// [`AgentBuildError::MissingBehaviorEngines`] error indicating that the
    /// agent was attempted to be built without any behavior engines
    /// configured.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let agent_builder = AgentBuilder::new("agent_id");
    /// let client = Arc::new(RevmMiddleware::new(...));
    /// let messager = Messager::new(...);
    /// let agent = agent_builder.build(client, messager).expect("Failed to build agent");
    /// ```
    pub fn build(
        self,
        client: Arc<ArbiterMiddleware>,
        messager: Messager,
    ) -> Result<Agent, ArbiterEngineError> {
        match self.behavior_engines {
            Some(engines) => Ok(Agent {
                id: self.id,
                messager,
                client,
                behavior_engines: engines,
            }),
            None => Err(ArbiterEngineError::AgentBuildError(
                "Missing behavior engines".to_owned(),
            )),
        }
    }
}