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
//! interface to the knowledge base of an agent
use crate::{Error, Result};

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

/// Trait for the low level interface to the knowledge base
pub trait KnowledgeBase: Sync + Send + dyn_clone::DynClone + 'static
{
  /// Insert data
  fn insert_data(&self, collection: String, key: String, data: Vec<u8>) -> Result<()>;
  /// Retrieve data
  fn retrieve_data(&self, collection: String, key: String) -> Result<Vec<u8>>;
}

dyn_clone::clone_trait_object!(KnowledgeBase);

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

/// High level interface to the knowledge base
pub trait KnowledgeBaseInterface: KnowledgeBase
{
  /// Insert a serializable object
  fn insert(
    &self,
    collection: impl Into<String>,
    key: impl Into<String>,
    data: &impl serde::Serialize,
  ) -> Result<()>;
  /// Retrieve and deserialize object
  fn retrieve<T: serde::de::DeserializeOwned>(
    &self,
    collection: impl Into<String>,
    key: impl Into<String>,
  ) -> Result<T>;
}

impl<TKnowledgeBase: ?Sized + KnowledgeBase> KnowledgeBaseInterface for TKnowledgeBase
{
  /// Insert a serializable object
  fn insert(
    &self,
    collection: impl Into<String>,
    key: impl Into<String>,
    data: &impl serde::Serialize,
  ) -> Result<()>
  {
    let mut data_u8 = Vec::<u8>::new();
    ciborium::into_writer(data, &mut data_u8)?;
    self.insert_data(collection.into(), key.into(), data_u8)
  }
  fn retrieve<T: serde::de::DeserializeOwned>(
    &self,
    collection: impl Into<String>,
    key: impl Into<String>,
  ) -> Result<T>
  {
    let data = self.retrieve_data(collection.into(), key.into())?;
    ciborium::from_reader::<T, &[u8]>(data.as_ref())
      .map_err(|e| Error::DeserializationError(e.to_string()))
  }
}

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

/// An implementation of KnowledgeBase where the data is stored in memory
#[derive(Clone)]
pub struct InMemoryBase
{
  storage: ccutils::sync::ArcMutex<
    std::collections::HashMap<String, std::collections::HashMap<String, Vec<u8>>>,
  >,
}

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

impl InMemoryBase
{
  /// Create a new in memory database
  pub fn new() -> Self
  {
    Self {
      storage: Default::default(),
    }
  }
}

impl KnowledgeBase for InMemoryBase
{
  fn insert_data(&self, collection: String, key: String, data: Vec<u8>) -> Result<()>
  {
    let mut storage = self.storage.lock()?;
    if !storage.contains_key(&collection)
    {
      storage.insert(collection.clone(), Default::default());
    }
    if let Some(v) = storage.get_mut(&collection)
    {
      v.insert(key, data);
      Ok(())
    }
    else
    {
      Err(Error::InvalidCollection(collection))
    }
  }
  fn retrieve_data(&self, collection: String, key: String) -> Result<Vec<u8>>
  {
    let storage = self.storage.lock()?;
    if let Some(v) = storage.get(&collection)
      && let Some(v) = v.get(&key)
    {
      return Ok(v.to_owned());
    }

    Err(Error::UnknownValue(collection, key))
  }
}