fix: generalize parser/stringifier to MonadAff

This commit is contained in:
orion kindel 2024-05-02 11:59:50 -05:00
parent 03cc9eba28
commit 1eb6f2242f
Signed by: orion
GPG Key ID: 6D4165AE4C928719
2 changed files with 15 additions and 13 deletions

View File

@ -6,7 +6,7 @@ import Control.Alt ((<|>))
import Control.Monad.Error.Class (liftEither) import Control.Monad.Error.Class (liftEither)
import Control.Monad.Except (runExcept) import Control.Monad.Except (runExcept)
import Control.Monad.Maybe.Trans (MaybeT(..), runMaybeT) import Control.Monad.Maybe.Trans (MaybeT(..), runMaybeT)
import Control.Monad.Rec.Class (whileJust) import Control.Monad.Rec.Class (class MonadRec, whileJust)
import Control.Monad.ST.Global as ST import Control.Monad.ST.Global as ST
import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Class (lift)
import Data.Array as Array import Data.Array as Array
@ -16,18 +16,19 @@ import Data.CSV.Record (class ReadCSVRecord, readCSVRecord)
import Data.Either (Either(..)) import Data.Either (Either(..))
import Data.Filterable (filter) import Data.Filterable (filter)
import Data.Map (Map) import Data.Map (Map)
import Data.Map as Map
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Data.Nullable (Nullable) import Data.Nullable (Nullable)
import Data.Nullable as Nullable import Data.Nullable as Nullable
import Data.Traversable (for_) import Data.Traversable (for_)
import Effect (Effect) import Effect (Effect)
import Effect.Aff (Aff, makeAff) import Effect.Aff (makeAff)
import Effect.Aff.Class (class MonadAff, liftAff)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Effect.Exception (error) import Effect.Exception (error)
import Effect.Uncurried (mkEffectFn1) import Effect.Uncurried (mkEffectFn1)
import Foreign (Foreign, unsafeToForeign) import Foreign (Foreign, unsafeToForeign)
import Foreign.Object (Object) import Foreign.Object (Object)
import Data.Map as Map
import Foreign.Object as Object import Foreign.Object as Object
import Node.Encoding (Encoding(..)) import Node.Encoding (Encoding(..))
import Node.EventEmitter (EventHandle(..)) import Node.EventEmitter (EventHandle(..))
@ -86,7 +87,7 @@ make :: forall @r rl @config @missing @extra. RowToList r rl => ReadCSVRecord r
make = makeImpl <<< unsafeToForeign <<< Object.union (recordToForeign {columns: false, cast: false, cast_date: false}) <<< recordToForeign make = makeImpl <<< unsafeToForeign <<< Object.union (recordToForeign {columns: false, cast: false, cast_date: false}) <<< recordToForeign
-- | Synchronously parse a CSV string -- | Synchronously parse a CSV string
parse :: forall @r rl @config missing extra. RowToList r rl => ReadCSVRecord r rl => Union config missing (Config extra) => { | config } -> String -> Aff (Array { | r }) parse :: forall @r rl @config missing extra m. MonadAff m => MonadRec m => RowToList r rl => ReadCSVRecord r rl => Union config missing (Config extra) => { | config } -> String -> m (Array { | r })
parse config csv = do parse config csv = do
stream <- liftEffect $ make @r @config @missing @extra config stream <- liftEffect $ make @r @config @missing @extra config
void $ liftEffect $ Stream.writeString stream UTF8 csv void $ liftEffect $ Stream.writeString stream UTF8 csv
@ -94,10 +95,10 @@ parse config csv = do
readAll stream readAll stream
-- | Loop until the stream is closed, invoking the callback with each record as it is parsed. -- | Loop until the stream is closed, invoking the callback with each record as it is parsed.
foreach :: forall @r rl x. RowToList r rl => ReadCSVRecord r rl => CSVParser r x -> ({ | r } -> Aff Unit) -> Aff Unit foreach :: forall @r rl x m. MonadRec m => MonadAff m => RowToList r rl => ReadCSVRecord r rl => CSVParser r x -> ({ | r } -> m Unit) -> m Unit
foreach stream cb = whileJust do foreach stream cb = whileJust do
isReadable <- liftEffect $ Stream.readable stream isReadable <- liftEffect $ Stream.readable stream
when (not isReadable) $ makeAff \res -> mempty <* flip (Event.once Stream.readableH) stream $ res $ Right unit liftAff $ when (not isReadable) $ makeAff \res -> mempty <* flip (Event.once Stream.readableH) stream $ res $ Right unit
whileJust do whileJust do
r <- liftEffect $ read @r stream r <- liftEffect $ read @r stream
for_ r cb for_ r cb
@ -117,7 +118,7 @@ read stream = runMaybeT do
liftEither $ lmap (error <<< show) $ runExcept $ readCSVRecord @r @rl cols raw liftEither $ lmap (error <<< show) $ runExcept $ readCSVRecord @r @rl cols raw
-- | Collect all parsed records into an array -- | Collect all parsed records into an array
readAll :: forall @r rl a. RowToList r rl => ReadCSVRecord r rl => CSVParser r a -> Aff (Array { | r }) readAll :: forall @r rl a m. MonadRec m => MonadAff m => RowToList r rl => ReadCSVRecord r rl => CSVParser r a -> m (Array { | r })
readAll stream = do readAll stream = do
records <- liftEffect $ ST.toEffect $ Array.ST.new records <- liftEffect $ ST.toEffect $ Array.ST.new
foreach stream $ void <<< liftEffect <<< ST.toEffect <<< flip Array.ST.push records foreach stream $ void <<< liftEffect <<< ST.toEffect <<< flip Array.ST.push records

View File

@ -2,7 +2,7 @@ module Node.Stream.CSV.Stringify where
import Prelude import Prelude
import Control.Monad.Rec.Class (whileJust) import Control.Monad.Rec.Class (class MonadRec, whileJust)
import Control.Monad.ST.Global as ST import Control.Monad.ST.Global as ST
import Data.Array as Array import Data.Array as Array
import Data.Array.ST as Array.ST import Data.Array.ST as Array.ST
@ -13,7 +13,8 @@ import Data.Maybe (Maybe(..))
import Data.String.Regex (Regex) import Data.String.Regex (Regex)
import Data.Traversable (for_) import Data.Traversable (for_)
import Effect (Effect) import Effect (Effect)
import Effect.Aff (Aff, makeAff) import Effect.Aff (makeAff)
import Effect.Aff.Class (class MonadAff, liftAff)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Foreign (Foreign, unsafeToForeign) import Foreign (Foreign, unsafeToForeign)
import Foreign.Object (Object) import Foreign.Object (Object)
@ -67,7 +68,7 @@ make :: forall @r rl @config @missing @extra. Keys rl => RowToList r rl => Write
make = makeImpl <<< unsafeToForeign <<< Object.union (recordToForeign {columns: Array.fromFoldable $ keys (Proxy @r)}) <<< recordToForeign make = makeImpl <<< unsafeToForeign <<< Object.union (recordToForeign {columns: Array.fromFoldable $ keys (Proxy @r)}) <<< recordToForeign
-- | Synchronously stringify a collection of records -- | Synchronously stringify a collection of records
stringify :: forall @r rl f @config missing extra. Keys rl => Foldable f => RowToList r rl => WriteCSVRecord r rl => Union config missing (Config extra) => { | config } -> f { | r } -> Aff String stringify :: forall @r rl f m @config missing extra. MonadAff m => MonadRec m => Keys rl => Foldable f => RowToList r rl => WriteCSVRecord r rl => Union config missing (Config extra) => { | config } -> f { | r } -> m String
stringify config records = do stringify config records = do
stream <- liftEffect $ make @r @config @missing @extra config stream <- liftEffect $ make @r @config @missing @extra config
liftEffect $ for_ records \r -> write stream r liftEffect $ for_ records \r -> write stream r
@ -82,10 +83,10 @@ write :: forall @r rl a. RowToList r rl => WriteCSVRecord r rl => CSVStringifier
write s = writeImpl s <<< writeCSVRecord @r @rl write s = writeImpl s <<< writeCSVRecord @r @rl
-- | Loop until the stream is closed, invoking the callback with each chunk of stringified CSV text. -- | Loop until the stream is closed, invoking the callback with each chunk of stringified CSV text.
foreach :: forall r x. CSVStringifier r x -> (String -> Aff Unit) -> Aff Unit foreach :: forall m r x. MonadAff m => MonadRec m => CSVStringifier r x -> (String -> m Unit) -> m Unit
foreach stream cb = whileJust do foreach stream cb = whileJust do
isReadable <- liftEffect $ Stream.readable stream isReadable <- liftEffect $ Stream.readable stream
when (not isReadable) $ makeAff \res -> mempty <* flip (Event.once Stream.readableH) stream $ res $ Right unit liftAff $ when (not isReadable) $ makeAff \res -> mempty <* flip (Event.once Stream.readableH) stream $ res $ Right unit
whileJust do whileJust do
s <- liftEffect $ (join <<< map blush) <$> Stream.readEither stream s <- liftEffect $ (join <<< map blush) <$> Stream.readEither stream
for_ s cb for_ s cb
@ -94,7 +95,7 @@ foreach stream cb = whileJust do
pure $ if isClosed then Nothing else Just unit pure $ if isClosed then Nothing else Just unit
-- | Read the stringified chunks until end-of-stream, returning the entire CSV string. -- | Read the stringified chunks until end-of-stream, returning the entire CSV string.
readAll :: forall r a. CSVStringifier r a -> Aff String readAll :: forall r a m. MonadAff m => MonadRec m => CSVStringifier r a -> m String
readAll stream = do readAll stream = do
chunks <- liftEffect $ ST.toEffect $ Array.ST.new chunks <- liftEffect $ ST.toEffect $ Array.ST.new
foreach stream $ void <<< liftEffect <<< ST.toEffect <<< flip Array.ST.push chunks foreach stream $ void <<< liftEffect <<< ST.toEffect <<< flip Array.ST.push chunks