{-# LANGUAGE FlexibleContexts #-} module Network.NineP.Internal.State ( Nine , NineVersion(..) , readVersion , Config(..) , NineState(..) , emptyState , lookup , insert , delete , iounit , call ) where import Control.Concurrent.MState import Control.Exception (throw) import Control.Exception.Peel as P import Control.Monad.Catch import Control.Monad.EmbedIO import Control.Monad.IO.Peel import Control.Monad.Reader import Control.Monad.State.Class import Data.List (isPrefixOf) import Data.Map (Map) import qualified Data.Map as M import Data.Word import Prelude hiding (lookup) import Network.NineP.Error import Network.NineP.Internal.File data NineVersion = VerUnknown | Ver9P2000 instance Show NineVersion where show VerUnknown = "unknown" show Ver9P2000 = "9P2000" readVersion :: String -> NineVersion readVersion s = if isPrefixOf "9P2000" s then Ver9P2000 else VerUnknown -- |Server configuration. data Config m = Config { -- |The @/@ directory of the hosted filesystem. root :: NineFile m, -- |The listening address. The syntax is taken from @Plan 9@ operating system and has the form @unix!\/path\/to\/socket@ for unix socket files, and @tcp!hostname!port@ for tcp sockets. addr :: String, -- |The initial state for the user-supplied monad. Use 'Void' for 'IO'. monadState :: Content m } data NineState m = NineState { fidMap :: Map Word32 (NineFile m), msize :: Word32, protoVersion :: NineVersion, mState :: Content m } emptyState m = NineState { fidMap = M.empty :: Map Word32 (NineFile m), msize = 0, protoVersion = VerUnknown, mState = m } type Nine m x = MState (NineState m) (ReaderT (Config m) IO) x instance MonadThrow m => MonadThrow (MState s m) where throwM e = lift $ throwM e instance (MonadCatch m, MonadPeelIO m) => MonadCatch (MState s m) where catch = P.catch call :: (EmbedIO m) => m a -> MState (NineState m) (ReaderT (Config m) IO) a call x = do s <- (return . mState) =<< get lift $ lift $ callback x s lookup :: Word32 -> Nine m (NineFile m) lookup fid = do m <- (return . fidMap) =<< get case M.lookup fid m of Nothing -> throw $ ENoFid fid Just f -> return f insert :: Word32 -> NineFile m -> Nine m () insert fid f = do m <- (return . fidMap) =<< get modifyM_ (\s -> s { fidMap = M.insert fid f $ fidMap s }) delete :: Word32 -> Nine m () delete fid = do modifyM_ (\s -> s { fidMap = M.delete fid $ fidMap s }) iounit :: Nine m Word32 iounit = do ms <- (return . msize) =<< get -- 23 is the biggest size of a message (write), but libixp uses 24, so we do too to stay safe return $ ms - 24