packages feed

hs-bindgen-1.0.0.0: src-internal/HsBindgen/Util/Process.hs

module HsBindgen.Util.Process (
    -- * Read actions
    cmdSpecReadAction
  , cmdSpecPrettyForTrace
  , checkOutput
  , parseSingleLine
  , parseNonEmpty
  ) where

import System.IO.Error (tryIOError)
import System.Process qualified as Proc
import Text.SimplePrettyPrint qualified as PP

import HsBindgen.Util.Tracer

{-------------------------------------------------------------------------------
  Read actions
-------------------------------------------------------------------------------}

-- | Interpret a 'Proc.CmdSpec' as a read action
cmdSpecReadAction :: Proc.CmdSpec -> IO String
cmdSpecReadAction = \case
    Proc.ShellCommand cmd    -> Proc.readCreateProcess (Proc.shell cmd) ""
    Proc.RawCommand exe args -> Proc.readProcess exe args ""

-- | Render a 'Proc.CmdSpec' for tracing
cmdSpecPrettyForTrace :: Proc.CmdSpec -> PP.CtxDoc
cmdSpecPrettyForTrace = PP.string . \case
    Proc.ShellCommand cmd    -> cmd
    Proc.RawCommand exe args -> unwords (exe : args)

-- | Run a read action and check the output
checkOutput ::
     Tracer msg
  -> (String  -> msg)      -- ^ Unexpected output constructor
  -> (IOError -> msg)      -- ^ Error constructor
  -> (String  -> Maybe a)  -- ^ Output parser
  -> IO String             -- ^ Read action
  -> IO (Maybe a)
checkOutput tracer mkUnexpected mkError parse action =
    tryIOError action >>= \case
      Right s -> case parse s of
        x@Just{} -> return x
        Nothing  -> Nothing <$ traceWith tracer (withCallStack $ mkUnexpected (abbr s))
      Left  e -> Nothing <$ traceWith tracer (withCallStack $ mkError e)
  where
    -- Abbreviate arbitrarily long strings in trace messages
    abbr :: String -> String
    abbr s = case splitAt 60 s of
      (_, []) -> s
      (s', _) -> s' ++ " ..."

-- | Parse a single line of output
parseSingleLine :: String -> Maybe String
parseSingleLine s = case lines s of
    [s'] -> Just s'
    _    -> Nothing

-- | Parse non-empty output
parseNonEmpty :: String -> Maybe String
parseNonEmpty = \case
    "" -> Nothing
    s  -> Just s