git.iko.soy/typeable/cassava-streams
on trunk help sign in
Add a tutorial
commit7f196342947ee11ecc935ad4da5fcaafe6804d3e authorPeter Jones <pjones@pmade.com> dateTue 22 Apr 2014, 12:07 (12 years ago) parent5df9b59
3 files changed+152−11
cassava-streams.cabal+4 src/System/IO/Streams/Csv.hs+16−11 src/System/IO/Streams/Csv/Tutorial.hs+132
cassava-streams.cabal+4
@@ -16,6 +16,8 @@ bug-reports: https://github.com/pjones/cassava-streams/issues
1616description:
1717 The cassava-streams library glues togeter the cassava CSV library
1818 and the io-streams streaming library.
19+ .
20+ See the "System.IO.Streams.Csv.Tutorial" module for a simple tutorial.
1921
2022--------------------------------------------------------------------------------
2123extra-source-files:
@@ -36,6 +38,7 @@ flag maintainer
3638library
3739 exposed-modules:
3840 System.IO.Streams.Csv
41+ System.IO.Streams.Csv.Tutorial
3942 other-modules:
4043 System.IO.Streams.Csv.Encode
4144 System.IO.Streams.Csv.Decode
@@ -52,3 +55,4 @@ library
5255 , bytestring >= 0.10 && < 1.0
5356 , cassava >= 0.3 && < 0.4
5457 , io-streams >= 1.1.0 && < 2.0
58+ , vector >= 0.10 && < 1.0
src/System/IO/Streams/Csv.hs+16−11
@@ -13,19 +13,24 @@ file.
1313--------------------------------------------------------------------------------
1414-- | This module exports functions which can be used to read instances
1515-- of the cassava classes @FromRecord@ and @FromNamedRecord@ from an
16−-- io-streams @InputStream ByteString@. It also exports functions
17−-- which can write instances of @ToRecord@ and @ToNamedRecord@ to an
18−-- io-streams @OutputStream ByteString@.
16+-- io-streams @InputStream ByteString@.
17+--
18+-- It also exports functions which can write instances of @ToRecord@
19+-- and @ToNamedRecord@ to an io-streams @OutputStream ByteString@.
20+--
21+-- See the "System.IO.Streams.Csv.Tutorial" module for a simple tutorial.
1922module System.IO.Streams.Csv
20− ( -- * Decoding
21− decodeStream
22− , decodeStreamWith
23− , decodeStreamByName
24− , decodeStreamByNameWith
23+ ( -- * Decoding CSV
24+ -- | These functions convert an io-streams @InputStream
25+ -- ByteString@ stream into one that decodes CSV records and
26+ -- produces these decoded records.
27+ module System.IO.Streams.Csv.Decode
2528
26− -- * Encoding
27− , encodeStream
28− , encodeStreamByName
29+ -- * Encoding CSV
30+ -- | These functions convert an io-streams @OutputStream
31+ -- ByteString@ stream into one that encodes records into CSV
32+ -- format before sending them downstream.
33+ , module System.IO.Streams.Csv.Encode
2934 ) where
3035
3136--------------------------------------------------------------------------------
src/System/IO/Streams/Csv/Tutorial.hsnew file+132
@@ -0,0 +1,132 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+
3+{-
4+
5+This file is part of the Haskell package cassava-streams. It is
6+subject to the license terms in the LICENSE file found in the
7+top-level directory of this distribution and at
8+git://pmade.com/cassava-streams/LICENSE. No part of cassava-streams
9+package, including this file, may be copied, modified, propagated, or
10+distributed except according to the terms contained in the LICENSE
11+file.
12+
13+-}
14+
15+--------------------------------------------------------------------------------
16+-- | A simple tutorial on using the cassava-streams library to glue
17+-- together cassava and io-streams.
18+--
19+-- Note: if you're reading this on Hackage or in Haddock then you
20+-- should switch to source view with the \"Source\" link at the top of
21+-- this page or open this file in your favorite text editor.
22+module System.IO.Streams.Csv.Tutorial
23+ ( -- * Types representing to-do items and their state
24+ Item (..)
25+ , TState (..)
26+
27+ -- * Functions which use cassava-streams functions
28+ , onlyTodo
29+ , markDone
30+ ) where
31+
32+--------------------------------------------------------------------------------
33+import Control.Applicative
34+import Control.Monad
35+import Data.Csv
36+import qualified Data.Vector as V
37+import System.IO
38+import qualified System.IO.Streams as Streams
39+import System.IO.Streams.Csv
40+
41+--------------------------------------------------------------------------------
42+-- | A to-do item.
43+data Item = Item
44+ { title :: String
45+ , state :: TState
46+ } deriving (Show, Eq)
47+
48+instance FromNamedRecord Item where
49+ parseNamedRecord m = Item <$> m .: "Title"
50+ <*> m .: "State"
51+
52+instance ToNamedRecord Item where
53+ toNamedRecord (Item t s) =
54+ namedRecord [ "Title" .= t
55+ , "State" .= s
56+ ]
57+
58+--------------------------------------------------------------------------------
59+-- | Possible states for a to-do item.
60+data TState = Todo -- ^ Item needs to be completed.
61+ | Done -- ^ Item has been finished.
62+ deriving (Show, Eq)
63+
64+instance FromField TState where
65+ parseField "TODO" = return Todo
66+ parseField "DONE" = return Done
67+ parseField _ = mzero
68+
69+instance ToField TState where
70+ toField Todo = "TODO"
71+ toField Done = "DONE"
72+
73+--------------------------------------------------------------------------------
74+-- | The @onlyTodo@ function reads to-do 'Item's from the given input
75+-- handle (in CSV format) and writes them back to the output handle
76+-- (also in CSV format), but only if the items are in the @Todo@
77+-- state. In another words, the CSV data is filtered so that the
78+-- output handle only receives to-do 'Item's which haven't been
79+-- completed.
80+--
81+-- The io-streams @handleToInputStream@ function is used to create an
82+-- @InputStream ByteString@ stream from the given input handle.
83+--
84+-- That stream is then given to the cassava-streams function
85+-- 'decodeStreamByName' which converts the @InputStream ByteString@
86+-- stream into an @InputStream Item@ stream.
87+--
88+-- Finally the io-streams @filter@ function is used to filter the
89+-- input stream so that it only produces to-do items which haven't
90+-- been completed.
91+onlyTodo :: Handle -- ^ Input handle where CSV data can be read.
92+ -> Handle -- ^ Output handle where CSV data can be written.
93+ -> IO ()
94+onlyTodo inH outH = do
95+ -- A stream which produces items which are not 'Done'.
96+ input <- Streams.handleToInputStream inH >>=
97+ decodeStreamByName >>=
98+ Streams.filter (\item -> state item == Done)
99+
100+ -- A stream to write items into. They will be converted to CSV.
101+ output <- Streams.handleToOutputStream outH >>=
102+ encodeStreamByName (V.fromList ["State", "Title"])
103+
104+ -- Connect the input and output streams.
105+ Streams.connect input output
106+
107+--------------------------------------------------------------------------------
108+-- | The @markDone@ function will read to-do items from the given
109+-- input handle and mark any matching items as @Done@. All to-do
110+-- items are written to the given output handle.
111+markDone :: String -- ^ Items with this title are marked as @Done@.
112+ -> Handle -- ^ Input handle where CSV data can be read.
113+ -> Handle -- ^ Output handle where CSV data can be written.
114+ -> IO ()
115+markDone titleOfItem inH outH = do
116+ -- Change matching items to the 'Done' state.
117+ let markDone' item = if title item == titleOfItem
118+ then item {state = Done}
119+ else item
120+
121+ -- A stream which produces items and converts matching items to the
122+ -- 'Done' state.
123+ input <- Streams.handleToInputStream inH >>=
124+ decodeStreamByName >>=
125+ Streams.map markDone'
126+
127+ -- A stream to write items into. They will be converted to CSV.
128+ output <- Streams.handleToOutputStream outH >>=
129+ encodeStreamByName (V.fromList ["State", "Title"])
130+
131+ -- Connect the input and output streams.
132+ Streams.connect input output