packages feed

hfmt-0.2.0: app/Main.hs

{-# LANGUAGE Rank2Types #-}

module Main
  ( main
  ) where

import Actions
import ExitCode
import Language.Haskell.Format
import Language.Haskell.Format.Utilities
import Language.Haskell.Source.Enumerator
import Options
import Types

import Conduit
import Options.Applicative.Extra          as OptApp
import System.Directory
import System.Exit

main :: IO ()
main = do
  options <- execParser Options.parser
  formattedCodeDiffers <- run options
  exitWith $ exitCode (optAction options) formattedCodeDiffers

run :: Options -> IO Bool
run options =
  runConduit $
  sources .| mapMC readSource .| mapMC formatSource .| handleFormatErrors .|
  mapMC action .|
  summarize
  where
    paths = do
      let explicitPaths = optPaths options
      if null explicitPaths
        then do
          currentPath <- getCurrentDirectory
          return [currentPath]
        else return explicitPaths
    sources :: Source IO SourceFile
    sources = lift paths >>= mapM_ sourcesFromPath
    formatSource source = do
      formatter <- defaultFormatter
      return $ applyFormatter formatter source
    handleFormatErrors = concatMapMC handleFormatError
    action = Actions.act options
    summarize =
      anyC' (\(Formatted _ source result) -> wasReformatted source result)

sourcesFromPath :: FilePath -> Source IO SourceFile
sourcesFromPath "-"  = yield StdinSource
sourcesFromPath path = enumeratePath path .| mapC SourceFilePath

readSource :: SourceFile -> IO SourceFileWithContents
readSource s@(SourceFilePath path) =
  SourceFileWithContents s . HaskellSource path <$> readFile path
readSource s@StdinSource =
  SourceFileWithContents s . HaskellSource "stdin" <$> getContents

applyFormatter :: Formatter -> SourceFileWithContents -> FormatResult
applyFormatter (Formatter doFormat) (SourceFileWithContents file contents) =
  case doFormat contents of
    Left err       -> Left (FormatError file err)
    Right reformat -> Right (Formatted file contents reformat)

handleFormatError :: FormatResult -> IO (Maybe Formatted)
handleFormatError (Left err)        = printFormatError err >> return Nothing
handleFormatError (Right formatted) = return $ Just formatted

printFormatError :: FormatError -> IO ()
printFormatError (FormatError input errorString) =
  putStrLn ("Error reformatting " ++ show input ++ ": " ++ errorString)

-- | Check that at least one value in the stream returns True.
--
-- Does not shortcut, entire stream is always consumed
anyC' :: Monad m => (a -> Bool) -> Consumer a m Bool
anyC' f = do
  result <- anyC f -- Check for at least one value, may shortcut
  sinkNull -- consume any remaining input skipped by a shortcut
  return result