-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Use OS processes as stream transformation functions -- -- Run operating system processes as stream source, sink or -- transformation functions. Use them seamlessly in a streaming data -- pipeline in the same way as any other Haskell functions. @package streamly-process @version 0.1.0 module Streamly.Internal.System.Process -- | Process configuration used for creating a new process. -- -- By default the process config is setup to inherit the following -- attributes from the parent process: -- -- data Config -- | An exception that is raised when a process fails. newtype ProcessFailure -- | The exit code of the process. ProcessFailure :: Int -> ProcessFailure -- | See processBytes. toBytes is defined as: -- --
--   >>> toBytes path args = processBytes path args Stream.nil
--   
-- -- The following code is equivalent to the shell command echo "hello -- world": -- --
--   >>> :{
--      Process.toBytes "echo" ["hello world"]
--    & Stream.fold Stdio.write
--    :}
--   hello world
--   
toBytes :: (IsStream t, MonadAsync m, MonadCatch m) => FilePath -> [String] -> t m Word8 -- | toBytes' path args runs the executable at path using -- args as arguments and returns a stream of Either -- bytes. The Left values are from stderr and the -- Right values are from stdout of the executable. -- -- Raises ProcessFailure exception in case of failure. -- -- The following example uses echo to write hello to -- stdout and world to stderr, then uses folds -- from Streamly.Console.Stdio to write them back to -- stdout and stderr respectively: -- --
--   >>> :{
--     Process.toBytes' "/bin/bash" ["-c", "echo 'hello'; echo 'world' 1>&2"]
--   & Stream.fold (Fold.partition Stdio.writeErr Stdio.write)
--   :}
--   world
--   hello
--   ((),())
--   
-- --
--   >>> toBytes' path args = Process.processBytes' path args Stream.nil
--   
toBytes' :: (IsStream t, MonadAsync m, MonadCatch m) => FilePath -> [String] -> t m (Either Word8 Word8) -- | See processChunks. toChunks is defined as: -- --
--   >>> toChunks path args = processChunks path args Stream.nil
--   
-- -- The following code is equivalent to the shell command echo "hello -- world": -- --
--   >>> :{
--      Process.toChunks "echo" ["hello world"]
--    & Stream.fold Stdio.writeChunks
--    :}
--   hello world
--   
toChunks :: (IsStream t, MonadAsync m, MonadCatch m) => FilePath -> [String] -> t m (Array Word8) -- | Like toBytes but generates a stream of Array Word8 -- instead of a stream of Word8. -- --
--   >>> :{
--     toChunks' "bash" ["-c", "echo 'hello'; echo 'world' 1>&2"]
--   & Stream.fold (Fold.partition Stdio.writeErrChunks Stdio.writeChunks)
--   :}
--   world
--   hello
--   ((),())
--   
-- --
--   >>> toChunks' path args = processChunks' path args Stream.nil
--   
-- -- Prefer toChunks over toBytes when performance matters. toChunks' :: (IsStream t, MonadAsync m, MonadCatch m) => FilePath -> [String] -> t m (Either (Array Word8) (Array Word8)) -- | Like processChunks except that it works on a stream of bytes -- instead of a stream of chunks. -- -- We can write the example in processChunks as follows. Notice -- how seamlessly we can replace the tr process with the Haskell -- toUpper function: -- --
--   >>> :{
--      Process.toBytes "echo" ["hello world"]
--    & Unicode.decodeLatin1 & Stream.map toUpper & Unicode.encodeLatin1
--    & Stream.fold Stdio.write
--    :}
--   HELLO WORLD
--   
processBytes :: (IsStream t, MonadCatch m, MonadAsync m) => FilePath -> [String] -> t m Word8 -> t m Word8 -- | processBytes' path args input runs the executable at -- path using args as arguments and input -- stream as its standard input. The error stream of the executable is -- presented as Left values in the resulting stream and output -- stream as Right values. -- -- Raises ProcessFailure exception in case of failure. -- -- For example, the following is equivalent to the shell command echo -- "hello world" | tr [:lower:] [:upper:]: -- --
--   >>> :{
--      processBytes' "echo" ["hello world"] Stream.nil
--    & Stream.rights
--    & processBytes' "tr" ["[:lower:]", "[:upper:]"]
--    & Stream.rights
--    & Stream.fold Stdio.write
--    :}
--   HELLO WORLD
--   
processBytes' :: (IsStream t, MonadCatch m, MonadAsync m) => FilePath -> [String] -> t m Word8 -> t m (Either Word8 Word8) processChunksWith :: (IsStream t, MonadCatch m, MonadAsync m) => (Config -> Config) -> FilePath -> [String] -> t m (Array Word8) -> t m (Array Word8) -- | processChunks file args input runs the executable -- file specified by its name or path using args as -- arguments and input stream as its standard input. Returns the -- standard output of the executable as a stream. -- -- If only the name of an executable file is specified instead of its -- path then the file name is searched in the directories specified by -- the PATH environment variable. -- -- If the input stream throws an exception or if the output stream is -- garbage collected before it could finish then the process is sent a -- SIGTERM and we wait for it to terminate gracefully. -- -- If the process terminates with a non-zero exit code then a -- ProcessFailure exception is raised. -- -- The following code is equivalent to the shell command echo "hello -- world" | tr [a-z] [A-Z]: -- --
--   >>> :{
--      Process.toChunks "echo" ["hello world"]
--    & Process.processChunks "tr" ["[a-z]", "[A-Z]"]
--    & Stream.fold Stdio.writeChunks
--    :}
--   HELLO WORLD
--   
processChunks :: (IsStream t, MonadCatch m, MonadAsync m) => FilePath -> [String] -> t m (Array Word8) -> t m (Array Word8) processChunks'With :: (IsStream t, MonadCatch m, MonadAsync m) => (Config -> Config) -> FilePath -> [String] -> t m (Array Word8) -> t m (Either (Array Word8) (Array Word8)) processChunks' :: (IsStream t, MonadCatch m, MonadAsync m) => FilePath -> [String] -> t m (Array Word8) -> t m (Either (Array Word8) (Array Word8)) instance GHC.Show.Show Streamly.Internal.System.Process.ProcessFailure instance GHC.Exception.Type.Exception Streamly.Internal.System.Process.ProcessFailure module Streamly.Internal.System.Process.Posix -- | Thread safe, mutable process handle. Process status is stored in the -- handle and is modified by the process inspection operations. data Process -- | Creates a new process, executes the specified action in the cloned -- process and then performs an exec system call using the -- provided path, arguments and environment. The PATH is searched for the -- specified binary when the specified path is not absolute? newProcess :: IO () -> FilePath -> [String] -> Maybe [(String, String)] -> IO Process -- | Wait until the process exits by itself or gets terminated due to a -- signal. Returns the ProcessStatus which includes the -- termination reason or exit code. -- -- Thread safe. wait :: Process -> IO ProcessStatus -- | Get the current status of a process. A Nothing value means the -- process is still running, a Just value means the process is -- terminated and provides the status of the process. -- -- Thread safe. getStatus :: Process -> IO (Maybe ProcessStatus) -- | return (parent, child, (parentAction, childAction, failureAction)) mkPipe :: Direction -> IO (Fd, Fd, (IO (), IO (), IO ())) mkStdioPipes :: Bool -> IO ((Handle, Handle, Maybe Handle, Handle, Handle), IO (), IO (), IO ()) instance GHC.Classes.Eq Streamly.Internal.System.Process.Posix.Direction instance GHC.Show.Show Streamly.Internal.System.Process.Posix.Direction instance GHC.Show.Show Streamly.Internal.System.Process.Posix.ProcessDoesNotExist instance GHC.Exception.Type.Exception Streamly.Internal.System.Process.Posix.ProcessDoesNotExist -- | Use OS processes as stream transformation functions. -- -- This module provides functions to run operating system processes as -- stream source, sink or transformation functions. Thus OS processes can -- be used in the same way as Haskell functions and all the streaming -- combinators in streamly can be used to combine them. This allows you -- to seamlessly integrate external programs into your Haskell program. -- -- We recommend that you use Streamly threads instead of system processes -- where possible as they have a simpler programming model and processes -- have a larger performance overhead. -- --

Imports for examples

-- -- Use the following imports for examples below. -- --
--   >>> :set -XFlexibleContexts
--   
--   >>> :set -XScopedTypeVariables
--   
--   >>> import Data.Char (toUpper)
--   
--   >>> import Data.Function ((&))
--   
--   >>> import qualified Streamly.Console.Stdio as Stdio
--   
--   >>> import qualified Streamly.Data.Array.Foreign as Array
--   
--   >>> import qualified Streamly.Data.Fold as Fold
--   
--   >>> import qualified Streamly.Prelude as Stream
--   
--   >>> import qualified Streamly.System.Process as Process
--   
--   >>> import qualified Streamly.Unicode.Stream as Unicode
--   
--   >>> import qualified Streamly.Internal.FileSystem.Dir as Dir
--   
--   >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream
--   
-- --

Executables as functions

-- -- Streamly provides powerful ways to combine streams. Processes can be -- composed in a streaming pipeline just like a Posix shell command -- pipeline except that we use & instead of |. -- Moreover, we can mix processes and Haskell functions seamlessly in a -- processing pipeline. For example: -- --
--   >>> :{
--      Process.toBytes "echo" ["hello world"]
--    & Process.processBytes "tr" ["[a-z]", "[A-Z]"]
--    & Stream.fold Stdio.write
--    :}
--    HELLO WORLD
--   
-- -- Of course, you can use a Haskell function instead of "tr": -- --
--   >>> :{
--      Process.toBytes "echo" ["hello world"]
--    & Unicode.decodeLatin1 & Stream.map toUpper & Unicode.encodeLatin1
--    & Stream.fold Stdio.write
--    :}
--    HELLO WORLD
--   
-- --

Shell commands as functions

-- -- Using a shell as the command interpreter we can use shell commands in -- a data processing pipeline: -- --
--   >>> :{
--      Process.toBytes "sh" ["-c", "echo hello | tr [a-z] [A-Z]"]
--    & Stream.fold Stdio.write
--    :}
--    HELLO
--   
-- --

Concurrent Processing

-- -- We can run executables or commands concurrently as we would run any -- other functions in Streamly. For example, the following program greps -- the word "to" in all the files in the current directory concurrently: -- --
--   >>> :{
--   grep file =
--      Process.toBytes "grep" ["-H", "to", file]
--    & Stream.handle (\(_ :: Process.ProcessFailure) -> Stream.nil)
--    & Stream.splitWithSuffix (== 10) Array.write
--    :}
--   
-- --
--   >>> :{
--   pgrep =
--      Dir.toFiles "."
--    & Stream.concatMapWith Stream.async grep
--    & Stream.fold Stdio.writeChunks
--   :}
--   
-- --

Exception handling

-- -- Since we are composing using Streamly streaming pipeline there is -- nothing special about exception handling, it works the same as in -- Streamly. Like the pipefail option in shells, exceptions are -- propagated if any of the stages fail. -- --

Process Attributes

-- -- When a new process is spawned, the following attributes are inherited -- from the parent process: -- -- module Streamly.System.Process -- | An exception that is raised when a process fails. newtype ProcessFailure -- | The exit code of the process. ProcessFailure :: Int -> ProcessFailure -- | See processChunks. toChunks is defined as: -- --
--   >>> toChunks path args = processChunks path args Stream.nil
--   
-- -- The following code is equivalent to the shell command echo "hello -- world": -- --
--   >>> :{
--      Process.toChunks "echo" ["hello world"]
--    & Stream.fold Stdio.writeChunks
--    :}
--   hello world
--   
toChunks :: (IsStream t, MonadAsync m, MonadCatch m) => FilePath -> [String] -> t m (Array Word8) -- | See processBytes. toBytes is defined as: -- --
--   >>> toBytes path args = processBytes path args Stream.nil
--   
-- -- The following code is equivalent to the shell command echo "hello -- world": -- --
--   >>> :{
--      Process.toBytes "echo" ["hello world"]
--    & Stream.fold Stdio.write
--    :}
--   hello world
--   
toBytes :: (IsStream t, MonadAsync m, MonadCatch m) => FilePath -> [String] -> t m Word8 -- | processChunks file args input runs the executable -- file specified by its name or path using args as -- arguments and input stream as its standard input. Returns the -- standard output of the executable as a stream. -- -- If only the name of an executable file is specified instead of its -- path then the file name is searched in the directories specified by -- the PATH environment variable. -- -- If the input stream throws an exception or if the output stream is -- garbage collected before it could finish then the process is sent a -- SIGTERM and we wait for it to terminate gracefully. -- -- If the process terminates with a non-zero exit code then a -- ProcessFailure exception is raised. -- -- The following code is equivalent to the shell command echo "hello -- world" | tr [a-z] [A-Z]: -- --
--   >>> :{
--      Process.toChunks "echo" ["hello world"]
--    & Process.processChunks "tr" ["[a-z]", "[A-Z]"]
--    & Stream.fold Stdio.writeChunks
--    :}
--   HELLO WORLD
--   
processChunks :: (IsStream t, MonadCatch m, MonadAsync m) => FilePath -> [String] -> t m (Array Word8) -> t m (Array Word8) -- | Like processChunks except that it works on a stream of bytes -- instead of a stream of chunks. -- -- We can write the example in processChunks as follows. Notice -- how seamlessly we can replace the tr process with the Haskell -- toUpper function: -- --
--   >>> :{
--      Process.toBytes "echo" ["hello world"]
--    & Unicode.decodeLatin1 & Stream.map toUpper & Unicode.encodeLatin1
--    & Stream.fold Stdio.write
--    :}
--   HELLO WORLD
--   
processBytes :: (IsStream t, MonadCatch m, MonadAsync m) => FilePath -> [String] -> t m Word8 -> t m Word8