module Hpack (
hpack
, hpackResult
, Result(..)
, Status(..)
, version
, main
#ifdef TEST
, hpackWithVersion
, extractVersion
, parseVersion
, splitDirectory
#endif
) where
import Prelude ()
import Prelude.Compat
import Control.Monad.Compat
import qualified Data.ByteString as B
import Data.List.Compat
import Data.Maybe
import qualified Data.Text as T
import Data.Text.Encoding (encodeUtf8)
import Data.Version (Version)
import qualified Data.Version as Version
import System.Environment
import System.Exit
import System.IO
import System.FilePath
import System.Directory
import Text.ParserCombinators.ReadP
import Paths_hpack (version)
import Hpack.Options
import Hpack.Config
import Hpack.Run
import Hpack.Util
programVersion :: Version -> String
programVersion v = "hpack version " ++ Version.showVersion v
header :: FilePath -> Version -> String
header p v = unlines [
"-- This file has been generated from " ++ p ++ " by " ++ programVersion v ++ "."
, "--"
, "-- see: https://github.com/sol/hpack"
, ""
]
main :: IO ()
main = do
args <- getArgs
case parseOptions args of
PrintVersion -> putStrLn (programVersion version)
Help -> printHelp
Run options -> case options of
Options _verbose True dir -> hpackStdOut dir
Options verbose False dir -> hpack dir verbose
ParseError -> do
printHelp
exitFailure
printHelp :: IO ()
printHelp = do
hPutStrLn stderr $ unlines [
"Usage: hpack [ --silent ] [ dir ] [ - ]"
, " hpack --version"
]
safeInit :: [a] -> [a]
safeInit [] = []
safeInit xs = init xs
extractVersion :: [String] -> Maybe Version
extractVersion = listToMaybe . mapMaybe (stripPrefix prefix >=> parseVersion . safeInit)
where
prefix = "-- This file has been generated from package.yaml by hpack version "
parseVersion :: String -> Maybe Version
parseVersion xs = case [v | (v, "") <- readP_to_S Version.parseVersion xs] of
[v] -> Just v
_ -> Nothing
hpack :: Maybe FilePath -> Bool -> IO ()
hpack = hpackWithVersion version
hpackResult :: Maybe FilePath -> IO Result
hpackResult = hpackWithVersionResult version
data Result = Result {
resultWarnings :: [String]
, resultCabalFile :: String
, resultStatus :: Status
}
data Status = Generated | AlreadyGeneratedByNewerHpack | OutputUnchanged
hpackWithVersion :: Version -> Maybe FilePath -> Bool -> IO ()
hpackWithVersion v p verbose = do
r <- hpackWithVersionResult v p
printWarnings (resultWarnings r)
when verbose $ putStrLn $
case resultStatus r of
Generated -> "generated " ++ resultCabalFile r
OutputUnchanged -> resultCabalFile r ++ " is up-to-date"
AlreadyGeneratedByNewerHpack -> resultCabalFile r ++ " was generated with a newer version of hpack, please upgrade and try again."
printWarnings :: [String] -> IO ()
printWarnings warnings = do
forM_ warnings $ \warning -> hPutStrLn stderr ("WARNING: " ++ warning)
splitDirectory :: Maybe FilePath -> IO (Maybe FilePath, FilePath)
splitDirectory Nothing = return (Nothing, packageConfig)
splitDirectory (Just p) = do
isDirectory <- doesDirectoryExist p
return $ if isDirectory
then (Just p, packageConfig)
else let
file = takeFileName p
dir = takeDirectory p
in (guard (p /= file) >> Just dir, if null file then packageConfig else file)
hpackWithVersionResult :: Version -> Maybe FilePath -> IO Result
hpackWithVersionResult v p = do
(dir, file) <- splitDirectory p
(warnings, cabalFile, new) <- run dir file
old <- fmap splitHeader <$> tryReadFile cabalFile
let oldVersion = fmap fst old >>= extractVersion
status <-
if (oldVersion <= Just v) then
if (fmap snd old == Just (lines new)) then
return OutputUnchanged
else do
B.writeFile cabalFile $ encodeUtf8 $ T.pack $ header file v ++ new
return Generated
else
return AlreadyGeneratedByNewerHpack
return Result
{ resultWarnings = warnings
, resultCabalFile = cabalFile
, resultStatus = status
}
where
splitHeader :: String -> ([String], [String])
splitHeader = fmap (dropWhile null) . span ("--" `isPrefixOf`) . lines
hpackStdOut :: Maybe FilePath -> IO ()
hpackStdOut p = do
(dir, file) <- splitDirectory p
(warnings, _cabalFile, new) <- run dir file
B.putStr $ encodeUtf8 $ T.pack new
printWarnings warnings