cabal-fmt-0.1.4: cli/Main.hs
-- |
-- License: BSD-3-Clause
-- Copyright: Oleg Grenrus
module Main (main) where
import Control.Applicative (many, (<**>))
import Control.Monad (unless, when)
import Data.Foldable (asum, for_)
import Data.Traversable (for)
import Data.Version (showVersion)
import System.Exit (exitFailure)
import System.FilePath (takeDirectory)
import System.IO (hPutStrLn, stderr)
import qualified Data.ByteString as BS
import qualified Options.Applicative as O
import CabalFmt (cabalFmt)
import CabalFmt.Error (renderError)
import CabalFmt.Monad (runCabalFmtIO)
import CabalFmt.Options
import CabalFmt.Prelude
import Paths_cabal_fmt (version)
main :: IO ()
main = do
(opts', filepaths) <- O.execParser optsP'
let opts = runOptionsMorphism opts' defaultOptions
notFormatted <- catMaybes <$> case filepaths of
[] -> fmap pure $ BS.getContents >>= main' opts Nothing
(_:_) -> for filepaths $ \filepath -> do
contents <- BS.readFile filepath
main' opts (Just filepath) contents
when ((optMode opts == ModeCheck) && not (null notFormatted)) $ do
for_ notFormatted $ \filepath ->
hPutStrLn stderr $ "error: Input " <> filepath <> " is not formatted."
exitFailure
where
optsP' = O.info (optsP <**> O.helper <**> versionP) $ mconcat
[ O.fullDesc
, O.progDesc "Reformat .cabal files"
, O.header "cabal-fmt - .cabal file reformatter"
]
versionP = O.infoOption (showVersion version)
$ O.long "version" <> O.help "Show version"
main' :: Options -> Maybe FilePath -> BS.ByteString -> IO (Maybe FilePath)
main' opts mfilepath input = do
-- name of the input
let filepath = fromMaybe "<stdin>" mfilepath
-- process
res <- runCabalFmtIO (takeDirectory <$> mfilepath) opts (cabalFmt filepath input)
case res of
Right output -> do
let outputBS = toUTF8BS output
formatted = outputBS == input
case optMode opts of
ModeStdout -> BS.putStr outputBS
ModeInplace -> case mfilepath of
Nothing -> BS.putStr outputBS
Just _ -> unless formatted $ BS.writeFile filepath outputBS
_ -> return ()
return $ if formatted then Nothing else Just filepath
Left err -> do
renderError err
exitFailure
-------------------------------------------------------------------------------
-- Options parser
-------------------------------------------------------------------------------
optsP :: O.Parser (OptionsMorphism, [FilePath])
optsP = (,)
<$> optsP'
<*> many (O.strArgument (O.metavar "FILE..." <> O.help "input files"))
where
optsP' = fmap mconcat $ many $ asum
[ werrorP
, noWerrorP
, indentP
, tabularP
, noTabularP
, cabalFileP
, noCabalFileP
, stdoutP
, inplaceP
, checkP
]
werrorP = O.flag' (mkOptionsMorphism $ \opts -> opts { optError = True })
$ O.long "Werror" <> O.help "Treat warnings as errors"
noWerrorP = O.flag' (mkOptionsMorphism $ \opts -> opts { optError = False })
$ O.long "Wno-error"
indentP = O.option (fmap (\n -> mkOptionsMorphism $ \opts -> opts { optIndent = n}) O.auto)
$ O.long "indent" <> O.help "Indentation" <> O.metavar "N"
tabularP = O.flag' (mkOptionsMorphism $ \opts -> opts { optTabular = True })
$ O.long "tabular" <> O.help "Tabular formatting"
noTabularP = O.flag' (mkOptionsMorphism $ \opts -> opts { optTabular = False })
$ O.long "no-tabular"
cabalFileP = O.flag' (mkOptionsMorphism $ \opts -> opts { optCabalFile = True })
$ O.long "cabal-file"
noCabalFileP = O.flag' (mkOptionsMorphism $ \opts -> opts { optCabalFile = False })
$ O.short 'n' <> O.long "no-cabal-file" <> O.help "Don't parse as .cabal file"
stdoutP = O.flag' (mkOptionsMorphism $ \opts -> opts { optMode = ModeStdout })
$ O.long "stdout" <> O.help "Write output to stdout (default)"
inplaceP = O.flag' (mkOptionsMorphism $ \opts -> opts { optMode = ModeInplace })
$ O.short 'i' <> O.long "inplace" <> O.help "Process files in-place"
checkP = O.flag' (mkOptionsMorphism $ \opts -> opts { optMode = ModeCheck })
$ O.short 'c' <> O.long "check" <> O.help "Fail with non-zero exit code if input is not formatted"