packages feed

lambdacat-0.1.0: LambdaCat/Session.hs

-- |
-- Module      : LambdaCat.Session
-- Copyright   : Andreas Baldeau, Daniel Ehlers
-- License     : BSD3
-- Maintainer  : Andreas Baldeau <andreas@baldeau.net>,
--               Daniel Ehlers <danielehlers@mindeye.net>
-- Stability   : Alpha
--
-- This module provides some basic session handling.

module LambdaCat.Session
    (
      -- * Datatypes
      MSession
    , Session (..)
    , Tab (..)

      -- * Concurrent session handling
    , newMSession
    , updateMSession
    , withMSession

      -- * Session access and manipulation
    , newSession
    , getSession

    , newTab
    , getTab
    , updateTab
    , deleteTab
    )
where

import Control.Concurrent.MVar
import Data.Map (Map)
import qualified Data.Map as Map
import Network.URI (URI)

import LambdaCat.History (History)
import qualified LambdaCat.History as History
import LambdaCat.View

-- | This datatype stores session related information.
data Session tabIdent tabmeta = Session
    { sessionTabs      :: Map tabIdent (Tab tabmeta)  -- ^ Map of tab's.
    , sessionTabActive :: Maybe tabIdent              -- ^ ID of the currently
                                                      -- active tab.
    }

-- | Datatypes for save concurrent access to session data.
newtype MSession tabIdent tabMeta = MSession
    { unMSession :: MVar (Session tabIdent tabMeta)  -- ^ Extract the stored
                                                     -- MVar.
    }

-- | Datatype representation of a tab.
data Tab tabmeta  = Tab
    { tabView    :: View     -- ^ Current view of the tab.
    , tabMeta    :: tabmeta  -- ^ Metadata connected to the tab.
    , tabHistory :: History  -- ^ History of the tab.
    }

-- | Create a (mutable) 'Session' for save concurrent access.
newMSession :: IO (MSession tabIdent tabMeta)
newMSession = return . MSession =<< newMVar newSession

-- | Create a session.
newSession :: Session tabIdent tabMeta
newSession = Session
    { sessionTabs      = Map.empty
    , sessionTabActive = Nothing
    }

-- | Create a new 'Tab' in the given session and add the given URI to its
-- initial 'History'.
newTab :: Ord tabIdent
       => tabIdent                  -- ^ The tab's identifier.
       -> View                      -- ^ The tab's view.
       -> tabMeta                   -- ^ Metadata to store with the tab.
       -> URI                       -- ^ The initial URI for the history.
       -> Session tabIdent tabMeta  -- ^ Session to add the tab to.
       -> Session tabIdent tabMeta  -- ^ Session containing the tab.
newTab ti view tm uri session =
    session { sessionTabs = Map.insert ti tab sessTabs }
  where
    tab = Tab
        { tabView    = view
        , tabHistory = History.singleton uri
        , tabMeta    = tm
        }

    sessTabs = sessionTabs session

-- | Update a tab within a 'Session'
updateTab :: (Eq tabIdent, Ord tabIdent)
          => Session tabIdent tabMeta              -- ^ The session to
                                                   -- operate on.
          -> tabIdent                              -- ^ Identifier of the tab
                                                   -- that should be modified.
          -> (Tab tabMeta -> Maybe (Tab tabMeta))  -- ^ Function that performs
                                                   -- the update. If 'Nothing'
                                                   -- is returned the tab gets
                                                   -- deleted.
          -> Session tabIdent tabMeta              -- ^ Modified session.
updateTab session tabId f =
    session { sessionTabs = newTabs }
  where
    tabs     = sessionTabs session
    newTabs  = Map.updateWithKey search tabId tabs  -- TODO fix
    search _ = f

-- | Get a tab specified by its identifier from the given session.
getTab
    :: Ord tabIdent
    => tabIdent                  -- ^ Identifier of the tab to get.
    -> Session tabIdent tabMeta  -- ^ Session to get the tab from.
    -> Maybe (Tab tabMeta)       -- ^ 'Nothing' if no tab for the identifier
                                 -- is found in the session.
getTab ti session =
    Map.lookup ti sessTabs
  where
    sessTabs = sessionTabs session

-- | Removes a tab from the session.
deleteTab
    :: Ord tabIdent
    => tabIdent                  -- ^ Identifier of the tab to remove.
    -> Session tabIdent tabMeta  -- ^ Session to remove from.
    -> Session tabIdent tabMeta  -- ^ Session without the tab.
deleteTab ident session =
    session { sessionTabs = Map.delete ident tabs }
  where
    tabs = sessionTabs session

-- | Return the current session stored in the 'MSession'.
getSession :: MSession tabIdent tabMeta -> IO (Session tabIdent tabMeta)
getSession = readMVar . unMSession

-- | Update the session stored in the given 'MSession' by applying the given
-- function and storing the result back in the MSession.
--
-- During this operation no other thread can access the session. If one tries
-- it will block.
updateMSession
    :: MSession tabIdent tabMeta
    -> (Session tabIdent tabMeta -> IO (Session tabIdent tabMeta, a))
    ->  IO a
updateMSession msession = modifyMVar (unMSession msession)

-- | Apply the given function to current session.
--
-- While the function is processed no other thread can access the session. If
-- one tries it will block.
withMSession
    :: MSession tabIdent tabMeta
    -> (Session tabIdent tabMeta -> IO a)
    -> IO a
withMSession msession = withMVar (unMSession msession)