packages feed

hindent-5.0.1: src/main/Main.hs

{-# LANGUAGE Unsafe #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}

-- | Main entry point to hindent.
--
-- hindent

module Main where

import           HIndent
import           HIndent.Types
import qualified Data.ByteString as S
import qualified Data.ByteString.Builder as S
import qualified Data.ByteString.Lazy.Char8 as L8
import           Control.Applicative
import           Data.Text (Text)
import qualified Data.Text as T
import           Data.Version (showVersion)
import           Descriptive
import           Descriptive.Options
import           Language.Haskell.Exts hiding (Style,style)
import           Paths_hindent (version)
import           System.Directory
import           System.Environment
import           System.IO
import           Text.Read
import           Control.Exception
import           GHC.IO.Exception
import           Foreign.C.Error

-- | Main entry point.
main :: IO ()
main = do
    args <- getArgs
    case consume options (map T.pack args) of
        Succeeded (style,exts,mfilepath) ->
            case mfilepath of
                Just filepath -> do
                    text <- S.readFile filepath
                    tmpDir <- getTemporaryDirectory
                    (fp,h) <- openTempFile tmpDir "hindent.hs"
                    L8.putStrLn
                                   (either
                                        error
                                        S.toLazyByteString
                                        (reformat style (Just exts) text))
                    hFlush h
                    hClose h
                    let exdev e =
                            if ioe_errno e ==
                               Just
                                   ((\(Errno a) ->
                                          a)
                                        eXDEV)
                                then copyFile fp filepath >> removeFile fp
                                else throw e
                    renameFile fp filepath `catch` exdev
                Nothing ->
                    L8.interact
                           (either error S.toLazyByteString . reformat style (Just exts) . L8.toStrict)
        Failed (Wrap (Stopped Version) _) ->
            putStrLn ("hindent " ++ showVersion version)
        Failed (Wrap (Stopped Help) _) -> putStrLn help
        _ -> error help
  where

help :: [Char]
help =
    "hindent " ++
    T.unpack (textDescription (describe options [])) ++
    "\nVersion " ++ showVersion version ++ "\n" ++
    "The --style option is now ignored, but preserved for backwards-compatibility.\n" ++
    "Johan Tibell is the default and only style."

-- | Options that stop the argument parser.
data Stoppers = Version | Help
  deriving (Show)

-- | Program options.
options :: Monad m
        => Consumer [Text] (Option Stoppers) m (Config,[Extension],Maybe FilePath)
options = ver *> ((,,) <$> style <*> exts <*> file)
  where
    ver =
        stop (flag "version" "Print the version" Version) *>
        stop (flag "help" "Show help" Help)
    style =
        makeStyle <$>
        fmap
            (const defaultConfig)
            (optional
                 (constant "--style" "Style to print with" () *>
                  anyString "STYLE")) <*>
        lineLen
    exts = fmap getExtensions (many (prefix "X" "Language extension"))
    lineLen =
        fmap
            (>>= (readMaybe . T.unpack))
            (optional (arg "line-length" "Desired length of lines"))
    makeStyle s mlen =
        case mlen of
            Nothing -> s
            Just len ->
                s
                { configMaxColumns = len
                }
    file = fmap (fmap T.unpack) (optional (anyString "[<filename>]"))