Skip to content

Commit 2e58f66

Browse files
committed
Add 'exec'
Also use the new Uid and Gid types instead of Int
1 parent f165faa commit 2e58f66

File tree

3 files changed

+103
-15
lines changed

3 files changed

+103
-15
lines changed

src/Node/ChildProcess.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ exports.spawnImpl = function spawnImpl(command) {
2121
};
2222
};
2323
};
24+
exports.execImpl = function execImpl(command) {
25+
return function(opts) {
26+
return function(callback) {
27+
return function() {
28+
return require("child_process").exec(command, opts, function(err, stdout, stderr) {
29+
callback(err)(stdout)(stderr)();
30+
});
31+
};
32+
};
33+
};
34+
};
2435
exports.mkOnExit = function mkOnExit(mkChildExit){
2536
return function onExit(cp){
2637
return function(cb){

src/Node/ChildProcess.purs

Lines changed: 85 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
-- | import Node.ChildProcess (ChildProcess(), CHILD_PROCESS())
88
-- | import Node.ChildProcess as ChildProcess
99
-- | ```
10+
-- |
11+
-- | The [Node.js documentation](https://nodejs.org/api/child_process.html)
12+
-- | will probably also be useful to read if you want to use this module.
1013
module Node.ChildProcess
1114
( Handle()
1215
, ChildProcess()
@@ -28,9 +31,13 @@ module Node.ChildProcess
2831
, onMessage
2932
, onError
3033
, spawn
31-
, fork
3234
, SpawnOptions()
3335
, defaultSpawnOptions
36+
, exec
37+
, ExecOptions()
38+
, ExecResult()
39+
, defaultExecOptions
40+
, fork
3441
, StdIOBehaviour(..)
3542
, pipe
3643
, inherit
@@ -48,13 +55,15 @@ import Control.Monad.Eff.Exception.Unsafe (unsafeThrow)
4855
import Data.StrMap (StrMap())
4956
import Data.Function (Fn2(), runFn2)
5057
import Data.Nullable (Nullable(), toNullable, toMaybe)
51-
import Data.Maybe (Maybe(..), fromMaybe)
58+
import Data.Maybe (Maybe(..), fromMaybe, maybe)
5259
import Data.Foreign (Foreign())
53-
import Data.Posix (Pid())
60+
import Data.Posix (Pid(), Gid(), Uid())
5461
import Data.Posix.Signal (Signal())
5562
import Data.Posix.Signal as Signal
5663
import Unsafe.Coerce (unsafeCoerce)
5764

65+
import Node.Buffer (Buffer())
66+
import Node.Encoding (Encoding())
5867
import Node.FS as FS
5968
import Node.Stream (Readable(), Writable(), Stream())
6069

@@ -138,14 +147,6 @@ instance showExit :: Show Exit where
138147
show (Normally x) = "Normally " <> show x
139148
show (BySignal sig) = "BySignal " <> show sig
140149

141-
type SpawnOptions =
142-
{ cwd :: Maybe String
143-
, stdio :: Array (Maybe StdIOBehaviour)
144-
, env :: Maybe (StrMap String)
145-
, detached :: Boolean
146-
, uid :: Maybe Int
147-
, gid :: Maybe Int
148-
}
149150

150151
mkExit :: Nullable Int -> Nullable String -> Exit
151152
mkExit code signal =
@@ -201,10 +202,14 @@ foreign import spawnImpl :: forall opts eff. String -> Array String -> { | opts
201202
-- There's gotta be a better way.
202203
foreign import undefined :: forall a. a
203204

204-
-- | A special case of `spawn` for creating Node.js child processes. The first
205-
-- | argument is the module to be run, and the second is the argv (command line
206-
-- | arguments).
207-
foreign import fork :: forall eff. String -> Array String -> Eff (cp :: CHILD_PROCESS | eff) ChildProcess
205+
type SpawnOptions =
206+
{ cwd :: Maybe String
207+
, stdio :: Array (Maybe StdIOBehaviour)
208+
, env :: Maybe (StrMap String)
209+
, detached :: Boolean
210+
, uid :: Maybe Uid
211+
, gid :: Maybe Gid
212+
}
208213

209214
defaultSpawnOptions :: SpawnOptions
210215
defaultSpawnOptions =
@@ -216,6 +221,71 @@ defaultSpawnOptions =
216221
, gid: Nothing
217222
}
218223

224+
-- | Similar to `spawn`, except that this variant will buffer output, and wait
225+
-- | until the process has exited before calling the callback.
226+
-- |
227+
-- | Note that the child process will be killed if the amount of output exceeds
228+
-- | a certain threshold (the default is defined by Node.js).
229+
exec :: forall eff.
230+
String ->
231+
ExecOptions ->
232+
(ExecResult -> Eff (cp :: CHILD_PROCESS | eff) Unit) ->
233+
Eff (cp :: CHILD_PROCESS | eff) Unit
234+
exec cmd opts callback =
235+
execImpl cmd (convert opts) \err stdout' stderr' ->
236+
callback { error: (toMaybe err)
237+
, stdout: stdout'
238+
, stderr: stderr'
239+
}
240+
where
241+
convert opts =
242+
{ cwd: fromMaybe undefined opts.cwd
243+
, env: fromMaybe undefined opts.env
244+
, timeout: fromMaybe undefined opts.timeout
245+
, maxBuffer: fromMaybe undefined opts.maxBuffer
246+
, killSignal: fromMaybe undefined opts.killSignal
247+
, uid: fromMaybe undefined opts.uid
248+
, gid: fromMaybe undefined opts.gid
249+
}
250+
251+
foreign import execImpl :: forall eff opts.
252+
String ->
253+
{ | opts } ->
254+
(Nullable Exception.Error -> Buffer -> Buffer -> Eff (cp :: CHILD_PROCESS | eff) Unit) ->
255+
Eff (cp :: CHILD_PROCESS | eff) Unit
256+
257+
type ExecOptions =
258+
{ cwd :: Maybe String
259+
, env :: Maybe (StrMap String)
260+
, timeout :: Maybe Number
261+
, maxBuffer :: Maybe Int
262+
, killSignal :: Maybe Signal
263+
, uid :: Maybe Uid
264+
, gid :: Maybe Gid
265+
}
266+
267+
defaultExecOptions :: ExecOptions
268+
defaultExecOptions =
269+
{ cwd: Nothing
270+
, env: Nothing
271+
, timeout: Nothing
272+
, maxBuffer: Nothing
273+
, killSignal: Nothing
274+
, uid: Nothing
275+
, gid: Nothing
276+
}
277+
278+
type ExecResult =
279+
{ stderr :: Buffer
280+
, stdout :: Buffer
281+
, error :: Maybe Exception.Error
282+
}
283+
284+
-- | A special case of `spawn` for creating Node.js child processes. The first
285+
-- | argument is the module to be run, and the second is the argv (command line
286+
-- | arguments).
287+
foreign import fork :: forall eff. String -> Array String -> Eff (cp :: CHILD_PROCESS | eff) ChildProcess
288+
219289
-- | An error which occurred inside a child process.
220290
type Error =
221291
{ code :: String

test/Main.purs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ main = do
4141
_ -> do
4242
log ("Bad exit: expected `BySignal SIGTERM`, got: " <> show exit)
4343

44+
log "exec"
45+
execLs
46+
4447
spawnLs = do
4548
ls <- spawn "ls" ["-la"] defaultSpawnOptions
4649
onExit ls \exit ->
@@ -50,3 +53,7 @@ spawnLs = do
5053
nonExistentExecutable done = do
5154
ch <- spawn "this-does-not-exist" [] defaultSpawnOptions
5255
onError ch (\err -> logAny err *> done)
56+
57+
execLs = do
58+
exec "ls >&2" defaultExecOptions \r ->
59+
log "redirected to stderr:" *> (Buffer.toString UTF8 r.stderr >>= log)

0 commit comments

Comments
 (0)