packages feed

hasql-th-format-0.1.0.0: app/Main.hs

module Main where

import Control.Monad (when)
import Data.ByteString qualified as BS
import Hasql.ThFormat (ProcessResult (..), processBytes, processFile)
import System.Environment (getArgs, getProgName)
import System.Exit (ExitCode (..), exitWith)
import Prelude


data Mode
    = -- | checkOnly, files
      ModeFiles Bool [FilePath]
    | ModeStdin


main :: IO ()
main = do
    args <- getArgs
    progName <- getProgName
    case parseArgs args of
        Left err -> do
            putStrLn err
            putStrLn (usage progName)
            exitWith (ExitFailure 2)
        Right ModeStdin -> do
            input <- BS.getContents
            case processBytes input of
                Left err -> do
                    putStrLn ("Error: " <> err)
                    exitWith (ExitFailure 1)
                Right output ->
                    BS.putStr output
        Right (ModeFiles checkOnly files)
            | null files -> do
                putStrLn (usage progName)
                exitWith (ExitFailure 2)
            | otherwise -> do
                results <- mapM (processOneFile checkOnly) files
                let hasErrors = any isError results
                    hasChanges = any isChanged results
                when hasErrors (exitWith (ExitFailure 1))
                when (checkOnly && hasChanges) (exitWith (ExitFailure 1))


processOneFile :: Bool -> FilePath -> IO ProcessResult
processOneFile checkOnly filePath = do
    result <- processFile checkOnly filePath
    case result of
        Error err -> do
            putStrLn ("Error processing " <> filePath <> ": " <> err)
            pure result
        Changed
            | checkOnly -> do
                putStrLn ("Would reformat: " <> filePath)
                pure result
            | otherwise -> do
                putStrLn ("Reformatted: " <> filePath)
                pure result
        Unchanged -> pure result


isError :: ProcessResult -> Bool
isError (Error _) = True
isError _ = False


isChanged :: ProcessResult -> Bool
isChanged Changed = True
isChanged _ = False


parseArgs :: [String] -> Either String Mode
parseArgs = go Nothing False []
  where
    go mStdin checkOnly files [] =
        case mStdin of
            Just True
                | not (null files) -> Left "Cannot combine --stdin with file arguments."
                | checkOnly -> Left "Cannot combine --stdin with --check."
                | otherwise -> Right ModeStdin
            _ -> Right (ModeFiles checkOnly (reverse files))
    go mStdin _ files ("--check" : rest) =
        go mStdin True files rest
    go _ checkOnly files ("--stdin" : rest) =
        go (Just True) checkOnly files rest
    go _ _ _ (('-' : '-' : opt) : _) =
        Left ("Unknown option: --" <> opt)
    go mStdin checkOnly files (f : rest) =
        go mStdin checkOnly (f : files) rest


usage :: String -> String
usage progName =
    "Usage: "
        <> progName
        <> " [--check] files...\n"
        <> "       "
        <> progName
        <> " --stdin\n\n"
        <> "Options:\n"
        <> "  --check       Don't modify files, exit with code 1 if any file would change\n"
        <> "  --stdin       Read from stdin, write formatted output to stdout\n"