agent-tk 0.4.0

`agent-tk` (`agent toolkit/tasks-knowledge`) is a crate for the development of autonomous agents using Rust, with an emphasis on tasks and knowledge based agents. This project is part of the [auKsys](http://auksys.org) project.
Documentation
//! Module to define the state of an agent.

use serde::{Deserialize, Serialize};
use std::any::TypeId;
use std::ops::{Deref, DerefMut};

use crate::{Error, Result};

// Structures

/// Represent a position state
#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Position
{
  /// Longitude
  pub longitude: f64,
  /// Latitude
  pub latitude: f64,
}

/// Represent a velocity state
#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Velocity
{
  /// x-component of the velocity
  pub x: f32,
  /// y-component of the velocity
  pub y: f32,
}

/// Trait to conveniently build a State from one of the possible states
pub trait StateTrait
{
  /// Convert to a State enum
  fn to_state(self) -> State;
}

//  ____    _             _
// / ___|  | |_    __ _  | |_    ___
// \___ \  | __|  / _` | | __|  / _ \
//  ___) | | |_  | (_| | | |_  |  __/
// |____/   \__|  \__,_|  \__|  \___|

/// Enum that serves as a container for any of the state
#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum State
{
  /// Position state
  Position(Position),
  /// Velocity state
  Velocity(Velocity),
}

//  ____    _             _
// / ___|  | |_    __ _  | |_    ___   ___
// \___ \  | __|  / _` | | __|  / _ \ / __|
//  ___) | | |_  | (_| | | |_  |  __/ \__ \
// |____/   \__|  \__,_|  \__|  \___| |___/

/// States of an agent
#[derive(Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct States(Vec<State>);

impl Deref for States
{
  type Target = Vec<State>;
  fn deref(&self) -> &Self::Target
  {
    &self.0
  }
}

impl DerefMut for States
{
  fn deref_mut(&mut self) -> &mut Self::Target
  {
    &mut self.0
  }
}

// Implementation

macro_rules! build_states_getter {
  ($(#[$attr:meta])* => ($name:tt, $type:tt)) => {
    $(#[$attr])*
    pub fn $name(&self) -> Result<$type>
    {
      let res = self.0.iter().find_map(|x| match x
      {
        State::$type(p) => Some(p),
        _ => None,
      });
      if res.is_none()
      {
        Err(Error::UnknownState(std::any::type_name::<$type>()))
      }
      else
      {
        Ok(*res.unwrap())
      }
    }
  };
}

impl States
{
  build_states_getter!(
    /// Get the position state, if any
    => (get_position, Position));
  build_states_getter!(
    /// Get the velocity state, if any
    => (get_velocity, Velocity));
  /// Add a state
  pub fn add_state<T: StateTrait + 'static>(&mut self, t: T) -> crate::Result<&mut States>
  {
    let it = self.0.iter().find(|x| match x
    {
      State::Position(_) => TypeId::of::<Position>() == TypeId::of::<T>(),
      State::Velocity(_) => TypeId::of::<Velocity>() == TypeId::of::<T>(),
    });
    if it.is_none()
    {
      self.push(t.to_state());
      Ok(self)
    }
    else
    {
      Err(Error::DuplicateState(std::any::type_name::<T>()))
    }
  }
  /// Update a state
  pub fn update_state<T: StateTrait + 'static>(&mut self, t: T) -> crate::Result<&mut States>
  {
    let it = self.0.iter().enumerate().find(|(_, x)| match x
    {
      State::Position(_) => TypeId::of::<Position>() == TypeId::of::<T>(),
      State::Velocity(_) => TypeId::of::<Velocity>() == TypeId::of::<T>(),
    });
    if let Some((index, _)) = it
    {
      self.remove(index);
      self.push(t.to_state());
      Ok(self)
    }
    else
    {
      Err(Error::UnknownState(std::any::type_name::<T>()))
    }
  }
}

impl From<States> for SharedStates
{
  fn from(val: States) -> Self
  {
    SharedStates::new(val)
  }
}

impl StateTrait for Position
{
  fn to_state(self) -> State
  {
    State::Position(self)
  }
}

impl StateTrait for Velocity
{
  fn to_state(self) -> State
  {
    State::Velocity(self)
  }
}

//  ____    _                                 _   ____    _             _
// / ___|  | |__     __ _   _ __    ___    __| | / ___|  | |_    __ _  | |_    ___
// \___ \  | '_ \   / _` | | '__|  / _ \  / _` | \___ \  | __|  / _` | | __|  / _ \
//  ___) | | | | | | (_| | | |    |  __/ | (_| |  ___) | | |_  | (_| | | |_  |  __/
// |____/  |_| |_|  \__,_| |_|     \___|  \__,_| |____/   \__|  \__,_|  \__|  \___|

macro_rules! build_shared_states_getter {
  ($(#[$attr:meta])* => ($name:tt, $type:tt)) => {
    $(#[$attr])*
    pub fn $name(&self) -> Result<$type>
    {
      self.states.lock()?.$name()
    }
  };
}

/// States that are protected by a mutx
#[derive(Clone)]
pub struct SharedStates
{
  states: ccutils::sync::ArcMutex<States>,
}

impl SharedStates
{
  build_shared_states_getter!(
    /// Get the position state, if any
    => (get_position, Position));
  build_shared_states_getter!(
    /// Get the velocity state, if any
    => (get_velocity, Velocity));
  /// Create a shared states from an initial states
  pub fn new(states: States) -> Self
  {
    Self {
      states: states.into(),
    }
  }
  /// This function is used to update the states
  pub fn update_states(&self, updater: impl Fn(&mut crate::states::States)) -> Result<()>
  {
    let mut locked = self.states.lock()?;
    updater(&mut locked);
    Ok(())
  }
  /// Update a state
  pub fn update_state<T: crate::states::StateTrait + 'static>(&self, state: T)
    -> crate::Result<()>
  {
    self.states.lock()?.update_state(state)?;
    Ok(())
  }
  /// Return a copy of the states, as a non shared variant.
  pub fn to_owned_states(&self) -> Result<States>
  {
    Ok(self.states.lock()?.to_owned())
  }
}