applyTransformation ::
    World m =>
    InfoFormatter.Loggable info =>
    InfoFormatter.ToConsole prompt =>
    (FilePath -> info)
    -> Bool
    -> ([FilePath] -> prompt)
    -> ((FilePath, Text) -> Either info Text)
    -> TransformMode
    -> m Bool
applyTransformation processingFile autoYes confirmPrompt transform mode =
    let
        usesStdout =
            case mode of
                StdinToStdout -> True
                StdinToFile _ -> True
                FileToStdout _ -> True
                FileToFile _ _ -> False
                FilesInPlace _ _ -> False

        infoMode = ForHuman usesStdout

        onInfo = InfoFormatter.onInfo infoMode

        approve = InfoFormatter.approve infoMode autoYes . confirmPrompt
    in
    runState (InfoFormatter.init infoMode) (InfoFormatter.done infoMode) $
    case mode of
        StdinToStdout ->
            lift (transform <$> readStdin) >>= logErrorOr onInfo (lift . World.writeStdout)

        StdinToFile outputFile ->
            lift (transform <$> readStdin) >>= logErrorOr onInfo (lift . World.writeUtf8File outputFile)

        FileToStdout inputFile ->
            lift (transform <$> World.readUtf8FileWithPath inputFile) >>= logErrorOr onInfo (lift . World.writeStdout)

        FileToFile inputFile outputFile ->
            (transform <$> readFromFile (onInfo . processingFile) inputFile) >>= logErrorOr onInfo (lift . World.writeUtf8File outputFile)

        FilesInPlace first rest ->
            do
                canOverwrite <- lift $ approve (first:rest)
                if canOverwrite
                    then all id <$> mapM formatFile (first:rest)
                    else return True
            where
                formatFile file = ((\i -> checkChange i <$> transform i) <$> readFromFile (onInfo . processingFile) file) >>= logErrorOr onInfo (lift . updateFile)
applyTransformation ::
    World m =>
    InfoFormatter.Loggable info =>
    InfoFormatter.ToConsole prompt =>
    (FilePath -> info)
    -> Bool
    -> ([FilePath] -> prompt)
    -> ((FilePath, Text) -> Either info Text)
    -> TransformMode
    -> m Bool
applyTransformation processingFile autoYes confirmPrompt transform mode =
    let
        usesStdout =
            case mode of
                StdinToStdout -> True
                StdinToFile _ -> True
                FileToStdout _ -> True
                FileToFile _ _ -> False
                FilesInPlace _ _ -> False

        infoMode = ForHuman usesStdout

        onInfo = InfoFormatter.onInfo infoMode

        approve = InfoFormatter.approve infoMode autoYes . confirmPrompt
    in
    runState (InfoFormatter.init infoMode) (InfoFormatter.done infoMode) $
    case mode of
        StdinToStdout ->
            lift (transform <$> readStdin) >>= logErrorOr onInfo (lift . World.writeStdout)

        StdinToFile outputFile ->
            lift (transform <$> readStdin) >>= logErrorOr onInfo (lift . World.writeUtf8File outputFile)

        FileToStdout inputFile ->
            lift (transform <$> World.readUtf8FileWithPath inputFile) >>= logErrorOr onInfo (lift . World.writeStdout)

        FileToFile inputFile outputFile ->
            (transform <$> readFromFile (onInfo . processingFile) inputFile) >>= logErrorOr onInfo (lift . World.writeUtf8File outputFile)

        FilesInPlace first rest ->
            do
                canOverwrite <- lift $ approve (first:rest)
                if canOverwrite
                    then all id <$> mapM formatFile (first:rest)
                    else return True
            where
                formatFile file = ((\i -> checkChange i <$> transform i) <$> readFromFile (onInfo . processingFile) file) >>= logErrorOr onInfo (lift . updateFile)
applyTransformation processingFile autoYes confirmPrompt transform mode =
    let
        usesStdout =
            case mode of
                StdinToStdout -> True
                StdinToFile _ -> True
                FileToStdout _ -> True
                FileToFile _ _ -> False
                FilesInPlace _ _ -> False

        infoMode = ForHuman usesStdout

        onInfo = InfoFormatter.onInfo infoMode

        approve = InfoFormatter.approve infoMode autoYes . confirmPrompt
    in
    runState (InfoFormatter.init infoMode) (InfoFormatter.done infoMode) $
    case mode of
        StdinToStdout ->
            lift (transform <$> readStdin) >>= logErrorOr onInfo (lift . World.writeStdout)

        StdinToFile outputFile ->
            lift (transform <$> readStdin) >>= logErrorOr onInfo (lift . World.writeUtf8File outputFile)

        FileToStdout inputFile ->
            lift (transform <$> World.readUtf8FileWithPath inputFile) >>= logErrorOr onInfo (lift . World.writeStdout)

        FileToFile inputFile outputFile ->
            (transform <$> readFromFile (onInfo . processingFile) inputFile) >>= logErrorOr onInfo (lift . World.writeUtf8File outputFile)

        FilesInPlace first rest ->
            do
                canOverwrite <- lift $ approve (first:rest)
                if canOverwrite
                    then all id <$> mapM formatFile (first:rest)
                    else return True
            where
                formatFile file = ((\i -> checkChange i <$> transform i) <$> readFromFile (onInfo . processingFile) file) >>= logErrorOr onInfo (lift . updateFile)