Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add APIs from System.Process to System.Command #81

Merged
merged 2 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

* Allow streamly-0.10.0 and streamly-core-0.2.0
* Fix a bug in quote escaping in the Command module
* Add APIs in System.Process and System.Command module with ability to set
process attributes.

## 0.3.0 (Apr 2023)

Expand Down
87 changes: 87 additions & 0 deletions src/Streamly/Internal/System/Command.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module Streamly.Internal.System.Command
-- * Generation
toBytes
, toChunks
, toChunksWith
, toChars
, toLines

Expand All @@ -27,6 +28,12 @@ module Streamly.Internal.System.Command
, pipeBytes
, pipeChars
, pipeChunks
, pipeChunksWith

-- * Standalone Processes
, standalone
, foreground
, daemon

-- * Helpers
, quotedWord
Expand All @@ -43,6 +50,9 @@ import Streamly.Data.Array (Array)
import Streamly.Data.Fold (Fold)
import Streamly.Data.Parser (Parser)
import Streamly.Data.Stream.Prelude (MonadAsync, Stream)
import Streamly.Internal.System.Process (Config)
import System.Exit (ExitCode(..))
import System.Process (ProcessHandle)

import qualified Streamly.Data.Fold as Fold
import qualified Streamly.Data.Parser as Parser
Expand Down Expand Up @@ -157,6 +167,15 @@ pipeWith f cmd input =
y:ys -> return $ f y ys input
_ -> error "streamWith: empty command"

-- | Like 'pipeChunks' but use the specified configuration to run the process.
{-# INLINE pipeChunksWith #-}
pipeChunksWith ::
(MonadCatch m, MonadAsync m)
=> (Config -> Config) -- ^ Config modifier
-> String -- ^ Command
-> Stream m (Array Word8) -- ^ Input stream
-> Stream m (Array Word8) -- ^ Output stream
pipeChunksWith modifier = pipeWith (Process.pipeChunksWith modifier)

-- | @pipeChunks command input@ runs the executable with arguments specified by
-- @command@ and supplying @input@ stream as its standard input. Returns the
Expand Down Expand Up @@ -241,6 +260,15 @@ pipeChars = pipeWith Process.pipeChars
toBytes :: (MonadAsync m, MonadCatch m) => String -> Stream m Word8
toBytes = streamWith Process.toBytes

-- | Like 'toChunks' but use the specified configuration to run the process.
{-# INLINE toChunksWith #-}
toChunksWith ::
(MonadCatch m, MonadAsync m)
=> (Config -> Config) -- ^ Config modifier
-> String -- ^ Command
-> Stream m (Array Word8) -- ^ Output stream
toChunksWith modifier = streamWith (Process.toChunksWith modifier)

-- >>> toChunks = streamWith Process.toChunks

-- |
Expand Down Expand Up @@ -318,3 +346,62 @@ toNull ::
=> String -- ^ Command
-> m ()
toNull = runWith Process.toNull

-------------------------------------------------------------------------------
-- Processes not interacting with the parent process
-------------------------------------------------------------------------------

-- | Launch a standlone process i.e. the process does not have a way to attach
-- the IO streams with other processes. The IO streams stdin, stdout, stderr
-- can either be inherited from the parent or closed.
--
-- This API is more powerful than 'interactive' and 'daemon' and can be used to
-- implement both of these. However, it should be used carefully e.g. if you
-- inherit the IO streams and parent is not waiting for the child process to
-- finish then both parent and child may use the IO streams resulting in
-- garbled IO if both are reading/writing simultaneously.
--
-- If the parent chooses to wait for the process an 'ExitCode' is returned
-- otherwise a 'ProcessHandle' is returned which can be used to terminate the
-- process, send signals to it or wait for it to finish.
{-# INLINE standalone #-}
standalone ::
Bool -- ^ Wait for process to finish?
-> (Bool, Bool, Bool) -- ^ close (stdin, stdout, stderr)
-> (Config -> Config)
-> String -- ^ Command
-> IO (Either ExitCode ProcessHandle)
standalone wait streams modCfg =
runWith (Process.standalone wait streams modCfg)

-- | Launch a process interfacing with the user. User interrupts are sent to
-- the launched process and ignored by the parent process. The launched process
-- inherits stdin, stdout, and stderr from the parent, so that the user can
-- interact with the process. The parent waits for the child process to exit,
-- an 'ExitCode' is returned when the process finishes.
--
-- This is the same as the common @system@ function found in other libraries
-- used to execute commands.
--
-- On Windows you can pass @setSession NewConsole@ to create a new console.
--
{-# INLINE foreground #-}
foreground ::
(Config -> Config)
-> String -- ^ Command
-> IO ExitCode
foreground modCfg = runWith (Process.foreground modCfg)

-- | Launch a daemon process. Closes stdin, stdout and stderr, creates a new
-- session, detached from the terminal, the parent does not wait for the
-- process to finish.
--
-- The 'ProcessHandle' returned can be used to terminate the daemon or send
-- signals to it.
--
{-# INLINE daemon #-}
daemon ::
(Config -> Config)
-> String -- ^ Command
-> IO ProcessHandle
daemon modCfg = runWith (Process.daemon modCfg)
50 changes: 46 additions & 4 deletions src/Streamly/System/Command.hs
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,36 @@ module Streamly.System.Command
--
-- $setup

-- * Types
ProcessFailure (..)
-- * Exceptions
Process.ProcessFailure (..)

-- * Process Configuration
-- | Use the config modifiers to modify the default config.
, Process.Config

-- ** Common Modifiers
-- | These options apply to both POSIX and Windows.
, Process.setCwd
, Process.setEnv
, Process.closeFiles
, Process.newProcessGroup
, Process.Session (..)
, Process.setSession

-- ** Posix Only Modifiers
-- | These options have no effect on Windows.
, Process.interruptChildOnly
, Process.setUserId
, Process.setGroupId

-- ** Windows Only Modifiers
-- | These options have no effect on Posix.
, Process.waitForDescendants

-- * Generation
, toBytes
, toChunks
, toChunksWith
, toChars
, toLines

Expand All @@ -109,9 +133,27 @@ module Streamly.System.Command
, toNull

-- * Transformation
, pipeChunks
, pipeChunksWith
, pipeBytes
, pipeChars
, pipeChunks

-- -- * Including Stderr Stream
-- | Like other "Generation" routines but along with stdout, stderr is also
-- included in the output stream. stdout is converted to 'Right' values in
-- the output stream and stderr is converted to 'Left' values.
-- , toBytesEither
-- , toChunksEither
-- , toChunksEitherWith
-- , pipeBytesEither
-- , pipeChunksEither
-- , pipeChunksEitherWith

-- * Non-streaming Processes
-- | These processes do not attach the IO streams with other processes.
, foreground
, daemon
, standalone

-- -- * Helpers
-- , runWith
Expand All @@ -121,7 +163,7 @@ module Streamly.System.Command
where

import Streamly.Internal.System.Command
import Streamly.Internal.System.Process (ProcessFailure (..))
import qualified Streamly.Internal.System.Process as Process -- (ProcessFailure (..))

-- Keep it synced with the Internal module

Expand Down
2 changes: 2 additions & 0 deletions src/Streamly/System/Process.hs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ module Streamly.System.Process
, toBytes
, toChars
, toLines

-- * Effects
, toString
, toStdout
, toNull
Expand Down
Loading