hnix-store-core-0.4.0.0: src/System/Nix/Internal/Nar/Effects.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
module System.Nix.Internal.Nar.Effects
( NarEffects(..)
, narEffectsIO
) where
import qualified Control.Exception.Lifted as Lifted
import qualified Control.Monad.Fail as MonadFail
import qualified Control.Monad.IO.Class as IO
import Control.Monad.Trans.Control (MonadBaseControl)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import Data.Int (Int64)
import qualified System.Directory as Directory
import qualified System.IO as IO
import System.Posix.Files (createSymbolicLink, fileSize,
getFileStatus, isDirectory,
readSymbolicLink)
data NarEffects (m :: * -> *) = NarEffects {
narReadFile :: FilePath -> m BSL.ByteString
, narWriteFile :: FilePath -> BSL.ByteString -> m ()
, narStreamFile :: FilePath -> m (Maybe BS.ByteString) -> m ()
, narListDir :: FilePath -> m [FilePath]
, narCreateDir :: FilePath -> m ()
, narCreateLink :: FilePath -> FilePath -> m ()
, narGetPerms :: FilePath -> m Directory.Permissions
, narSetPerms :: FilePath -> Directory.Permissions -> m ()
, narIsDir :: FilePath -> m Bool
, narIsSymLink :: FilePath -> m Bool
, narFileSize :: FilePath -> m Int64
, narReadLink :: FilePath -> m FilePath
, narDeleteDir :: FilePath -> m ()
, narDeleteFile :: FilePath -> m ()
}
-- | A particular @NarEffects@ that uses regular POSIX for file manipulation
-- You would replace this with your own @NarEffects@ if you wanted a
-- different backend
narEffectsIO
:: (IO.MonadIO m,
MonadFail.MonadFail m,
MonadBaseControl IO m
) => NarEffects m
narEffectsIO = NarEffects {
narReadFile = IO.liftIO . BSL.readFile
, narWriteFile = \a b -> IO.liftIO $ BSL.writeFile a b
, narStreamFile = streamStringOutIO
, narListDir = IO.liftIO . Directory.listDirectory
, narCreateDir = IO.liftIO . Directory.createDirectory
, narCreateLink = \f t -> IO.liftIO $ createSymbolicLink f t
, narGetPerms = IO.liftIO . Directory.getPermissions
, narSetPerms = \f p -> IO.liftIO $ Directory.setPermissions f p
, narIsDir = \d -> fmap isDirectory $ IO.liftIO (getFileStatus d)
, narIsSymLink = IO.liftIO . Directory.pathIsSymbolicLink
, narFileSize = \n -> fmap (fromIntegral . fileSize) $ IO.liftIO (getFileStatus n)
, narReadLink = IO.liftIO . readSymbolicLink
, narDeleteDir = IO.liftIO . Directory.removeDirectoryRecursive
, narDeleteFile = IO.liftIO . Directory.removeFile
}
-- | This default implementation for @narStreamFile@ requires @IO.MonadIO@
streamStringOutIO
:: forall m
.(IO.MonadIO m,
MonadFail.MonadFail m,
MonadBaseControl IO m
) => FilePath
-> m (Maybe BS.ByteString)
-> m ()
streamStringOutIO f getChunk =
Lifted.bracket
(IO.liftIO (IO.openFile f IO.WriteMode)) (IO.liftIO . IO.hClose) go
`Lifted.catch`
cleanupException
where
go :: IO.Handle -> m ()
go handle = do
chunk <- getChunk
case chunk of
Nothing -> return ()
Just c -> do
IO.liftIO $ BS.hPut handle c
go handle
cleanupException (e :: Lifted.SomeException) = do
IO.liftIO $ Directory.removeFile f
MonadFail.fail $
"Failed to stream string to " ++ f ++ ": " ++ show e