@@ -18,6 +18,7 @@ module Node.ChildProcess
1818 , onError
1919 , spawn
2020 , SpawnOptions ()
21+ , StdIOBehaviour ()
2122 , defaultSpawnOptions
2223 ) where
2324
@@ -28,11 +29,13 @@ import Control.Monad.Eff (Eff())
2829import Data.StrMap (StrMap ())
2930import Data.Function (Fn2 (), runFn2 )
3031import Data.Nullable (Nullable (), toNullable )
31- import Data.Maybe (Maybe (..))
32+ import Data.Maybe (Maybe (..), fromMaybe )
3233import Data.Foreign (Foreign ())
34+ import Unsafe.Coerce (unsafeCoerce )
3335
36+ import Node.FS as FS
3437import Node.Buffer (Buffer ())
35- import Node.Stream (Readable (), Writable ())
38+ import Node.Stream (Readable (), Writable (), Stream () )
3639import Node.ChildProcess.Signal (Signal (..))
3740
3841-- | A handle for inter-process communication (IPC).
@@ -95,53 +98,121 @@ kill :: forall eff. Signal -> ChildProcess -> Eff (cp :: CHILD_PROCESS | eff) Bo
9598kill sig (ChildProcess cp) = pure (cp.kill sig)
9699
97100type SpawnOptions =
98- { cwd :: String
99- , stdio :: Array String
100- , env :: Nullable (StrMap String )
101+ { cwd :: Maybe String
102+ , stdio :: Array ( Maybe StdIOBehaviour )
103+ , env :: Maybe (StrMap String )
101104 , detached :: Boolean
102- , uid :: Int
103- , gid :: Int
105+ , uid :: Maybe Int
106+ , gid :: Maybe Int
104107 }
105108
106109onExit :: forall eff . ChildProcess -> (Maybe Int -> Maybe Signal -> Eff eff Unit ) -> Eff eff Unit
107110onExit = mkOnExit Nothing Just Signal
108111
112+ foreign import mkOnExit :: forall a eff .
113+ Maybe a -> (a -> Maybe a ) -> (String -> Signal ) ->
114+ ChildProcess -> (Maybe Int -> Maybe Signal -> Eff eff Unit ) -> Eff eff Unit
115+
109116onClose :: forall eff . ChildProcess -> (Maybe Int -> Maybe Signal -> Eff eff Unit ) -> Eff eff Unit
110117onClose = mkOnClose Nothing Just Signal
111118
119+ foreign import mkOnClose :: forall a eff .
120+ Maybe a -> (a -> Maybe a ) -> (String -> Signal ) ->
121+ ChildProcess -> (Maybe Int -> Maybe Signal -> Eff eff Unit ) -> Eff eff Unit
122+
112123onMessage :: forall eff . ChildProcess -> (Foreign -> Maybe Handle -> Eff eff Unit ) -> Eff eff Unit
113124onMessage = mkOnMessage Nothing Just
114125
126+ foreign import mkOnMessage :: forall a eff .
127+ Maybe a -> (a -> Maybe a ) ->
128+ ChildProcess -> (Foreign -> Maybe Handle -> Eff eff Unit ) -> Eff eff Unit
129+
115130foreign import onDisconnect :: forall eff . ChildProcess -> Eff eff Unit -> Eff eff Unit
116131foreign import onError :: forall eff . ChildProcess -> (ChildProcessError -> Eff eff Unit ) -> Eff eff Unit
117132
118- foreign import spawn :: forall eff . String -> Array String -> SpawnOptions -> Eff (cp :: CHILD_PROCESS | eff ) ChildProcess
133+ spawn :: forall eff . String -> Array String -> SpawnOptions -> Eff (cp :: CHILD_PROCESS | eff ) ChildProcess
134+ spawn cmd args opts = spawnImpl cmd args (convertOpts opts)
135+ where
136+ convertOpts opts =
137+ { cwd: fromMaybe undefined opts.cwd
138+ , stdio: toActualStdIOOptions opts.stdio
139+ , env: toNullable opts.env
140+ , detached: opts.detached
141+ , uid: fromMaybe undefined opts.uid
142+ , gid: fromMaybe undefined opts.gid
143+ }
144+
145+ foreign import spawnImpl :: forall opts eff . String -> Array String -> { | opts } -> Eff (cp :: CHILD_PROCESS | eff ) ChildProcess
146+
147+ -- There's gotta be a better way.
148+ foreign import undefined :: forall a . a
119149
120150defaultSpawnOptions :: SpawnOptions
121151defaultSpawnOptions =
122- { cwd: undefined
123- , stdio: [ " pipe" , " pipe " , " pipe " ]
124- , env: toNullable Nothing
152+ { cwd: Nothing
153+ , stdio: pipe
154+ , env: Nothing
125155 , detached: false
126- , uid: undefined
127- , gid: undefined
156+ , uid: Nothing
157+ , gid: Nothing
128158 }
129159
130- type ChildProcessError =
160+ -- | An error which occurred inside a child process.
161+ type ChildProcessError =
131162 { code :: String
132163 , errno :: String
133164 , syscall :: String
134165 }
135166
136- foreign import mkOnExit :: forall a eff .
137- Maybe a -> (a -> Maybe a ) -> (String -> Signal ) ->
138- ChildProcess -> (Maybe Int -> Maybe Signal -> Eff eff Unit ) -> Eff eff Unit
139- foreign import mkOnMessage :: forall a eff .
140- Maybe a -> (a -> Maybe a ) ->
141- ChildProcess -> (Foreign -> Maybe Handle -> Eff eff Unit ) -> Eff eff Unit
142- foreign import mkOnClose :: forall a eff .
143- Maybe a -> (a -> Maybe a ) -> (String -> Signal ) ->
144- ChildProcess -> (Maybe Int -> Maybe Signal -> Eff eff Unit ) -> Eff eff Unit
145-
146- -- There's gotta be a better way.
147- foreign import undefined :: forall a . a
167+ -- | Behaviour for standard IO streams (eg, standard input, standard output) of
168+ -- | a child process.
169+ -- |
170+ -- | * `Pipe`: creates a pipe between the child and parent process, which can
171+ -- | then be accessed as a `Stream` via the `stdin`, `stdout`, or `stderr`
172+ -- | functions.
173+ -- | * `Ignore`: ignore this stream. This will cause Node to open /dev/null and
174+ -- | connect it to the stream.
175+ -- | * `ShareStream`: Connect the supplied stream to the corresponding file
176+ -- | descriptor in the child.
177+ -- | * `ShareFD`: Connect the supplied file descriptor (which should be open
178+ -- | in the parent) to the corresponding file descriptor in the child.
179+ data StdIOBehaviour
180+ = Pipe
181+ | Ignore
182+ | ShareStream (forall r eff a . Stream r eff a )
183+ | ShareFD FS.FileDescriptor
184+
185+ -- | Create pipes for each of the three standard IO streams.
186+ pipe :: Array (Maybe StdIOBehaviour )
187+ pipe = map Just [Pipe , Pipe , Pipe ]
188+
189+ -- | Share stdin with stdin, stdout with stdout, and stderr with stderr.
190+ inherit :: Array (Maybe StdIOBehaviour )
191+ inherit = map Just
192+ [ ShareStream process.stdin
193+ , ShareStream process.stdout
194+ , ShareStream process.stderr
195+ ]
196+
197+ foreign import process :: forall props . { | props }
198+
199+ -- | Ignore all streams.
200+ ignore :: Array (Maybe StdIOBehaviour )
201+ ignore = map Just [Ignore , Ignore , Ignore ]
202+
203+ foreign import data ActualStdIOBehaviour :: *
204+
205+ toActualStdIOBehaviour :: StdIOBehaviour -> ActualStdIOBehaviour
206+ toActualStdIOBehaviour b = case b of
207+ Pipe -> c " pipe"
208+ Ignore -> c " ignore"
209+ ShareFD x -> c x
210+ ShareStream stream -> c stream
211+ where
212+ c :: forall a . a -> ActualStdIOBehaviour
213+ c = unsafeCoerce
214+
215+ type ActualStdIOOptions = Array (Nullable ActualStdIOBehaviour )
216+
217+ toActualStdIOOptions :: Array (Maybe StdIOBehaviour ) -> ActualStdIOOptions
218+ toActualStdIOOptions = map (toNullable <<< map toActualStdIOBehaviour)
0 commit comments