{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Highlight.Common.Monad.Output
  ( Output(..)
  , handleInputData
  , runOutputProducer
  ) where

import Prelude ()
import Prelude.Compat

import Control.Exception (IOException)
import Control.Monad.IO.Class (MonadIO)
import Control.Monad.State (MonadState, StateT, evalStateT, get)
import Control.Monad.Trans.Class (lift)
import Data.ByteString (ByteString)
import Pipes
       (Consumer, Pipe, Producer, Producer', Proxy, (>->), await, each,
        runEffect, yield)
import Pipes.ByteString (stdout)

import Highlight.Common.Monad.Input
       (FilenameHandlingFromFiles, FileOrigin,
        FileReader(FileReaderErr, FileReaderSuccess),
        InputData(InputData), getFileOriginFromFileReader,
        getFilePathFromFileReader)
import Highlight.Pipes (stderrConsumer)
import Highlight.Util
       (convertStringToRawByteString, modify', whenNonNull)

-----------------------------
-- Convert input to Output --
-----------------------------

-- | The current number of the file we are outputting.  This is increased by
-- one for each file.
type ColorNum = Int

-- | The state for which number file we are outputting with its 'FileOrigin'.
data FileColorState
  = FileColorState !(Maybe FileOrigin) {-# UNPACK #-} !ColorNum
  deriving (FileColorState -> FileColorState -> Bool
(FileColorState -> FileColorState -> Bool)
-> (FileColorState -> FileColorState -> Bool) -> Eq FileColorState
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: FileColorState -> FileColorState -> Bool
$c/= :: FileColorState -> FileColorState -> Bool
== :: FileColorState -> FileColorState -> Bool
$c== :: FileColorState -> FileColorState -> Bool
Eq, ReadPrec [FileColorState]
ReadPrec FileColorState
Int -> ReadS FileColorState
ReadS [FileColorState]
(Int -> ReadS FileColorState)
-> ReadS [FileColorState]
-> ReadPrec FileColorState
-> ReadPrec [FileColorState]
-> Read FileColorState
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [FileColorState]
$creadListPrec :: ReadPrec [FileColorState]
readPrec :: ReadPrec FileColorState
$creadPrec :: ReadPrec FileColorState
readList :: ReadS [FileColorState]
$creadList :: ReadS [FileColorState]
readsPrec :: Int -> ReadS FileColorState
$creadsPrec :: Int -> ReadS FileColorState
Read, Int -> FileColorState -> ShowS
[FileColorState] -> ShowS
FileColorState -> String
(Int -> FileColorState -> ShowS)
-> (FileColorState -> String)
-> ([FileColorState] -> ShowS)
-> Show FileColorState
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [FileColorState] -> ShowS
$cshowList :: [FileColorState] -> ShowS
show :: FileColorState -> String
$cshow :: FileColorState -> String
showsPrec :: Int -> FileColorState -> ShowS
$cshowsPrec :: Int -> FileColorState -> ShowS
Show)

-- | Initial value for 'FileColorState'. Defined as the following:
--
-- >>> defFileColorState
-- FileColorState Nothing 0
defFileColorState :: FileColorState
defFileColorState :: FileColorState
defFileColorState = Maybe FileOrigin -> Int -> FileColorState
FileColorState Maybe FileOrigin
forall a. Maybe a
Nothing Int
0

-- | Convert 'InputData' to 'Output'.
handleInputData
  :: forall m.
     MonadIO m
  => (ByteString -> m [ByteString])
  -- ^ Function to use for conversion for a line from stdin.
  -> (FilenameHandlingFromFiles
        -> ByteString
        -> Int
        -> ByteString
        -> m [ByteString]
     )
  -- ^ Function to use for conversion for a line from a normal file.
  -> (ByteString -> IOException -> Maybe IOException -> m [ByteString])
  -- ^ Function to use for conversion for an io error.
  -> InputData m ()
  -- ^ All of the input lines.
  -> Producer Output m ()
handleInputData :: (ByteString -> m [ByteString])
-> (FilenameHandlingFromFiles
    -> ByteString -> Int -> ByteString -> m [ByteString])
-> (ByteString
    -> IOException -> Maybe IOException -> m [ByteString])
-> InputData m ()
-> Producer Output m ()
handleInputData ByteString -> m [ByteString]
stdinF FilenameHandlingFromFiles
-> ByteString -> Int -> ByteString -> m [ByteString]
nonErrF ByteString -> IOException -> Maybe IOException -> m [ByteString]
errF (InputData FilenameHandlingFromFiles
nameHandling Producer (FileReader ByteString) m ()
producer) =
  Producer (FileReader ByteString) m ()
producer Producer (FileReader ByteString) m ()
-> Proxy () (FileReader ByteString) () Output m ()
-> Producer Output m ()
forall (m :: * -> *) a' a b r c' c.
Functor m =>
Proxy a' a () b m r -> Proxy () b c' c m r -> Proxy a' a c' c m r
>-> StateT FileColorState (Pipe (FileReader ByteString) Output m) ()
-> FileColorState
-> Proxy () (FileReader ByteString) () Output m ()
forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m a
evalStateT StateT FileColorState (Pipe (FileReader ByteString) Output m) ()
go FileColorState
defFileColorState
  where
    go :: StateT FileColorState (Pipe (FileReader ByteString) Output m) ()
    go :: StateT FileColorState (Pipe (FileReader ByteString) Output m) ()
go = do
      FileReader ByteString
fileReader <- Proxy
  () (FileReader ByteString) () Output m (FileReader ByteString)
-> StateT
     FileColorState
     (Pipe (FileReader ByteString) Output m)
     (FileReader ByteString)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift Proxy
  () (FileReader ByteString) () Output m (FileReader ByteString)
forall (m :: * -> *) a. Functor m => Consumer' a m a
await
      let maybeFilePath :: Maybe String
maybeFilePath = FileReader ByteString -> Maybe String
forall a. FileReader a -> Maybe String
getFilePathFromFileReader FileReader ByteString
fileReader
          fileOrigin :: FileOrigin
fileOrigin = FileReader ByteString -> FileOrigin
forall a. FileReader a -> FileOrigin
getFileOriginFromFileReader FileReader ByteString
fileReader
      Int
colorNumIfNewFile <- FileOrigin
-> StateT
     FileColorState (Pipe (FileReader ByteString) Output m) Int
forall (m :: * -> *).
MonadState FileColorState m =>
FileOrigin -> m Int
getColorNumIfNewFileM FileOrigin
fileOrigin
      case Maybe String
maybeFilePath of
        -- The current @'FileReader' 'ByteString'@ is from stdin.
        Maybe String
Nothing ->
          case FileReader ByteString
fileReader of
            FileReaderSuccess FileOrigin
_ ByteString
line -> do
              [ByteString]
outByteStrings <- Proxy () (FileReader ByteString) () Output m [ByteString]
-> StateT
     FileColorState (Pipe (FileReader ByteString) Output m) [ByteString]
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (Proxy () (FileReader ByteString) () Output m [ByteString]
 -> StateT
      FileColorState
      (Pipe (FileReader ByteString) Output m)
      [ByteString])
-> (m [ByteString]
    -> Proxy () (FileReader ByteString) () Output m [ByteString])
-> m [ByteString]
-> StateT
     FileColorState (Pipe (FileReader ByteString) Output m) [ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. m [ByteString]
-> Proxy () (FileReader ByteString) () Output m [ByteString]
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m [ByteString]
 -> StateT
      FileColorState
      (Pipe (FileReader ByteString) Output m)
      [ByteString])
-> m [ByteString]
-> StateT
     FileColorState (Pipe (FileReader ByteString) Output m) [ByteString]
forall a b. (a -> b) -> a -> b
$ ByteString -> m [ByteString]
stdinF ByteString
line
              [ByteString]
-> FileOrigin
-> StateT FileColorState (Pipe (FileReader ByteString) Output m) ()
forall (m :: * -> *) x' x.
Monad m =>
[ByteString]
-> FileOrigin -> StateT FileColorState (Proxy x' x () Output m) ()
toStdoutWhenNonNull [ByteString]
outByteStrings FileOrigin
fileOrigin
            FileReaderErr FileOrigin
_ IOException
_ Maybe IOException
_ -> ()
-> StateT FileColorState (Pipe (FileReader ByteString) Output m) ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
        -- The current @'FileReader' 'ByteString'@ is from a normal file.
        Just String
filePath -> do
          ByteString
byteStringFilePath <- String
-> StateT
     FileColorState (Pipe (FileReader ByteString) Output m) ByteString
forall (m :: * -> *). MonadIO m => String -> m ByteString
convertStringToRawByteString String
filePath
          case FileReader ByteString
fileReader of
            FileReaderErr FileOrigin
_ IOException
ioerr Maybe IOException
maybeioerr -> do
              [ByteString]
outByteStrings <-
                Proxy () (FileReader ByteString) () Output m [ByteString]
-> StateT
     FileColorState (Pipe (FileReader ByteString) Output m) [ByteString]
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (Proxy () (FileReader ByteString) () Output m [ByteString]
 -> StateT
      FileColorState
      (Pipe (FileReader ByteString) Output m)
      [ByteString])
-> (m [ByteString]
    -> Proxy () (FileReader ByteString) () Output m [ByteString])
-> m [ByteString]
-> StateT
     FileColorState (Pipe (FileReader ByteString) Output m) [ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. m [ByteString]
-> Proxy () (FileReader ByteString) () Output m [ByteString]
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m [ByteString]
 -> StateT
      FileColorState
      (Pipe (FileReader ByteString) Output m)
      [ByteString])
-> m [ByteString]
-> StateT
     FileColorState (Pipe (FileReader ByteString) Output m) [ByteString]
forall a b. (a -> b) -> a -> b
$ ByteString -> IOException -> Maybe IOException -> m [ByteString]
errF ByteString
byteStringFilePath IOException
ioerr Maybe IOException
maybeioerr
              [ByteString]
-> StateT FileColorState (Pipe (FileReader ByteString) Output m) ()
forall (m :: * -> *) s x' x.
Monad m =>
[ByteString] -> StateT s (Proxy x' x () Output m) ()
toStderrWhenNonNull [ByteString]
outByteStrings
            FileReaderSuccess FileOrigin
_ ByteString
inputLine -> do
              [ByteString]
outByteStrings <-
                Proxy () (FileReader ByteString) () Output m [ByteString]
-> StateT
     FileColorState (Pipe (FileReader ByteString) Output m) [ByteString]
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (Proxy () (FileReader ByteString) () Output m [ByteString]
 -> StateT
      FileColorState
      (Pipe (FileReader ByteString) Output m)
      [ByteString])
-> (m [ByteString]
    -> Proxy () (FileReader ByteString) () Output m [ByteString])
-> m [ByteString]
-> StateT
     FileColorState (Pipe (FileReader ByteString) Output m) [ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. m [ByteString]
-> Proxy () (FileReader ByteString) () Output m [ByteString]
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m [ByteString]
 -> StateT
      FileColorState
      (Pipe (FileReader ByteString) Output m)
      [ByteString])
-> m [ByteString]
-> StateT
     FileColorState (Pipe (FileReader ByteString) Output m) [ByteString]
forall a b. (a -> b) -> a -> b
$
                  FilenameHandlingFromFiles
-> ByteString -> Int -> ByteString -> m [ByteString]
nonErrF
                    FilenameHandlingFromFiles
nameHandling
                    ByteString
byteStringFilePath
                    Int
colorNumIfNewFile
                    ByteString
inputLine
              [ByteString]
-> FileOrigin
-> StateT FileColorState (Pipe (FileReader ByteString) Output m) ()
forall (m :: * -> *) x' x.
Monad m =>
[ByteString]
-> FileOrigin -> StateT FileColorState (Proxy x' x () Output m) ()
toStdoutWhenNonNull [ByteString]
outByteStrings FileOrigin
fileOrigin
      StateT FileColorState (Pipe (FileReader ByteString) Output m) ()
go

-- | When the list of 'ByteString' is not @[]@, convert them to 'OutputStdout'
-- and return them in a 'Producer'.  Call 'updateColorNum' with the passed-in
-- 'FileOrigin'.
toStdoutWhenNonNull
  :: forall m x' x.
     Monad m
  => [ByteString]
  -> FileOrigin
  -> StateT FileColorState (Proxy x' x () Output m) ()
toStdoutWhenNonNull :: [ByteString]
-> FileOrigin -> StateT FileColorState (Proxy x' x () Output m) ()
toStdoutWhenNonNull [ByteString]
outByteStrings FileOrigin
fileOrigin =
  [ByteString]
-> StateT FileColorState (Proxy x' x () Output m) ()
-> StateT FileColorState (Proxy x' x () Output m) ()
forall (m :: * -> *) a. Monad m => [a] -> m () -> m ()
whenNonNull [ByteString]
outByteStrings (StateT FileColorState (Proxy x' x () Output m) ()
 -> StateT FileColorState (Proxy x' x () Output m) ())
-> StateT FileColorState (Proxy x' x () Output m) ()
-> StateT FileColorState (Proxy x' x () Output m) ()
forall a b. (a -> b) -> a -> b
$ do
    Proxy x' x () Output m ()
-> StateT FileColorState (Proxy x' x () Output m) ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (Proxy x' x () Output m ()
 -> StateT FileColorState (Proxy x' x () Output m) ())
-> Proxy x' x () Output m ()
-> StateT FileColorState (Proxy x' x () Output m) ()
forall a b. (a -> b) -> a -> b
$ [ByteString] -> Producer' Output m ()
forall (m :: * -> *).
Monad m =>
[ByteString] -> Producer' Output m ()
toStdoutWithNewline [ByteString]
outByteStrings
    FileOrigin -> StateT FileColorState (Proxy x' x () Output m) ()
forall (m :: * -> *).
MonadState FileColorState m =>
FileOrigin -> m ()
updateColorNumM FileOrigin
fileOrigin

-- | When the list of 'ByteString' is not @[]@, convert them to 'OutputStderr'
-- and return them in a 'Producer'.  Do not call 'updateColorNum'.
toStderrWhenNonNull
  :: forall m s x' x.
     Monad m
  => [ByteString] -> StateT s (Proxy x' x () Output m) ()
toStderrWhenNonNull :: [ByteString] -> StateT s (Proxy x' x () Output m) ()
toStderrWhenNonNull [ByteString]
outByteStrings =
  [ByteString]
-> StateT s (Proxy x' x () Output m) ()
-> StateT s (Proxy x' x () Output m) ()
forall (m :: * -> *) a. Monad m => [a] -> m () -> m ()
whenNonNull [ByteString]
outByteStrings (StateT s (Proxy x' x () Output m) ()
 -> StateT s (Proxy x' x () Output m) ())
-> (Proxy x' x () Output m ()
    -> StateT s (Proxy x' x () Output m) ())
-> Proxy x' x () Output m ()
-> StateT s (Proxy x' x () Output m) ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Proxy x' x () Output m () -> StateT s (Proxy x' x () Output m) ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (Proxy x' x () Output m () -> StateT s (Proxy x' x () Output m) ())
-> Proxy x' x () Output m ()
-> StateT s (Proxy x' x () Output m) ()
forall a b. (a -> b) -> a -> b
$ [ByteString] -> Producer' Output m ()
forall (m :: * -> *).
Monad m =>
[ByteString] -> Producer' Output m ()
toStderrWithNewline [ByteString]
outByteStrings

-- | Call 'updateColorNum' with the current value of 'FileColorState'.
updateColorNumM
  :: MonadState FileColorState m => FileOrigin -> m ()
updateColorNumM :: FileOrigin -> m ()
updateColorNumM = (FileColorState -> FileColorState) -> m ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify' ((FileColorState -> FileColorState) -> m ())
-> (FileOrigin -> FileColorState -> FileColorState)
-> FileOrigin
-> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FileOrigin -> FileColorState -> FileColorState
updateColorNum

-- | Based on the value of the passed in 'FileOrigin' and the 'FileOrigin' in
-- 'FileColorState', update the 'ColorNum' in 'FileColorState'.
--
-- Setup for examples:
--
-- >>> import Highlight.Common.Monad.Input (FileOrigin(FileSpecifiedByUser))
--
-- If 'defFileColorState' is passed in, then update the 'FileOrigin'.
--
-- >>> let fileOrigin = FileSpecifiedByUser "hello.txt"
-- >>> updateColorNum fileOrigin defFileColorState
-- FileColorState (Just (FileSpecifiedByUser "hello.txt")) 0
--
-- If the 'FileOrigin' is different from the one in 'FileColorState', then
-- increment the 'ColorNum'.
--
-- >>> let oldFileOrigin = FileSpecifiedByUser "hello.txt"
-- >>> let newFileOrigin = FileSpecifiedByUser "bye.txt"
-- >>> let fileColorStateDiff = FileColorState (Just oldFileOrigin) 5
-- >>> updateColorNum newFileOrigin fileColorStateDiff
-- FileColorState (Just (FileSpecifiedByUser "bye.txt")) 6
--
-- If the 'FileOrigin' is the same as the one in 'FileColorState', then do not
-- increment the 'ColorNum'.
--
-- >>> let sameFileOrigin = FileSpecifiedByUser "hello.txt"
-- >>> let fileColorStateDiff = FileColorState (Just sameFileOrigin) 5
-- >>> updateColorNum sameFileOrigin fileColorStateDiff
-- FileColorState (Just (FileSpecifiedByUser "hello.txt")) 5
updateColorNum
  :: FileOrigin
  -> FileColorState
  -> FileColorState
updateColorNum :: FileOrigin -> FileColorState -> FileColorState
updateColorNum FileOrigin
newFileOrigin (FileColorState Maybe FileOrigin
Nothing Int
colorNum) =
  Maybe FileOrigin -> Int -> FileColorState
FileColorState (FileOrigin -> Maybe FileOrigin
forall a. a -> Maybe a
Just FileOrigin
newFileOrigin) Int
colorNum
updateColorNum FileOrigin
newFileOrigin (FileColorState (Just FileOrigin
prevFileOrigin) Int
colorNum)
  | FileOrigin
prevFileOrigin FileOrigin -> FileOrigin -> Bool
forall a. Eq a => a -> a -> Bool
== FileOrigin
newFileOrigin =
    Maybe FileOrigin -> Int -> FileColorState
FileColorState (FileOrigin -> Maybe FileOrigin
forall a. a -> Maybe a
Just FileOrigin
newFileOrigin) Int
colorNum
  | Bool
otherwise = Maybe FileOrigin -> Int -> FileColorState
FileColorState (FileOrigin -> Maybe FileOrigin
forall a. a -> Maybe a
Just FileOrigin
newFileOrigin) (Int
colorNum Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)

-- | This called 'updateColorNum' and returns the new value of 'ColorNum', but
-- it doesn't update the 'FileColorNum' in 'MonadState'.
getColorNumIfNewFileM
  :: MonadState FileColorState m
  => FileOrigin -> m ColorNum
getColorNumIfNewFileM :: FileOrigin -> m Int
getColorNumIfNewFileM FileOrigin
newFileOrigin = do
  FileColorState
fileColorState <- m FileColorState
forall s (m :: * -> *). MonadState s m => m s
get
  let (FileColorState Maybe FileOrigin
_ Int
colorNum) = FileOrigin -> FileColorState -> FileColorState
updateColorNum FileOrigin
newFileOrigin FileColorState
fileColorState
  Int -> m Int
forall (m :: * -> *) a. Monad m => a -> m a
return Int
colorNum

-- | This is just 'toOutputWithNewLine' 'OutputStderr'.
toStderrWithNewline :: Monad m => [ByteString] -> Producer' Output m ()
toStderrWithNewline :: [ByteString] -> Producer' Output m ()
toStderrWithNewline = (ByteString -> Output) -> [ByteString] -> Producer' Output m ()
forall (m :: * -> *).
Monad m =>
(ByteString -> Output) -> [ByteString] -> Producer' Output m ()
toOutputWithNewline ByteString -> Output
OutputStderr

-- | This is just 'toOutputWithNewLine' 'OutputStdout'.
toStdoutWithNewline :: Monad m => [ByteString] -> Producer' Output m ()
toStdoutWithNewline :: [ByteString] -> Producer' Output m ()
toStdoutWithNewline = (ByteString -> Output) -> [ByteString] -> Producer' Output m ()
forall (m :: * -> *).
Monad m =>
(ByteString -> Output) -> [ByteString] -> Producer' Output m ()
toOutputWithNewline ByteString -> Output
OutputStdout

-- | Convert a list of 'ByteString' to 'Output' with the given function.
--
-- Setup for examples:
--
-- >>> :set -XOverloadedStrings
-- >>> import Pipes.Prelude (toListM)
--
-- If the list of 'ByteString' is empty, then do nothing.
--
-- >>> toListM $ toOutputWithNewline OutputStdout []
-- []
--
-- If the list of 'ByteString' is not empty, then convert to 'Output' and add
-- an ending newline.
--
-- >>> toListM $ toOutputWithNewline OutputStderr ["hello", "bye"]
-- [OutputStderr "hello",OutputStderr "bye",OutputStderr "\n"]
toOutputWithNewline
  :: Monad m
  => (ByteString -> Output)
  -- ^ Function to use to convert the 'ByteString' to 'Output'.
  -> [ByteString]
  -- ^ List of 'ByteString's to convert to 'Output'.
  -> Producer' Output m ()
toOutputWithNewline :: (ByteString -> Output) -> [ByteString] -> Producer' Output m ()
toOutputWithNewline ByteString -> Output
_ [] = () -> Proxy x' x () Output m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
toOutputWithNewline ByteString -> Output
byteStringToOutput [ByteString]
byteStrings = do
  [Output] -> Proxy x' x () Output m ()
forall (m :: * -> *) (f :: * -> *) a x' x.
(Functor m, Foldable f) =>
f a -> Proxy x' x () a m ()
each ([Output] -> Proxy x' x () Output m ())
-> [Output] -> Proxy x' x () Output m ()
forall a b. (a -> b) -> a -> b
$ (ByteString -> Output) -> [ByteString] -> [Output]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Output
byteStringToOutput [ByteString]
byteStrings
  Output -> Proxy x' x () Output m ()
forall (m :: * -> *) a x' x. Functor m => a -> Proxy x' x () a m ()
yield (Output -> Proxy x' x () Output m ())
-> Output -> Proxy x' x () Output m ()
forall a b. (a -> b) -> a -> b
$ ByteString -> Output
byteStringToOutput ByteString
"\n"

------------
-- Output --
------------

-- | Sum-type to represent where a given 'ByteString' should be output, whether
-- it is stdout or stderr.
data Output
  = OutputStdout !ByteString
  | OutputStderr !ByteString
  deriving (Output -> Output -> Bool
(Output -> Output -> Bool)
-> (Output -> Output -> Bool) -> Eq Output
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Output -> Output -> Bool
$c/= :: Output -> Output -> Bool
== :: Output -> Output -> Bool
$c== :: Output -> Output -> Bool
Eq, ReadPrec [Output]
ReadPrec Output
Int -> ReadS Output
ReadS [Output]
(Int -> ReadS Output)
-> ReadS [Output]
-> ReadPrec Output
-> ReadPrec [Output]
-> Read Output
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Output]
$creadListPrec :: ReadPrec [Output]
readPrec :: ReadPrec Output
$creadPrec :: ReadPrec Output
readList :: ReadS [Output]
$creadList :: ReadS [Output]
readsPrec :: Int -> ReadS Output
$creadsPrec :: Int -> ReadS Output
Read, Int -> Output -> ShowS
[Output] -> ShowS
Output -> String
(Int -> Output -> ShowS)
-> (Output -> String) -> ([Output] -> ShowS) -> Show Output
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Output] -> ShowS
$cshowList :: [Output] -> ShowS
show :: Output -> String
$cshow :: Output -> String
showsPrec :: Int -> Output -> ShowS
$cshowsPrec :: Int -> Output -> ShowS
Show)

-- | Write each 'Output' to either 'stdout' or 'stderrConsumer'.
--
-- Setup for tests:
--
-- >>> import Pipes (runEffect)
--
-- Send 'OutputStdout' to 'stdout'.
--
-- >>> runEffect $ yield (OutputStdout "this goes to stdout") >-> outputConsumer
-- this goes to stdout
--
-- Send 'OutputStderr' to 'stderrConsumer'.
--
-- >>> runEffect $ yield (OutputStderr "this goes to stderr") >-> outputConsumer
-- this goes to stderr
outputConsumer :: MonadIO m => Consumer Output m ()
outputConsumer :: Consumer Output m ()
outputConsumer = do
  Output
output <- Proxy () Output () X m Output
forall (m :: * -> *) a. Functor m => Consumer' a m a
await
  case Output
output of
    OutputStdout ByteString
byteString ->
      ByteString -> Proxy () Output () ByteString m ()
forall (m :: * -> *) a x' x. Functor m => a -> Proxy x' x () a m ()
yield ByteString
byteString Proxy () Output () ByteString m ()
-> Proxy () ByteString () X m () -> Consumer Output m ()
forall (m :: * -> *) a' a b r c' c.
Functor m =>
Proxy a' a () b m r -> Proxy () b c' c m r -> Proxy a' a c' c m r
>-> Proxy () ByteString () X m ()
forall (m :: * -> *). MonadIO m => Consumer' ByteString m ()
stdout
    OutputStderr ByteString
byteString ->
      ByteString -> Proxy () Output () ByteString m ()
forall (m :: * -> *) a x' x. Functor m => a -> Proxy x' x () a m ()
yield ByteString
byteString Proxy () Output () ByteString m ()
-> Proxy () ByteString () X m () -> Consumer Output m ()
forall (m :: * -> *) a' a b r c' c.
Functor m =>
Proxy a' a () b m r -> Proxy () b c' c m r -> Proxy a' a c' c m r
>-> Proxy () ByteString () X m ()
forall (m :: * -> *). MonadIO m => Consumer' ByteString m ()
stderrConsumer
  Consumer Output m ()
forall (m :: * -> *). MonadIO m => Consumer Output m ()
outputConsumer

-- | Run a 'Producer' 'Output' by connecting it to 'outputConsumer'.
runOutputProducer :: forall m. MonadIO m => Producer Output m () -> m ()
runOutputProducer :: Producer Output m () -> m ()
runOutputProducer Producer Output m ()
producer = Effect m () -> m ()
forall (m :: * -> *) r. Monad m => Effect m r -> m r
runEffect (Effect m () -> m ()) -> Effect m () -> m ()
forall a b. (a -> b) -> a -> b
$ Producer Output m ()
producer Producer Output m () -> Proxy () Output () X m () -> Effect m ()
forall (m :: * -> *) a' a b r c' c.
Functor m =>
Proxy a' a () b m r -> Proxy () b c' c m r -> Proxy a' a c' c m r
>-> Proxy () Output () X m ()
forall (m :: * -> *). MonadIO m => Consumer Output m ()
outputConsumer