require-0.4.9: library/Require/File.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-- | This module contains functions for file handling. The names of the
-- functions and data types are chosen in a way to suggest a qualified import.
--
-- @
-- __import qualified__ Require.File __as__ File
-- @
module Require.File where
import Relude
import qualified Data.Text.IO as TIO
-- | Wraps the name of a file as given by the user. Usually this corresponds to
-- the file's path.
newtype Name = Name Text
-- | Associates a file's contents with the path from which it was read.
data Input = Input
{ inputName :: Name,
inputContent :: Text
}
-- | A type-safe wrapper for line numbers.
newtype LineNumber = LineNumber Int
deriving (Enum)
-- | Identifies a specific line in a file.
data LineTag = LineTag !Name !LineNumber
-- | Returns the 'LineTag' referencing the first line in a given 'FileInput'.
--
-- Note that the tag's line number is 1-based, which fits well with how GHC
-- understands @{-\# LINE ... #-}@ pragmas.
initialLineTag :: Input -> LineTag
initialLineTag inp = LineTag (inputName inp) (LineNumber 1)
-- | Returns a line tag from the same file but referencing the next line.
advanceLineTag :: LineTag -> LineTag
advanceLineTag (LineTag fn ln) = LineTag fn (succ ln)
-- | @read name@ reads the contents of the file identified by @name@.
read :: Name -> IO Input
read f = Input f <$> readFileText (nameToPath f)
-- | @write name lines@ writes all every line in @lines@ to the file identified
-- by @name@ and appends a newline.
writeLines :: Name -> [Text] -> IO ()
writeLines name theLines = withFile (nameToPath name) WriteMode $ \h ->
traverse_ (TIO.hPutStrLn h) theLines
-- | Splits the input into lines and annotates each with a 'LineTag'.
inputLines :: Input -> [(LineTag, Text)]
inputLines fi = zip lineTags contents
where
lineTags = iterate advanceLineTag (initialLineTag fi)
contents = lines (inputContent fi)
-- | Returns the 'FilePath' corresponding a given 'Name'.
nameToPath :: Name -> FilePath
nameToPath (Name fp) = toString fp