------------------------------------------------------------------------
-- |
-- Module      :  ALife.Creatur.Database
-- Copyright   :  (c) 2012-2021 Amy de Buitléir
-- License     :  BSD-style
-- Maintainer  :  amy@nualeargais.ie
-- Stability   :  experimental
-- Portability :  portable
--
-- Database interface for the Créatúr framework.
--
------------------------------------------------------------------------
{-# LANGUAGE TypeFamilies, FlexibleContexts, ConstrainedClassMethods #-}

module ALife.Creatur.Database
 (
    Database(..),
    Record(..),
    SizedRecord(..)
 ) where

import Prelude hiding (lookup)
import Control.Monad.State (StateT)
import Data.Serialize (Serialize)

class Record r where
  key :: r -> String

class (Record r) => SizedRecord r where
  size :: r -> Int

-- | A database offering storage and retrieval for records.
class Database d where
  type DBRecord d
  -- | Get a list of all active keys in the database.
  keys :: StateT d IO [String]
  -- | Return the number of records stored in the database.
  numRecords :: StateT d IO Int
  -- | Get a list of all archived keys in the database. If the database
  --   does not implement archiving, it may return an empty list.
  archivedKeys :: StateT d IO [String]
  -- | Read an active record from the database.
  lookup :: Serialize (DBRecord d) => 
    String -> StateT d IO (Either String (DBRecord d))
  -- | Read an archived record from the database.
  lookupInArchive :: Serialize (DBRecord d) => 
    String -> StateT d IO (Either String (DBRecord d))
  -- | Fetch all active records from the database.
  readAll :: Serialize (DBRecord d) => 
    StateT d IO [Either String (DBRecord d)]
  readAll = StateT d IO [String]
forall d. Database d => StateT d IO [String]
keys StateT d IO [String]
-> ([String] -> StateT d IO [Either String (DBRecord d)])
-> StateT d IO [Either String (DBRecord d)]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (String -> StateT d IO (Either String (DBRecord d)))
-> [String] -> StateT d IO [Either String (DBRecord d)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM String -> StateT d IO (Either String (DBRecord d))
forall d.
(Database d, Serialize (DBRecord d)) =>
String -> StateT d IO (Either String (DBRecord d))
lookup
  -- | Read all archived records from the database.
  readAllInArchive :: Serialize (DBRecord d) => 
    StateT d IO [Either String (DBRecord d)]
  readAllInArchive = StateT d IO [String]
forall d. Database d => StateT d IO [String]
archivedKeys StateT d IO [String]
-> ([String] -> StateT d IO [Either String (DBRecord d)])
-> StateT d IO [Either String (DBRecord d)]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (String -> StateT d IO (Either String (DBRecord d)))
-> [String] -> StateT d IO [Either String (DBRecord d)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM String -> StateT d IO (Either String (DBRecord d))
forall d.
(Database d, Serialize (DBRecord d)) =>
String -> StateT d IO (Either String (DBRecord d))
lookupInArchive
  -- | Write a record to the database. 
  --   If an agent with the same name already exists, it will be overwritten.
  store :: (Record (DBRecord d), Serialize (DBRecord d)) => 
    DBRecord d -> StateT d IO ()
  -- | Remove a record from the database.
  --   The database may archive records rather than simply deleting them.
  delete :: Serialize (DBRecord d) => String -> StateT d IO ()