From fd33a8f06a29d3bb3c2c0d74beee84e503e3d6bd Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Mon, 14 Dec 2015 23:19:55 +0100 Subject: [PATCH 1/2] Add stream functions --- bower.json | 5 +- docs/Node/FS/Stream.md | 95 +++++++++++++++++++++++++ src/Node/FS/Internal.js | 4 ++ src/Node/FS/Internal.purs | 2 + src/Node/FS/Stream.purs | 144 ++++++++++++++++++++++++++++++++++++++ src/Node/FS/Sync.purs | 1 + test/Main.purs | 2 + test/Streams.purs | 38 ++++++++++ 8 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 docs/Node/FS/Stream.md create mode 100644 src/Node/FS/Internal.js create mode 100644 src/Node/FS/Stream.purs create mode 100644 test/Streams.purs diff --git a/bower.json b/bower.json index 1b567ab..ffb7328 100644 --- a/bower.json +++ b/bower.json @@ -16,12 +16,13 @@ ], "dependencies": { "purescript-datetime": "^0.9.0", - "purescript-exceptions": "^0.3.0", "purescript-foreign": "^0.7.0", "purescript-node-buffer": "^0.2.0", "purescript-node-path": "^0.4.0", "purescript-unsafe-coerce": "^0.1.0", - "purescript-nullable": "~0.2.1" + "purescript-nullable": "~0.2.1", + "purescript-node-streams": "~0.3.0", + "purescript-exceptions": "~0.3.3" }, "devDependencies": { "purescript-console": "~0.1.1" diff --git a/docs/Node/FS/Stream.md b/docs/Node/FS/Stream.md new file mode 100644 index 0000000..50ec181 --- /dev/null +++ b/docs/Node/FS/Stream.md @@ -0,0 +1,95 @@ +## Module Node.FS.Stream + +#### `createWriteStream` + +``` purescript +createWriteStream :: forall eff. FilePath -> Eff (fs :: FS | eff) (Writable () (fs :: FS | eff)) +``` + +Create a Writable stream which writes data to the specified file, using +the default options. + +#### `fdCreateWriteStream` + +``` purescript +fdCreateWriteStream :: forall eff. FileDescriptor -> Eff (fs :: FS | eff) (Writable () (fs :: FS | eff)) +``` + +Create a Writable stream which writes data to the specified file +descriptor, using the default options. + +#### `WriteStreamOptions` + +``` purescript +type WriteStreamOptions = { flags :: FileFlags, perms :: Perms } +``` + +#### `defaultWriteStreamOptions` + +``` purescript +defaultWriteStreamOptions :: WriteStreamOptions +``` + +#### `createWriteStreamWith` + +``` purescript +createWriteStreamWith :: forall eff. WriteStreamOptions -> FilePath -> Eff (fs :: FS | eff) (Writable () (fs :: FS | eff)) +``` + +Like `createWriteStream`, but allows you to pass options. + +#### `fdCreateWriteStreamWith` + +``` purescript +fdCreateWriteStreamWith :: forall eff. WriteStreamOptions -> FileDescriptor -> Eff (fs :: FS | eff) (Writable () (fs :: FS | eff)) +``` + +Like `fdCreateWriteStream`, but allows you to pass options. + +#### `createReadStream` + +``` purescript +createReadStream :: forall eff. FilePath -> Eff (fs :: FS | eff) (Readable () (fs :: FS | eff)) +``` + +Create a Readable stream which reads data to the specified file, using +the default options. + +#### `fdCreateReadStream` + +``` purescript +fdCreateReadStream :: forall eff. FileDescriptor -> Eff (fs :: FS | eff) (Readable () (fs :: FS | eff)) +``` + +Create a Readable stream which reads data to the specified file +descriptor, using the default options. + +#### `ReadStreamOptions` + +``` purescript +type ReadStreamOptions = { flags :: FileFlags, perms :: Perms, autoClose :: Boolean } +``` + +#### `defaultReadStreamOptions` + +``` purescript +defaultReadStreamOptions :: ReadStreamOptions +``` + +#### `createReadStreamWith` + +``` purescript +createReadStreamWith :: forall eff. ReadStreamOptions -> FilePath -> Eff (fs :: FS | eff) (Readable () (fs :: FS | eff)) +``` + +Create a Readable stream which reads data from the specified file. + +#### `fdCreateReadStreamWith` + +``` purescript +fdCreateReadStreamWith :: forall eff. ReadStreamOptions -> FileDescriptor -> Eff (fs :: FS | eff) (Readable () (fs :: FS | eff)) +``` + +Create a Readable stream which reads data from the specified file descriptor. + + diff --git a/src/Node/FS/Internal.js b/src/Node/FS/Internal.js new file mode 100644 index 0000000..90c3442 --- /dev/null +++ b/src/Node/FS/Internal.js @@ -0,0 +1,4 @@ +"use strict"; +// module Node.FS.Internal + +exports.unsafeRequireFS = require("fs"); diff --git a/src/Node/FS/Internal.purs b/src/Node/FS/Internal.purs index ab95804..fa41ae6 100644 --- a/src/Node/FS/Internal.purs +++ b/src/Node/FS/Internal.purs @@ -7,3 +7,5 @@ import Unsafe.Coerce mkEff :: forall e a. (Unit -> a) -> Eff e a mkEff = unsafeCoerce + +foreign import unsafeRequireFS :: forall props. { | props } diff --git a/src/Node/FS/Stream.purs b/src/Node/FS/Stream.purs new file mode 100644 index 0000000..6143cb4 --- /dev/null +++ b/src/Node/FS/Stream.purs @@ -0,0 +1,144 @@ +module Node.FS.Stream + ( createWriteStream + , fdCreateWriteStream + , WriteStreamOptions() + , defaultWriteStreamOptions + , createWriteStreamWith + , fdCreateWriteStreamWith + , createReadStream + , fdCreateReadStream + , ReadStreamOptions() + , defaultReadStreamOptions + , createReadStreamWith + , fdCreateReadStreamWith + ) where + +import Prelude +import Data.Maybe (Maybe(..)) +import Data.Function +import Data.Nullable +import Control.Monad.Eff +import Node.Stream (Readable(), Writable()) +import Node.Path (FilePath()) + +import Node.FS +import Node.FS.Perms (Perms()) +import Node.FS.Perms as Perms +import Node.FS.Internal + +fs :: + { createReadStream :: forall eff opts. Fn2 (Nullable FilePath) { | opts } (Readable () (fs :: FS | eff)) + , createWriteStream :: forall eff opts. Fn2 (Nullable FilePath) { | opts } (Writable () (fs :: FS | eff)) + } +fs = unsafeRequireFS + +readWrite :: Perms +readWrite = Perms.mkPerms rw rw rw + where + rw = Perms.read + Perms.write + +null :: forall a. Nullable a +null = toNullable Nothing + +nonnull :: forall a. a -> Nullable a +nonnull = toNullable <<< Just + +-- | Create a Writable stream which writes data to the specified file, using +-- | the default options. +createWriteStream :: forall eff. + FilePath + -> Eff (fs :: FS | eff) (Writable () (fs :: FS | eff)) +createWriteStream = createWriteStreamWith defaultWriteStreamOptions + +-- | Create a Writable stream which writes data to the specified file +-- | descriptor, using the default options. +fdCreateWriteStream :: forall eff. + FileDescriptor + -> Eff (fs :: FS | eff) (Writable () (fs :: FS | eff)) +fdCreateWriteStream = fdCreateWriteStreamWith defaultWriteStreamOptions + +type WriteStreamOptions = + { flags :: FileFlags + , perms :: Perms + } + +defaultWriteStreamOptions :: WriteStreamOptions +defaultWriteStreamOptions = + { flags: W + , perms: readWrite + } + +-- | Like `createWriteStream`, but allows you to pass options. +createWriteStreamWith :: forall eff. + WriteStreamOptions + -> FilePath + -> Eff (fs :: FS | eff) (Writable () (fs :: FS | eff)) +createWriteStreamWith opts file = mkEff $ \_ -> runFn2 + fs.createWriteStream (nonnull file) + { mode: Perms.permsToInt opts.perms + , flags: fileFlagsToNode opts.flags + } + +-- | Like `fdCreateWriteStream`, but allows you to pass options. +fdCreateWriteStreamWith :: forall eff. + WriteStreamOptions + -> FileDescriptor + -> Eff (fs :: FS | eff) (Writable () (fs :: FS | eff)) +fdCreateWriteStreamWith opts fd = mkEff $ \_ -> runFn2 + fs.createWriteStream null + { fd + , mode: Perms.permsToInt opts.perms + , flags: fileFlagsToNode opts.flags + } + +-- | Create a Readable stream which reads data to the specified file, using +-- | the default options. +createReadStream :: forall eff. + FilePath + -> Eff (fs :: FS | eff) (Readable () (fs :: FS | eff)) +createReadStream = createReadStreamWith defaultReadStreamOptions + +-- | Create a Readable stream which reads data to the specified file +-- | descriptor, using the default options. +fdCreateReadStream :: forall eff. + FileDescriptor + -> Eff (fs :: FS | eff) (Readable () (fs :: FS | eff)) +fdCreateReadStream = fdCreateReadStreamWith defaultReadStreamOptions + +type ReadStreamOptions = + { flags :: FileFlags + , perms :: Perms + , autoClose :: Boolean + } + +defaultReadStreamOptions :: ReadStreamOptions +defaultReadStreamOptions = + { flags: R + , perms: readWrite + , autoClose: true + } + +-- | Create a Readable stream which reads data from the specified file. +createReadStreamWith :: forall eff. + ReadStreamOptions + -> FilePath + -> Eff (fs :: FS | eff) (Readable () (fs :: FS | eff)) +createReadStreamWith opts file = mkEff $ \_ -> runFn2 + fs.createReadStream (nonnull file) + { mode: Perms.permsToInt opts.perms + , flags: fileFlagsToNode opts.flags + , autoClose: opts.autoClose + } + +-- | Create a Readable stream which reads data from the specified file descriptor. +fdCreateReadStreamWith :: forall eff. + ReadStreamOptions + -> FileDescriptor + -> Eff (fs :: FS | eff) (Readable () (fs :: FS | eff)) +fdCreateReadStreamWith opts fd = mkEff $ \_ -> runFn2 + fs.createReadStream null + { fd + , mode: Perms.permsToInt opts.perms + , flags: fileFlagsToNode opts.flags + , autoClose: opts.autoClose + } diff --git a/src/Node/FS/Sync.purs b/src/Node/FS/Sync.purs index 1383c78..a801638 100644 --- a/src/Node/FS/Sync.purs +++ b/src/Node/FS/Sync.purs @@ -42,6 +42,7 @@ import Data.Int (round) import Data.Maybe (Maybe(..)) import Node.Buffer (Buffer(), BUFFER(), size) import Node.Encoding + import Node.FS import Node.FS.Stats import Node.Path (FilePath()) diff --git a/test/Main.purs b/test/Main.purs index 6f203ac..0a2d869 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -3,7 +3,9 @@ module Test.Main where import Prelude import qualified Test as Test import qualified TestAsync as TestAsync +import Test.Streams as Streams main = do Test.main TestAsync.main + Streams.main diff --git a/test/Streams.purs b/test/Streams.purs new file mode 100644 index 0000000..c67c30b --- /dev/null +++ b/test/Streams.purs @@ -0,0 +1,38 @@ +module Test.Streams where + +import Prelude +import Data.Maybe +import Data.Either +import Control.Apply ((*>)) +import Control.Bind ((=<<)) +import Control.Monad.Eff +import Control.Monad.Eff.Exception +import Control.Monad.Eff.Console (log) +import Node.Encoding +import Node.Buffer as Buffer +import Node.Path as Path +import Node.Stream as Stream +import Unsafe.Coerce + +import Node.FS +import Node.FS.Stats +import Node.FS.Stream +import Node.FS.Sync as Sync + +main = do + let fp = Path.concat + + log "Testing streams" + + r <- createReadStream (fp ["test", "Streams.purs"]) + w <- createWriteStream (fp ["tmp", "Streams.purs"]) + + Stream.pipe r w + + Stream.onEnd r do + src <- Sync.readTextFile UTF8 (fp ["test", "Streams.purs"]) + dst <- Sync.readTextFile UTF8 (fp ["tmp", "Streams.purs"]) + + if src == dst + then log "all good" + else log "not good" From 3373834aa5a269e2800f80ce6837103e11c059b7 Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Tue, 15 Dec 2015 10:45:57 +0100 Subject: [PATCH 2/2] Remove some unnecessary FFI implementations --- src/Node/FS/Async.js | 2 -- src/Node/FS/Async.purs | 3 ++- src/Node/FS/Sync.js | 17 ----------------- src/Node/FS/Sync.purs | 3 ++- 4 files changed, 4 insertions(+), 21 deletions(-) delete mode 100644 src/Node/FS/Sync.js diff --git a/src/Node/FS/Async.js b/src/Node/FS/Async.js index 792891f..c47cf76 100644 --- a/src/Node/FS/Async.js +++ b/src/Node/FS/Async.js @@ -4,8 +4,6 @@ // module Node.FS.Async -exports.fs = require('fs'); - exports.handleCallbackImpl = function (left, right, f) { return function (err, value) { if (err) { diff --git a/src/Node/FS/Async.purs b/src/Node/FS/Async.purs index 64a7673..264321c 100644 --- a/src/Node/FS/Async.purs +++ b/src/Node/FS/Async.purs @@ -61,7 +61,7 @@ foreign import handleCallbackImpl :: handleCallback :: forall eff a. (Callback eff a) -> JSCallback a handleCallback cb = runFn3 handleCallbackImpl Left Right cb -foreign import fs :: +fs :: { rename :: Fn3 FilePath FilePath (JSCallback Unit) Unit , truncate :: Fn3 FilePath Int (JSCallback Unit) Unit , chown :: Fn4 FilePath Int Int (JSCallback Unit) Unit @@ -85,6 +85,7 @@ foreign import fs :: , write :: Fn6 FileDescriptor Buffer BufferOffset BufferLength (Nullable FilePosition) (JSCallback ByteCount) Unit , close :: Fn2 FileDescriptor (JSCallback Unit) Unit } +fs = unsafeRequireFS -- | Type synonym for callback functions. type Callback eff a = Either Error a -> Eff (fs :: FS | eff) Unit diff --git a/src/Node/FS/Sync.js b/src/Node/FS/Sync.js deleted file mode 100644 index 33fd190..0000000 --- a/src/Node/FS/Sync.js +++ /dev/null @@ -1,17 +0,0 @@ -/* global require */ -/* global exports */ -"use strict"; - -// module Node.FS.Sync - -exports.fs = require('fs'); - -exports.handleCallbackImpl = function (left, right, f) { - return function (err, value) { - if (err) { - f(left(err))(); - } else { - f(right(value))(); - } - }; -}; diff --git a/src/Node/FS/Sync.purs b/src/Node/FS/Sync.purs index a801638..b537920 100644 --- a/src/Node/FS/Sync.purs +++ b/src/Node/FS/Sync.purs @@ -49,7 +49,7 @@ import Node.Path (FilePath()) import Node.FS.Perms import Node.FS.Internal -foreign import fs :: +fs :: { renameSync :: Fn2 FilePath FilePath Unit , truncateSync :: Fn2 FilePath Int Unit , chownSync :: Fn3 FilePath Int Int Unit @@ -74,6 +74,7 @@ foreign import fs :: , fsyncSync :: Fn1 FileDescriptor Unit , closeSync :: Fn1 FileDescriptor Unit } +fs = unsafeRequireFS -- | Renames a file. rename :: forall eff. FilePath