use crate::{GoapError, Result, State};
use std::fmt;
#[derive(Debug, Clone)]
pub struct ActionResponse {
stdout: String,
stderr: String,
return_code: i32,
}
impl ActionResponse {
pub fn new(stdout: String, stderr: String, return_code: i32) -> Self {
Self {
stdout,
stderr,
return_code,
}
}
pub fn stdout(&self) -> &str {
&self.stdout
}
pub fn stderr(&self) -> &str {
&self.stderr
}
pub fn return_code(&self) -> i32 {
self.return_code
}
pub fn response(&self) -> String {
self.stdout.clone()
}
pub fn is_success(&self) -> bool {
self.return_code == 0
}
}
impl fmt::Display for ActionResponse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.stdout)
}
}
#[derive(Debug, Clone)]
pub struct Action {
pub name: String,
pub cost: f32,
pub preconditions: State,
pub effects: State,
}
impl Action {
pub fn new(name: impl Into<String>, cost: f32) -> Result<Self> {
if cost <= 0.0 {
return Err(GoapError::InvalidActionCost);
}
Ok(Self {
name: name.into(),
cost,
preconditions: State::new(),
effects: State::new(),
})
}
pub fn can_perform(&self, state: &State) -> bool {
state.satisfies(&self.preconditions)
}
pub fn apply_effects(&self, state: &mut State) {
state.apply_effects(&self.effects);
}
pub async fn exec(&self) -> Result<ActionResponse> {
Ok(ActionResponse::new(
format!("Executed action: {}", self.name),
String::new(),
0,
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_valid_action() {
let action = Action::new("test_action", 1.0).unwrap();
assert_eq!(action.name, "test_action");
assert_eq!(action.cost, 1.0);
assert!(action.preconditions.values().is_empty());
assert!(action.effects.values().is_empty());
}
#[test]
fn test_create_invalid_action() {
let result = Action::new("test_action", 0.0);
assert!(matches!(result, Err(GoapError::InvalidActionCost)));
let result = Action::new("test_action", -1.0);
assert!(matches!(result, Err(GoapError::InvalidActionCost)));
}
#[test]
fn test_can_perform_with_empty_preconditions() {
let action = Action::new("test_action", 1.0).unwrap();
let state = State::new();
assert!(action.can_perform(&state));
}
#[test]
fn test_can_perform_with_matching_preconditions() {
let mut action = Action::new("test_action", 1.0).unwrap();
action.preconditions.set("has_tool", true);
let mut state = State::new();
state.set("has_tool", true);
assert!(action.can_perform(&state));
}
#[test]
fn test_can_perform_with_unmatching_preconditions() {
let mut action = Action::new("test_action", 1.0).unwrap();
action.preconditions.set("has_tool", true);
let mut state = State::new();
state.set("has_tool", false);
assert!(!action.can_perform(&state));
}
#[test]
fn test_can_perform_with_missing_preconditions() {
let mut action = Action::new("test_action", 1.0).unwrap();
action.preconditions.set("has_tool", true);
let state = State::new();
assert!(!action.can_perform(&state));
}
#[test]
fn test_apply_effects_empty() {
let action = Action::new("test_action", 1.0).unwrap();
let mut state = State::new();
action.apply_effects(&mut state);
assert!(state.values().is_empty());
}
#[test]
fn test_apply_effects_single() {
let mut action = Action::new("test_action", 1.0).unwrap();
action.effects.set("has_result", true);
let mut state = State::new();
action.apply_effects(&mut state);
assert_eq!(state.get("has_result"), Some(true));
}
#[test]
fn test_apply_effects_multiple() {
let mut action = Action::new("test_action", 1.0).unwrap();
action.effects.set("has_result", true);
action.effects.set("is_complete", true);
let mut state = State::new();
action.apply_effects(&mut state);
assert_eq!(state.get("has_result"), Some(true));
assert_eq!(state.get("is_complete"), Some(true));
}
#[test]
fn test_apply_effects_overwrite() {
let mut action = Action::new("test_action", 1.0).unwrap();
action.effects.set("has_result", true);
let mut state = State::new();
state.set("has_result", false);
action.apply_effects(&mut state);
assert_eq!(state.get("has_result"), Some(true));
}
}