-
Notifications
You must be signed in to change notification settings - Fork 91
Description
I wish to "fold" over the lines of a shell command's output, and then make a decision about how to continue based on both the result of the fold AND the exit status.
For example I may be looking for some particular text in the output AND a particular non-zero exit status at the same time. And whilst I'm searching for the text I may want to echo all output (or perhaps some or none) back to the user.
But the issue is the function, inprocWithErr, which is close to what I want, doesn't return the ExitCode, but instead throws it if it is non-zero.
Problem is if it throws, I can catch it but then the result of the fold is gone.
I've hacked up the following, using IORef to hold the fold accumulator so it's not lost on catching an exception, but I feel like it's hacky and I don't like it:
foldInprocWithErr :: MonadIO io => (Either Line Line -> a -> IO a) -> a -> Text -> [Text] -> Shell Line -> io (ExitCode, a)
foldInprocWithErr step initial cmd args stdinLines = liftIO $ do
accRef <- newIORef initial
let shellAction = do
eLine <- T.inprocWithErr cmd args stdinLines
acc0 <- liftIO (readIORef accRef)
acc1 <- liftIO (step eLine acc0)
liftIO (writeIORef accRef acc1)
result <- try (T.sh shellAction) :: IO (Either ExitCode ())
acc <- readIORef accRef
let exitCode = either id (const ExitSuccess) result
pure (exitCode, acc)
Is there a better way to approach what I've done above? I considered functions like Turtle.Shell.foldIO but I can't see how that helps because on anything non-zero exit status I'm still going to have to catch an exception, and then I've lost the result, unless I've stashed it away whilst processing in an IORef.