{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE PatternSynonyms #-}

-- | A streaming library build around the 'Jet' type, which behaves as a kind of \"effectful list\".
--
-- For example, here's a way to print the first ten lines of a file to @stdout@:
--
-- >>> action = J.jet @Line (File "foo.txt") & J.limit 10 & J.sink stdout
--
-- The code is using the 'J.jet' function to create a 'Jet' of 'Line' values
-- (read using the default system encoding). 'J.jet' is part of the
-- 'J.JetSource' helper typeclass. Meanwhile, 'J.sink' is part of the
-- complementary 'J.JetSink' typeclass.
--
-- Note also the use of '(&)', which is simply a flipped '($)'. I've found it
-- useful to define forward-chained pipelines.
--
-- If instead of printing to @stdout@ we wanted to store the lines in a list:
--
-- >>> action = J.jet @Line (File "foo.txt") & J.limit 10 & J.toList
--
-- Imagine we wanted to print the combined lines of two files, excepting the
-- first 10 lines of each:
--
-- >>> :{
-- action =
--  do file <- J.each [File "foo.txt", File "bar.txt"]
--     jet @Line file & J.drop 10
--  & J.sink stdout
-- :}
--
-- Here we are making use of the 'Monad' instance of 'Jet', which resembles
-- that of conventional lists. We are mixing monadic do-blocks and conventional
-- function application. Also we use 'J.each', a function which creates a 'Jet'
-- out of any 'Foldable' container.
--
-- 'Jet's are 'Monoid's too, so we could have written:
--
-- >>> action = [File "foo.txt", File "bar.txt"] & foldMap (J.drop 10 . J.jet @Line) & J.sink stdout
--
-- Here's an interesting use of 'sink'. Imagine we have a big utf8-encoded file
-- and we want to split it into a number of files of no more than 100000 bytes
-- each, with the extra condition that we don't want to split any line between
-- two files. We could do it like this:
--
-- >>> :{
-- action =
--    let buckets = BoundedSize 100000 . File . ("result.txt." ++) . show <$> [1..]
--     in jet (File "12999.txt.utf-8")
--        & J.decodeUtf8
--        & J.lines
--      <&> (\line -> J.lineToUtf8 line <> J.textToUtf8 J.newline)
--        & J.sink buckets
-- :}
--
-- In this example we aren't using the default system encoding: instead of
-- that, we are reading bytes, explicity decoding them with 'J.decodeUtf8' and
-- finding 'J.lines'. Then we create a 'ByteBundle' for each 'Line' to signify
-- that it shouldn't be broken, and end by writing to a sequence of
-- 'BoundedSize' 'File's.
module Jet
  ( -- * The Jet type
    Jet,
    J.run,
    J.consume,
    J.drain,
    -- J.flatMap,

    -- * Building Jets
    J.each,
    J.repeat,
    J.repeatIO,
    J.replicate,
    J.replicateIO,
    J.iterate,
    J.iterateIO,
    J.unfold,
    J.unfoldIO,
    J.untilEOF,
    J.untilNothing,

    -- * List-like functions
    -- $listlike
    J.toList,
    J.length,
    J.traverse,
    J.traverse_,
    J.for,
    J.for_,
    J.filter,
    J.filterIO,
    J.take,
    J.limit,
    J.takeWhile,
    J.takeWhileIO,
    J.drop,
    J.dropWhile,
    J.dropWhileIO,
    J.mapAccum,
    J.mapAccumIO,
    J.intersperse,

    -- * Zips
    -- $zips
    J.zip,
    J.zipWith,
    J.zipIO,
    J.zipWithIO,

    -- * Control operations
    -- $control
    J.withFile,
    J.bracket,
    J.bracket_,
    J.bracketOnError,
    J.finally,
    J.onException,

    -- ** Building your own
    -- $doityourself
    J.control,
    J.control_,

    -- * Folding Jets
    -- $folding
    J.fold,
    J.foldIO,

    -- * Byte utils
    J.bytes,
    J.ChunkSize (..),
    J.ByteBundle,
    J.bundle,
    J.bundleLength,
    J.bundleBytes,

    -- * Text and line utils
    J.decodeUtf8,
    J.encodeUtf8,
    J.Line (Line),
    J.lines,
    J.unlines,
    J.newline,
    J.lineToText,
    J.lineToUtf8,
    J.textToLine,
    J.textToUtf8,
    J.stringToLine,
    J.lineContains,
    J.lineBeginsWith,
    J.prefixLine,

    -- * Concurrency
    traverseConcurrently,
    PoolConf,
    defaults,
    inputQueueSize,
    numberOfWorkers,
    outputQueueSize,

    -- * Process invocation
    throughProcess,
    linesThroughProcess,
    utf8LinesThroughProcess,
    ProcConf,
    bufferStdin,
    readFromStderr,
    handleExitCode,

    -- * Conversion helpers
    J.JetSource (..),
    J.JetSink (..),
    J.Sink (..),
    J.File (..),
    J.BoundedSize (..),
    J.BucketOverflow (..),

    -- * Some complicated stuff
    -- $complicated
    recast,
    Splitter (..),
    MealyIO (..),
    SplitStepResult (..),
    bytesOverBuckets,
    byteBundlesOverBuckets,
    Combiners,
    combiners,
    withCombiners,
    withCombiners_,
    combineIntoLists,

    -- * Re-exports
    -- $pipelines
    (&),
    (<&>),
    -- $standardstreams
    stdin,
    stdout,
    stderr,
    -- $exceptions
    T.UnicodeException,
    -- $process
    proc,
    shell,
  )
where

import Data.Function ((&))
import Data.Functor ((<&>))
import Data.Text.Encoding.Error qualified as T
import Jet.Internal
import Jet.Internal qualified as J
import System.IO (stderr, stdin, stdout)
import System.Process

-- $setup
--
-- >>> :set -XTypeApplications
-- >>> :set -XImportQualifiedPost
-- >>> :set -XScopedTypeVariables
-- >>> :set -XLambdaCase
-- >>> :set -XNumDecimals
-- >>> import Jet (Jet, (&))
-- >>> import Jet qualified as J
-- >>> import Control.Foldl qualified as L
-- >>> import Control.Concurrent
-- >>> import Data.IORef
-- >>> import Data.Text qualified as T

-- $zips
--
-- It's not possible to zip two 'Jet's together. But 'Jet's can be zipped with
-- pure lists, or with lists of 'IO' actions.

-- $complicated
--
-- I didn't manage to make this stuff simpler.

-- $pipelines
-- I've found that the 'Data.Function.&' (reverse application) and 'Data.Functor.<&>' (reverse 'fmap')
-- operators feel quite natural for building pipelines.

-- $standardstreams
-- The standard streams, useful with functions like 'sink'.

-- $exceptions
-- Thrown when decoding UTF8.

-- $process
-- Functions that create process specs for use with 'throughProcess'. For more control, import the whole of "System.Process".

-- $folding These functions can be used directly, but they're also useful for
-- interfacing with the @Applicative@ folds from the
-- [foldl](https://hackage.haskell.org/package/foldl) library, with the help of
-- functions like @Control.Foldl.purely@ and @Control.Foldl.impurely@.
--
-- @Applicative@ folds are useful because they let you run multiple
-- \"analyses\" of a 'Jet' while going through it only once.

-- $doityourself
-- These are for advanced usage.
--
-- Sometimes we want to lift some existing
-- resource-handling operation not already covered, one that works with plain
-- 'IO' values. These functions help with that.

-- $control
-- Some 'Jet's must allocate resources to do its work. For example, opening a
-- text file and yielding its lines. These resources must be promptly released
-- when the 'Jet' itself finishes or the consumers stops it (for example, by
-- using 'limit' on the 'Jet'). They must also be released in the face of
-- exceptions.
--
-- Here are various control operations like those from "Control.Exception", but
-- lifted to work on 'Jet's.
--
-- When put in a do-block, these operations \"protect\" every statement in the
-- do-block below the operation itself.

-- $listlike
--
-- In these functions, the 'Jet' is working as a kind of \"effectful list\".
-- The effects which produce the elements, and the effects with which we
-- transform and consume the elements, are always 'IO' effects.
--
-- Don't confuse these functions with similarly named functions from
-- 'Data.Traversable' or 'Control.Monad', for which 'Jet' doesn't work as the
-- \"container\", but as the Applicative/Monadic effect itself.