never executed always true always false
    1 module CommandLine.TransformFiles
    2     ( Result
    3     , TransformMode(..), applyTransformation
    4     , ValidateMode(..), validateNoChanges
    5     ) where
    6 
    7 -- This module provides reusable functions for command line tools that
    8 -- transform files.
    9 
   10 import CommandLine.InfoFormatter (ExecuteMode(..))
   11 import qualified CommandLine.InfoFormatter as InfoFormatter
   12 import CommandLine.World (World)
   13 import qualified CommandLine.World as World
   14 import Control.Monad.State hiding (runState)
   15 import Data.Text (Text)
   16 
   17 
   18 
   19 data Result a
   20     = NoChange FilePath a
   21     | Changed FilePath a
   22 
   23 
   24 checkChange :: Eq a => (FilePath, a) -> a -> Result a
   25 checkChange (inputFile, inputText) outputText =
   26     if inputText == outputText
   27         then NoChange inputFile outputText
   28         else Changed inputFile outputText
   29 
   30 
   31 updateFile :: World m => Result Text -> m ()
   32 updateFile result =
   33     case result of
   34         NoChange _ _ -> return ()
   35         Changed outputFile outputText -> World.writeUtf8File outputFile outputText
   36 
   37 
   38 readStdin :: World m => m (FilePath, Text)
   39 readStdin =
   40     (,) "<STDIN>" <$> World.getStdin
   41 
   42 
   43 readFromFile :: World m => (FilePath -> StateT s m ()) -> FilePath -> StateT s m (FilePath, Text)
   44 readFromFile onProcessingFile filePath =
   45     onProcessingFile filePath
   46         *> lift (World.readUtf8FileWithPath filePath)
   47 
   48 
   49 data TransformMode
   50     = StdinToStdout
   51     | StdinToFile FilePath
   52     | FileToStdout FilePath
   53     | FileToFile FilePath FilePath
   54     | FilesInPlace FilePath [FilePath]
   55 
   56 
   57 applyTransformation ::
   58     World m =>
   59     InfoFormatter.Loggable info =>
   60     InfoFormatter.ToConsole prompt =>
   61     (FilePath -> info)
   62     -> Bool
   63     -> ([FilePath] -> prompt)
   64     -> ((FilePath, Text) -> Either info Text)
   65     -> TransformMode
   66     -> m Bool
   67 applyTransformation processingFile autoYes confirmPrompt transform mode =
   68     let
   69         usesStdout =
   70             case mode of
   71                 StdinToStdout -> True
   72                 StdinToFile _ -> True
   73                 FileToStdout _ -> True
   74                 FileToFile _ _ -> False
   75                 FilesInPlace _ _ -> False
   76 
   77         infoMode = ForHuman usesStdout
   78 
   79         onInfo = InfoFormatter.onInfo infoMode
   80 
   81         approve = InfoFormatter.approve infoMode autoYes . confirmPrompt
   82     in
   83     runState (InfoFormatter.init infoMode) (InfoFormatter.done infoMode) $
   84     case mode of
   85         StdinToStdout ->
   86             lift (transform <$> readStdin) >>= logErrorOr onInfo (lift . World.writeStdout)
   87 
   88         StdinToFile outputFile ->
   89             lift (transform <$> readStdin) >>= logErrorOr onInfo (lift . World.writeUtf8File outputFile)
   90 
   91         FileToStdout inputFile ->
   92             lift (transform <$> World.readUtf8FileWithPath inputFile) >>= logErrorOr onInfo (lift . World.writeStdout)
   93 
   94         FileToFile inputFile outputFile ->
   95             (transform <$> readFromFile (onInfo . processingFile) inputFile) >>= logErrorOr onInfo (lift . World.writeUtf8File outputFile)
   96 
   97         FilesInPlace first rest ->
   98             do
   99                 canOverwrite <- lift $ approve (first:rest)
  100                 if canOverwrite
  101                     then all id <$> mapM formatFile (first:rest)
  102                     else return True
  103             where
  104                 formatFile file = ((\i -> checkChange i <$> transform i) <$> readFromFile (onInfo . processingFile) file) >>= logErrorOr onInfo (lift . updateFile)
  105 
  106 
  107 data ValidateMode
  108     = ValidateStdin
  109     | ValidateFiles FilePath [FilePath]
  110 
  111 
  112 validateNoChanges ::
  113     World m =>
  114     InfoFormatter.Loggable info =>
  115     (FilePath -> info)
  116     -> ((FilePath, Text) -> Either info ())
  117     -> ValidateMode
  118     -> m Bool
  119 validateNoChanges processingFile validate mode =
  120     let
  121         infoMode = ForMachine
  122         onInfo = InfoFormatter.onInfo infoMode
  123     in
  124     runState (InfoFormatter.init infoMode) (InfoFormatter.done infoMode) $
  125     case mode of
  126         ValidateStdin ->
  127             lift (validate <$> readStdin) >>= logError onInfo
  128 
  129         ValidateFiles first rest ->
  130             and <$> mapM validateFile (first:rest)
  131             where
  132                 validateFile file =
  133                     (validate <$> readFromFile (onInfo . processingFile) file)
  134                         >>= logError onInfo
  135 
  136 
  137 logErrorOr :: Monad m => (error -> m ()) -> (a -> m ()) -> Either error a -> m Bool
  138 logErrorOr onInfo fn result =
  139     case result of
  140         Left message ->
  141             onInfo message *> return False
  142 
  143         Right value ->
  144             fn value *> return True
  145 
  146 logError :: Monad m => (error -> m ()) -> Either error () -> m Bool
  147 logError onInfo =
  148     logErrorOr onInfo return
  149 
  150 
  151 
  152 runState :: Monad m => (m (), state) -> (state -> m ()) -> StateT state m result -> m result
  153 runState (initM, initialState) done run =
  154     do
  155         initM
  156         (result, finalState) <- runStateT run initialState
  157         done finalState
  158         return result