packages feed

hfmt-0.2.3.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
import System.IO

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

run :: Options -> IO RunResult
run opt =
  runConduit $
  sources opt .| mapMC readSource .| mapMC formatSource .| mapMC doAction .|
  foldMapMC toRunResult
  where
    formatSource source = do
      formatter <- defaultFormatter
      return $ applyFormatter formatter source
    doAction :: FormatResult -> IO FormatResult
    doAction = bimapM return (Actions.act opt)
    toRunResult :: FormatResult -> IO RunResult
    toRunResult (Left err) = do
      hPrint stderr (show err)
      return SourceParseFailure
    toRunResult (Right (Formatted _ source result)) =
      if wasReformatted source result
        then return HadDifferences
        else return NoDifferences

sources :: Options -> ConduitT () SourceFile IO ()
sources opt = lift paths >>= mapM_ sourcesFromPath
  where
    explicitPaths = optPaths opt
    paths =
      if null explicitPaths
        then do
          currentPath <- getCurrentDirectory
          return [currentPath]
        else return explicitPaths

sourcesFromPath :: FilePath -> ConduitT () SourceFile IO ()
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)

bimapM :: Monad m => (a -> m c) -> (b -> m d) -> Either a b -> m (Either c d)
bimapM f _ (Left a)  = Left <$> f a
bimapM _ g (Right b) = Right <$> g b