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
//! capabilities
//! ============
//!
//! Support for defining capabilities of an agent.

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

use crate::{Error, Result};

// Structures

/// Trait for capability
pub trait CapabilityTrait
{
  /// Indicate if the capability is unique
  const IS_UNIQUE: bool;
  /// Convert to the capability enum
  fn to_capability(self) -> Capability;
}

/// Capability enum
#[derive(Clone)]
#[allow(missing_docs)]
pub enum Capability
{
  Move(Move),
  Acquire(Acquire),
  Grasp(Grasp),
}

macro_rules! define_capability {
  ($type:ident, unique) => {
    impl CapabilityTrait for $type
    {
      const IS_UNIQUE: bool = true;
      fn to_capability(self) -> Capability
      {
        return Capability::$type(self);
      }
    }
  };
  ($type:ident) => {
    impl CapabilityTrait for $type
    {
      const IS_UNIQUE: bool = false;
      fn to_capability(self) -> Capability
      {
        return Capability::$type(self);
      }
    }
  };
}

/// Move Capability
#[derive(Copy, Clone)]
pub struct Move
{
  max_velocity: f32,
}

impl Move
{
  /// New move capability with the given max velocity
  pub fn new(max_velocity: f32) -> Move
  {
    Move { max_velocity }
  }
  /// Get the max velocity
  pub fn get_max_velocity(&self) -> f32
  {
    self.max_velocity
  }
}

define_capability!(Move, unique);

/// Acquire capability, needs a sensor

#[derive(Clone)]
pub struct Acquire {}

impl Default for Acquire
{
  fn default() -> Self
  {
    Self::new()
  }
}

impl Acquire
{
  /// New acquire capability
  pub fn new() -> Acquire
  {
    Acquire {}
  }
}

define_capability!(Acquire);

/// Grasp
#[derive(Clone)]
pub struct Grasp {}

define_capability!(Grasp);

/// Capabilities of an agent
#[derive(Default, Clone)]
pub struct Capabilities(Vec<Capability>);

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

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

macro_rules! build_capability_getter {
  ($name:tt, $type:tt) => {
    /// Get the capability for $name
    pub fn $name(&self) -> Result<$type>
    {
      let res = self.0.iter().find_map(|x| match x
      {
        Capability::$type(p) => Some(p),
        _ => None,
      });
      if res.is_none()
      {
        Err(Error::UnknownCapability(std::any::type_name::<$type>()))
      }
      else
      {
        Ok(*res.unwrap())
      }
    }
  };
}

macro_rules! build_capabilities_getter {
  ($name:tt, $type:tt) => {
    /// Get the capability for $name
    pub fn $name(&self) -> Result<Vec<$type>>
    {
      let res = self
        .0
        .iter()
        .filter_map(|x| match x
        {
          Capability::$type(p) => Some(p),
          _ => None,
        })
        .map(|x| x.clone())
        .collect::<Vec<$type>>();
      if res.is_empty()
      {
        Err(Error::UnknownCapability(std::any::type_name::<$type>()))
      }
      else
      {
        Ok(res)
      }
    }
  };
}

impl Capabilities
{
  build_capability_getter!(get_move, Move);
  build_capabilities_getter!(get_acquire, Acquire);
  /// Add a capability to an agent
  pub fn add_capability<T: CapabilityTrait + 'static>(
    &mut self,
    t: T,
  ) -> crate::Result<&mut Capabilities>
  {
    // If the capability is unique, check it wasn't already added
    if T::IS_UNIQUE
    {
      let it = self.0.iter().find(|x| match x
      {
        Capability::Move(_) => TypeId::of::<Move>() == TypeId::of::<T>(),
        Capability::Acquire(_) => TypeId::of::<Acquire>() == TypeId::of::<T>(),
        Capability::Grasp(_) => TypeId::of::<Grasp>() == TypeId::of::<T>(),
      });
      if it.is_none()
      {
        self.push(t.to_capability());
        Ok(self)
      }
      else
      {
        Err(Error::DuplicateCapability(std::any::type_name::<T>()))
      }
    }
    else
    {
      self.push(t.to_capability());
      Ok(self)
    }
  }
}