]> git.immae.eu Git - github/fretlink/text-pipes.git/blame - Pipes/Text/IO.hs
IO documentation wibbles
[github/fretlink/text-pipes.git] / Pipes / Text / IO.hs
CommitLineData
bbdfd305 1{-#LANGUAGE RankNTypes#-}
0ac0c414 2
bbdfd305 3
4module Pipes.Text.IO
327be763 5 (
0ac0c414 6 -- * Text IO
7 -- $textio
8
9 -- * Caveats
10 -- $caveats
11
327be763 12 -- * Producers
13 fromHandle
14 , stdin
bbdfd305 15 , readFile
327be763 16 -- * Consumers
17 , toHandle
18 , stdout
bbdfd305 19 , writeFile
20 ) where
21
22import qualified System.IO as IO
23import Control.Exception (throwIO, try)
24import Foreign.C.Error (Errno(Errno), ePIPE)
25import qualified GHC.IO.Exception as G
26import Data.Text (Text)
27import qualified Data.Text as T
28import qualified Data.Text.IO as T
29import Pipes
30import qualified Pipes.Safe.Prelude as Safe
31import qualified Pipes.Safe as Safe
32import Pipes.Safe (MonadSafe(..), Base(..))
33import Prelude hiding (readFile, writeFile)
34
0ac0c414 35{- $textio
36 Where pipes IO replaces lazy IO, @Producer Text m r@ replaces lazy 'Text'.
37 This module exports some convenient functions for producing and consuming
81074089 38 pipes 'Text' in IO, namely, 'readFile', 'writeFile', 'fromHandle', 'toHandle',
39 'stdin' and 'stdout'. Some caveats described below.
0ac0c414 40
4ea59a8b 41 The main points are as in
42 <https://hackage.haskell.org/package/pipes-bytestring-1.0.0/docs/Pipes-ByteString.html Pipes.ByteString>
43
44 An 'IO.Handle' can be associated with a 'Producer' or 'Consumer' according
45 as it is read or written to.
81074089 46
0ac0c414 47> import Pipes
48> import qualified Pipes.Text as Text
49> import qualified Pipes.Text.IO as Text
50> import System.IO
51>
52> main =
53> withFile "inFile.txt" ReadMode $ \hIn ->
54> withFile "outFile.txt" WriteMode $ \hOut ->
55> runEffect $ Text.fromHandle hIn >-> Text.toHandle hOut
56
57To stream from files, the following is perhaps more Prelude-like (note that it uses Pipes.Safe):
58
59> import Pipes
60> import qualified Pipes.Text as Text
61> import qualified Pipes.Text.IO as Text
62> import Pipes.Safe
63>
64> main = runSafeT $ runEffect $ Text.readFile "inFile.txt" >-> Text.writeFile "outFile.txt"
65
81074089 66 Finally, you can stream to and from 'stdin' and 'stdout' using the predefined 'stdin'
0ac0c414 67 and 'stdout' pipes, as with the following \"echo\" program:
68
69> main = runEffect $ Text.stdin >-> Text.stdout
70
81074089 71
0ac0c414 72-}
73
74
75{- $caveats
76
77 The operations exported here are a convenience, like the similar operations in
78 @Data.Text.IO@ (or rather, @Data.Text.Lazy.IO@, since, again, @Producer Text m r@ is
79 'effectful text' and something like the pipes equivalent of lazy Text.)
80
81 * Like the functions in @Data.Text.IO@, they attempt to work with the system encoding.
82
a4913c42 83 * Like the functions in @Data.Text.IO@, they significantly slower than ByteString operations. Where
0ac0c414 84 you know what encoding you are working with, use @Pipes.ByteString@ and @Pipes.Text.Encoding@ instead,
85 e.g. @view utf8 Bytes.stdin@ instead of @Text.stdin@
86
a4913c42 87 * Like the functions in @Data.Text.IO@ , they use Text exceptions, not the standard Pipes protocols.
0ac0c414 88
89 Something like
90
91> view utf8 . Bytes.fromHandle :: Handle -> Producer Text IO (Producer ByteString m ())
92
93 yields a stream of Text, and follows
94 standard pipes protocols by reverting to (i.e. returning) the underlying byte stream
95 upon reaching any decoding error. (See especially the pipes-binary package.)
96
97 By contrast, something like
98
99> Text.fromHandle :: Handle -> Producer Text IO ()
100
101 supplies a stream of text returning '()', which is convenient for many tasks,
102 but violates the pipes @pipes-binary@ approach to decoding errors and
103 throws an exception of the kind characteristic of the @text@ library instead.
104
105
106-}
bbdfd305 107
108{-| Convert a 'IO.Handle' into a text stream using a text size
327be763 109 determined by the good sense of the text library. Note with the remarks
110 at the head of this module that this
111 is slower than @view utf8 (Pipes.ByteString.fromHandle h)@
112 but uses the system encoding and has other nice @Data.Text.IO@ features
bbdfd305 113-}
114
3f76b550 115fromHandle :: MonadIO m => IO.Handle -> Producer Text m ()
bbdfd305 116fromHandle h = go where
117 go = do txt <- liftIO (T.hGetChunk h)
118 if T.null txt then return ()
119 else do yield txt
120 go
121{-# INLINABLE fromHandle#-}
122
327be763 123-- | Stream text from 'stdin'
3f76b550 124stdin :: MonadIO m => Producer Text m ()
327be763 125stdin = fromHandle IO.stdin
126{-# INLINE stdin #-}
127
bbdfd305 128
129{-| Stream text from a file in the simple fashion of @Data.Text.IO@
130
131>>> runSafeT $ runEffect $ Text.readFile "hello.hs" >-> Text.map toUpper >-> hoist lift Text.stdout
132MAIN = PUTSTRLN "HELLO WORLD"
133-}
134
3f76b550 135readFile :: MonadSafe m => FilePath -> Producer Text m ()
bbdfd305 136readFile file = Safe.withFile file IO.ReadMode fromHandle
137{-# INLINE readFile #-}
138
139
140{-| Stream text to 'stdout'
141
142 Unlike 'toHandle', 'stdout' gracefully terminates on a broken output pipe.
143
144 Note: For best performance, it might be best just to use @(for source (liftIO . putStr))@
145 instead of @(source >-> stdout)@ .
146-}
147stdout :: MonadIO m => Consumer' Text m ()
148stdout = go
149 where
150 go = do
151 txt <- await
152 x <- liftIO $ try (T.putStr txt)
153 case x of
154 Left (G.IOError { G.ioe_type = G.ResourceVanished
155 , G.ioe_errno = Just ioe })
156 | Errno ioe == ePIPE
157 -> return ()
158 Left e -> liftIO (throwIO e)
159 Right () -> go
160{-# INLINABLE stdout #-}
161
162
163{-| Convert a text stream into a 'Handle'
164
165 Note: again, for best performance, where possible use
166 @(for source (liftIO . hPutStr handle))@ instead of @(source >-> toHandle handle)@.
167-}
168toHandle :: MonadIO m => IO.Handle -> Consumer' Text m r
169toHandle h = for cat (liftIO . T.hPutStr h)
170{-# INLINABLE toHandle #-}
171
172{-# RULES "p >-> toHandle h" forall p h .
173 p >-> toHandle h = for p (\txt -> liftIO (T.hPutStr h txt))
174 #-}
175
176
177-- | Stream text into a file. Uses @pipes-safe@.
178writeFile :: (MonadSafe m) => FilePath -> Consumer' Text m ()
179writeFile file = Safe.withFile file IO.WriteMode toHandle
180{-# INLINE writeFile #-}