-- |
-- Module      :  System.AtomicWrite.Writer.ByteStringBuilder
-- Copyright   :  © 2015-2019 Stack Builders Inc.
-- License     :  MIT
--
-- Maintainer  :  Stack Builders <hackage@stackbuilders.com>
-- Stability   :  experimental
-- Portability :  portable
--
-- Provides functionality to dump the contents of a ByteStringBuilder
-- to a file.

module System.AtomicWrite.Writer.ByteStringBuilder (atomicWriteFile, atomicWriteFileWithMode) where

import           System.AtomicWrite.Internal (closeAndRename, maybeSetFileMode,
                                              tempFileFor)

import           Data.ByteString.Builder     (Builder, hPutBuilder)

import           GHC.IO.Handle               (BufferMode (BlockBuffering),
                                              hSetBinaryMode, hSetBuffering)

import           System.Posix.Types          (FileMode)

-- | Creates or modifies a file atomically on POSIX-compliant
-- systems while preserving permissions.
atomicWriteFile ::
  FilePath    -- ^ The path where the file will be updated or created
  -> Builder  -- ^ The content to write to the file
  -> IO ()
atomicWriteFile :: FilePath -> Builder -> IO ()
atomicWriteFile =
  Maybe FileMode -> FilePath -> Builder -> IO ()
atomicWriteFileMaybeMode Maybe FileMode
forall a. Maybe a
Nothing

-- | Creates or modifies a file atomically on POSIX-compliant
-- systems and updates permissions.
atomicWriteFileWithMode ::
  FileMode
  -> FilePath    -- ^ The path where the file will be updated or created
  -> Builder  -- ^ The content to write to the file
  -> IO ()
atomicWriteFileWithMode :: FileMode -> FilePath -> Builder -> IO ()
atomicWriteFileWithMode FileMode
mode =
  Maybe FileMode -> FilePath -> Builder -> IO ()
atomicWriteFileMaybeMode (Maybe FileMode -> FilePath -> Builder -> IO ())
-> Maybe FileMode -> FilePath -> Builder -> IO ()
forall a b. (a -> b) -> a -> b
$ FileMode -> Maybe FileMode
forall a. a -> Maybe a
Just FileMode
mode

-- Helper function
atomicWriteFileMaybeMode ::
  Maybe FileMode
  -> FilePath    -- ^ The path where the file will be updated or created
  -> Builder  -- ^ The content to write to the file
  -> IO ()
atomicWriteFileMaybeMode :: Maybe FileMode -> FilePath -> Builder -> IO ()
atomicWriteFileMaybeMode Maybe FileMode
mmode FilePath
path Builder
builder = do
  (FilePath
temppath, Handle
h) <- FilePath -> IO (FilePath, Handle)
tempFileFor FilePath
path

  -- Recommendations for binary and buffering are from the
  -- Data.ByteString.Builder docs:
  -- http://hackage.haskell.org/package/bytestring-0.10.2.0/docs/Data-ByteString-Builder.html#v:hPutBuilder
  Handle -> Bool -> IO ()
hSetBinaryMode Handle
h Bool
True
  Handle -> BufferMode -> IO ()
hSetBuffering Handle
h (Maybe Int -> BufferMode
BlockBuffering Maybe Int
forall a. Maybe a
Nothing)

  Handle -> Builder -> IO ()
hPutBuilder Handle
h Builder
builder

  Handle -> FilePath -> FilePath -> IO ()
closeAndRename Handle
h FilePath
temppath FilePath
path

  -- set new permissions if a FileMode was provided
  FilePath -> Maybe FileMode -> IO ()
maybeSetFileMode FilePath
path Maybe FileMode
mmode