{-# LANGUAGE DeriveDataTypeable    #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE RankNTypes            #-}
{-# LANGUAGE TypeFamilies          #-}
module Text.ProjectTemplate
    ( -- * Create a template
      createTemplate
      -- * Unpack a template
    , unpackTemplate
      -- ** Receivers
    , FileReceiver
    , receiveMem
    , receiveFS
      -- * Exceptions
    , ProjectTemplateException (..)
    ) where

import           Control.Exception            (Exception, assert)
import           Control.Monad                (unless)
import           Control.Monad.IO.Class       (liftIO)
import           Control.Monad.Trans.Class    (lift)
import           Control.Monad.Trans.Resource (MonadResource, MonadThrow,
                                               throwM)
import           Control.Monad.Writer         (MonadWriter, tell)
import           Data.ByteString              (ByteString)
import qualified Data.ByteString              as S
import qualified Data.ByteString.Base64       as B64
import qualified Data.ByteString.Lazy         as L
import           Data.Conduit                 (ConduitM, await,
                                               awaitForever, leftover, yield,
                                               runConduit, (.|))
import qualified Data.Conduit.Binary          as CB
import           Data.Conduit.List            (consume, sinkNull)
import           Conduit                      (concatMapC, chunksOfCE)
import qualified Data.Conduit.List            as CL
import qualified Data.Conduit.Text            as CT
import           Data.Map                     (Map)
import qualified Data.Map                     as Map
import           Data.Text                    (Text)
import qualified Data.Text                    as T
import           Data.Text.Encoding           (encodeUtf8)
import           Data.Typeable                (Typeable)
import           Data.Void                    (Void)
import           System.Directory             (createDirectoryIfMissing)
import           System.FilePath              (takeDirectory, (</>))

-- | Create a template file from a stream of file/contents combinations.
--
-- Since 0.1.0
createTemplate
    :: Monad m => ConduitM (FilePath, m ByteString) ByteString m ()
createTemplate :: ConduitM (FilePath, m ByteString) ByteString m ()
createTemplate = ((FilePath, m ByteString)
 -> ConduitM (FilePath, m ByteString) ByteString m ())
-> ConduitM (FilePath, m ByteString) ByteString m ()
forall (m :: * -> *) i o r.
Monad m =>
(i -> ConduitT i o m r) -> ConduitT i o m ()
awaitForever (((FilePath, m ByteString)
  -> ConduitM (FilePath, m ByteString) ByteString m ())
 -> ConduitM (FilePath, m ByteString) ByteString m ())
-> ((FilePath, m ByteString)
    -> ConduitM (FilePath, m ByteString) ByteString m ())
-> ConduitM (FilePath, m ByteString) ByteString m ()
forall a b. (a -> b) -> a -> b
$ \(fp :: FilePath
fp, getBS :: m ByteString
getBS) -> do
    ByteString
bs <- m ByteString
-> ConduitT (FilePath, m ByteString) ByteString m ByteString
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m ByteString
getBS
    case ConduitT () Void Maybe () -> Maybe ()
forall (m :: * -> *) r. Monad m => ConduitT () Void m r -> m r
runConduit (ConduitT () Void Maybe () -> Maybe ())
-> ConduitT () Void Maybe () -> Maybe ()
forall a b. (a -> b) -> a -> b
$ ByteString -> ConduitT () ByteString Maybe ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield ByteString
bs ConduitT () ByteString Maybe ()
-> ConduitM ByteString Void Maybe () -> ConduitT () Void Maybe ()
forall (m :: * -> *) a b c r.
Monad m =>
ConduitM a b m () -> ConduitM b c m r -> ConduitM a c m r
.| Codec -> ConduitT ByteString Text Maybe ()
forall (m :: * -> *).
MonadThrow m =>
Codec -> ConduitT ByteString Text m ()
CT.decode Codec
CT.utf8 ConduitT ByteString Text Maybe ()
-> ConduitM Text Void Maybe () -> ConduitM ByteString Void Maybe ()
forall (m :: * -> *) a b c r.
Monad m =>
ConduitM a b m () -> ConduitM b c m r -> ConduitM a c m r
.| ConduitM Text Void Maybe ()
forall (m :: * -> *) i o. Monad m => ConduitT i o m ()
sinkNull of
        Nothing -> do
            ByteString -> ConduitM (FilePath, m ByteString) ByteString m ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield "{-# START_FILE BASE64 "
            ByteString -> ConduitM (FilePath, m ByteString) ByteString m ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield (ByteString -> ConduitM (FilePath, m ByteString) ByteString m ())
-> ByteString -> ConduitM (FilePath, m ByteString) ByteString m ()
forall a b. (a -> b) -> a -> b
$ Text -> ByteString
encodeUtf8 (Text -> ByteString) -> Text -> ByteString
forall a b. (a -> b) -> a -> b
$ FilePath -> Text
T.pack FilePath
fp
            ByteString -> ConduitM (FilePath, m ByteString) ByteString m ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield " #-}\n"
            ByteString -> ConduitM (FilePath, m ByteString) ByteString m ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield (ByteString -> ByteString
B64.encode ByteString
bs) ConduitM (FilePath, m ByteString) ByteString m ()
-> ConduitM ByteString ByteString m ()
-> ConduitM (FilePath, m ByteString) ByteString m ()
forall (m :: * -> *) a b c r.
Monad m =>
ConduitM a b m () -> ConduitM b c m r -> ConduitM a c m r
.| Index ByteString -> ConduitM ByteString ByteString m ()
forall (m :: * -> *) seq.
(Monad m, IsSequence seq) =>
Index seq -> ConduitT seq seq m ()
chunksOfCE 76 ConduitM ByteString ByteString m ()
-> ConduitM ByteString ByteString m ()
-> ConduitM ByteString ByteString m ()
forall (m :: * -> *) a b c r.
Monad m =>
ConduitM a b m () -> ConduitM b c m r -> ConduitM a c m r
.| (ByteString -> [ByteString])
-> ConduitT ByteString (Element [ByteString]) m ()
forall (m :: * -> *) mono a.
(Monad m, MonoFoldable mono) =>
(a -> mono) -> ConduitT a (Element mono) m ()
concatMapC (\x :: ByteString
x -> [ByteString
x, "\n"])
            ByteString -> ConduitM (FilePath, m ByteString) ByteString m ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield "\n"
        Just _ -> do
            ByteString -> ConduitM (FilePath, m ByteString) ByteString m ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield "{-# START_FILE "
            ByteString -> ConduitM (FilePath, m ByteString) ByteString m ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield (ByteString -> ConduitM (FilePath, m ByteString) ByteString m ())
-> ByteString -> ConduitM (FilePath, m ByteString) ByteString m ()
forall a b. (a -> b) -> a -> b
$ Text -> ByteString
encodeUtf8 (Text -> ByteString) -> Text -> ByteString
forall a b. (a -> b) -> a -> b
$ FilePath -> Text
T.pack FilePath
fp
            ByteString -> ConduitM (FilePath, m ByteString) ByteString m ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield " #-}\n"
            ByteString -> ConduitM (FilePath, m ByteString) ByteString m ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield ByteString
bs
            ByteString -> ConduitM (FilePath, m ByteString) ByteString m ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield "\n"

-- | Unpack a template to some destination. Destination is provided by the
-- first argument.
--
-- The second argument allows you to modify the incoming stream, usually to
-- replace variables. For example, to replace PROJECTNAME with myproject, you
-- could use:
--
-- > Data.Text.replace "PROJECTNAME" "myproject"
--
-- Note that this will affect both file contents and file names.
--
-- Since 0.1.0
unpackTemplate
    :: MonadThrow m
    => (FilePath -> ConduitM ByteString o m ()) -- ^ receive individual files
    -> (Text -> Text) -- ^ fix each input line, good for variables
    -> ConduitM ByteString o m ()
unpackTemplate :: (FilePath -> ConduitM ByteString o m ())
-> (Text -> Text) -> ConduitM ByteString o m ()
unpackTemplate perFile :: FilePath -> ConduitM ByteString o m ()
perFile fixLine :: Text -> Text
fixLine =
    Codec -> ConduitT ByteString Text m ()
forall (m :: * -> *).
MonadThrow m =>
Codec -> ConduitT ByteString Text m ()
CT.decode Codec
CT.utf8 ConduitT ByteString Text m ()
-> ConduitM Text o m () -> ConduitM ByteString o m ()
forall (m :: * -> *) a b c r.
Monad m =>
ConduitM a b m () -> ConduitM b c m r -> ConduitM a c m r
.| ConduitT Text Text m ()
forall (m :: * -> *). Monad m => ConduitT Text Text m ()
CT.lines ConduitT Text Text m ()
-> ConduitM Text o m () -> ConduitM Text o m ()
forall (m :: * -> *) a b c r.
Monad m =>
ConduitM a b m () -> ConduitM b c m r -> ConduitM a c m r
.| (Text -> Text) -> ConduitT Text Text m ()
forall (m :: * -> *) a b. Monad m => (a -> b) -> ConduitT a b m ()
CL.map Text -> Text
fixLine ConduitT Text Text m ()
-> ConduitM Text o m () -> ConduitM Text o m ()
forall (m :: * -> *) a b c r.
Monad m =>
ConduitM a b m () -> ConduitM b c m r -> ConduitM a c m r
.| ConduitM Text o m ()
start
  where
    start :: ConduitM Text o m ()
start =
        ConduitT Text o m (Maybe Text)
forall (m :: * -> *) i. Monad m => Consumer i m (Maybe i)
await ConduitT Text o m (Maybe Text)
-> (Maybe Text -> ConduitM Text o m ()) -> ConduitM Text o m ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ConduitM Text o m ()
-> (Text -> ConduitM Text o m ())
-> Maybe Text
-> ConduitM Text o m ()
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (() -> ConduitM Text o m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()) Text -> ConduitM Text o m ()
go
      where
        go :: Text -> ConduitM Text o m ()
go t :: Text
t =
            case Text -> Maybe (Text, Bool)
getFileName Text
t of
                Nothing -> m () -> ConduitM Text o m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> ConduitM Text o m ()) -> m () -> ConduitM Text o m ()
forall a b. (a -> b) -> a -> b
$ ProjectTemplateException -> m ()
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM (ProjectTemplateException -> m ())
-> ProjectTemplateException -> m ()
forall a b. (a -> b) -> a -> b
$ Text -> ProjectTemplateException
InvalidInput Text
t
                Just (fp' :: Text
fp', isBinary :: Bool
isBinary) -> do
                    let src :: ConduitM Text ByteString m ()
src
                            | Bool
isBinary  = ConduitM Text ByteString m ()
binaryLoop ConduitM Text ByteString m ()
-> ConduitM ByteString ByteString m ()
-> ConduitM Text ByteString m ()
forall (m :: * -> *) a b c r.
Monad m =>
ConduitM a b m () -> ConduitM b c m r -> ConduitM a c m r
.| ConduitM ByteString ByteString m ()
forall (m :: * -> *).
Monad m =>
ConduitM ByteString ByteString m ()
decode64
                            | Bool
otherwise = Bool -> ConduitM Text ByteString m ()
forall (m :: * -> *).
Monad m =>
Bool -> ConduitT Text ByteString m ()
textLoop Bool
True
                    ConduitM Text ByteString m ()
src ConduitM Text ByteString m ()
-> ConduitM ByteString o m () -> ConduitM Text o m ()
forall (m :: * -> *) a b c r.
Monad m =>
ConduitM a b m () -> ConduitM b c m r -> ConduitM a c m r
.| FilePath -> ConduitM ByteString o m ()
perFile (Text -> FilePath
T.unpack Text
fp')
                    ConduitM Text o m ()
start

    binaryLoop :: ConduitM Text ByteString m ()
binaryLoop = do
        ConduitT Text ByteString m (Maybe Text)
forall (m :: * -> *) i. Monad m => Consumer i m (Maybe i)
await ConduitT Text ByteString m (Maybe Text)
-> (Maybe Text -> ConduitM Text ByteString m ())
-> ConduitM Text ByteString m ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ConduitM Text ByteString m ()
-> (Text -> ConduitM Text ByteString m ())
-> Maybe Text
-> ConduitM Text ByteString m ()
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (() -> ConduitM Text ByteString m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()) Text -> ConduitM Text ByteString m ()
go
      where
        go :: Text -> ConduitM Text ByteString m ()
go t :: Text
t =
            case Text -> Maybe (Text, Bool)
getFileName Text
t of
                Just{} -> Text -> ConduitM Text ByteString m ()
forall i o (m :: * -> *). i -> ConduitT i o m ()
leftover Text
t
                Nothing -> do
                    ByteString -> ConduitM Text ByteString m ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield (ByteString -> ConduitM Text ByteString m ())
-> ByteString -> ConduitM Text ByteString m ()
forall a b. (a -> b) -> a -> b
$ Text -> ByteString
encodeUtf8 Text
t
                    ConduitM Text ByteString m ()
binaryLoop
    textLoop :: Bool -> ConduitT Text ByteString m ()
textLoop isFirst :: Bool
isFirst =
        ConduitT Text ByteString m (Maybe Text)
forall (m :: * -> *) i. Monad m => Consumer i m (Maybe i)
await ConduitT Text ByteString m (Maybe Text)
-> (Maybe Text -> ConduitT Text ByteString m ())
-> ConduitT Text ByteString m ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ConduitT Text ByteString m ()
-> (Text -> ConduitT Text ByteString m ())
-> Maybe Text
-> ConduitT Text ByteString m ()
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (() -> ConduitT Text ByteString m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()) Text -> ConduitT Text ByteString m ()
go
      where
        go :: Text -> ConduitT Text ByteString m ()
go t :: Text
t =
            case Text -> Maybe (Text, Bool)
getFileName Text
t of
                Just{} -> Text -> ConduitT Text ByteString m ()
forall i o (m :: * -> *). i -> ConduitT i o m ()
leftover Text
t
                Nothing -> do
                    Bool
-> ConduitT Text ByteString m () -> ConduitT Text ByteString m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
isFirst (ConduitT Text ByteString m () -> ConduitT Text ByteString m ())
-> ConduitT Text ByteString m () -> ConduitT Text ByteString m ()
forall a b. (a -> b) -> a -> b
$ ByteString -> ConduitT Text ByteString m ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield "\n"
                    ByteString -> ConduitT Text ByteString m ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield (ByteString -> ConduitT Text ByteString m ())
-> ByteString -> ConduitT Text ByteString m ()
forall a b. (a -> b) -> a -> b
$ Text -> ByteString
encodeUtf8 Text
t
                    Bool -> ConduitT Text ByteString m ()
textLoop Bool
False

    getFileName :: Text -> Maybe (Text, Bool)
getFileName t :: Text
t =
        case Text -> [Text]
T.words Text
t of
            ["{-#", "START_FILE", fn :: Text
fn, "#-}"] -> (Text, Bool) -> Maybe (Text, Bool)
forall a. a -> Maybe a
Just (Text
fn, Bool
False)
            ["{-#", "START_FILE", "BASE64", fn :: Text
fn, "#-}"] -> (Text, Bool) -> Maybe (Text, Bool)
forall a. a -> Maybe a
Just (Text
fn, Bool
True)
            _ -> Maybe (Text, Bool)
forall a. Maybe a
Nothing

-- | The first argument to 'unpackTemplate', specifying how to receive a file.
--
-- Since 0.1.0
type FileReceiver m = FilePath -> ConduitM ByteString Void m ()

-- | Receive files to the given folder on the filesystem.
--
-- > unpackTemplate (receiveFS "some-destination") (T.replace "PROJECTNAME" "foo")
--
-- Since 0.1.0
receiveFS :: MonadResource m
          => FilePath -- ^ root
          -> FileReceiver m
receiveFS :: FilePath -> FileReceiver m
receiveFS root :: FilePath
root rel :: FilePath
rel = do
    IO () -> ConduitT ByteString Void m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> ConduitT ByteString Void m ())
-> IO () -> ConduitT ByteString Void m ()
forall a b. (a -> b) -> a -> b
$ Bool -> FilePath -> IO ()
createDirectoryIfMissing Bool
True (FilePath -> IO ()) -> FilePath -> IO ()
forall a b. (a -> b) -> a -> b
$ FilePath -> FilePath
takeDirectory FilePath
fp
    FileReceiver m
forall (m :: * -> *) o.
MonadResource m =>
FilePath -> ConduitT ByteString o m ()
CB.sinkFile FilePath
fp
  where
    fp :: FilePath
fp = FilePath
root FilePath -> FilePath -> FilePath
</> FilePath
rel

-- | Receive files to a @Writer@ monad in memory.
--
-- > execWriter $ runExceptionT_ $ src $$ unpackTemplate receiveMem id
--
-- Since 0.1.0
receiveMem :: MonadWriter (Map FilePath L.ByteString) m
           => FileReceiver m
receiveMem :: FileReceiver m
receiveMem fp :: FilePath
fp = do
    [ByteString]
bss <- ConduitT ByteString Void m [ByteString]
forall (m :: * -> *) a o. Monad m => ConduitT a o m [a]
consume
    m () -> ConduitT ByteString Void m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> ConduitT ByteString Void m ())
-> m () -> ConduitT ByteString Void m ()
forall a b. (a -> b) -> a -> b
$ Map FilePath ByteString -> m ()
forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell (Map FilePath ByteString -> m ())
-> Map FilePath ByteString -> m ()
forall a b. (a -> b) -> a -> b
$ FilePath -> ByteString -> Map FilePath ByteString
forall k a. k -> a -> Map k a
Map.singleton FilePath
fp (ByteString -> Map FilePath ByteString)
-> ByteString -> Map FilePath ByteString
forall a b. (a -> b) -> a -> b
$ [ByteString] -> ByteString
L.fromChunks [ByteString]
bss

-- | Exceptions that can be thrown.
--
-- Since 0.1.0
data ProjectTemplateException = InvalidInput Text
                              | BinaryLoopNeedsOneLine
    deriving (Int -> ProjectTemplateException -> FilePath -> FilePath
[ProjectTemplateException] -> FilePath -> FilePath
ProjectTemplateException -> FilePath
(Int -> ProjectTemplateException -> FilePath -> FilePath)
-> (ProjectTemplateException -> FilePath)
-> ([ProjectTemplateException] -> FilePath -> FilePath)
-> Show ProjectTemplateException
forall a.
(Int -> a -> FilePath -> FilePath)
-> (a -> FilePath) -> ([a] -> FilePath -> FilePath) -> Show a
showList :: [ProjectTemplateException] -> FilePath -> FilePath
$cshowList :: [ProjectTemplateException] -> FilePath -> FilePath
show :: ProjectTemplateException -> FilePath
$cshow :: ProjectTemplateException -> FilePath
showsPrec :: Int -> ProjectTemplateException -> FilePath -> FilePath
$cshowsPrec :: Int -> ProjectTemplateException -> FilePath -> FilePath
Show, Typeable)
instance Exception ProjectTemplateException

decode64 :: Monad m => ConduitM ByteString ByteString m ()
decode64 :: ConduitM ByteString ByteString m ()
decode64 = Int
-> (ByteString -> ByteString)
-> ConduitM ByteString ByteString m ()
forall (m :: * -> *).
Monad m =>
Int
-> (ByteString -> ByteString)
-> ConduitM ByteString ByteString m ()
codeWith 4 ByteString -> ByteString
B64.decodeLenient

codeWith :: Monad m => Int -> (ByteString -> ByteString) -> ConduitM ByteString ByteString m ()
codeWith :: Int
-> (ByteString -> ByteString)
-> ConduitM ByteString ByteString m ()
codeWith size :: Int
size f :: ByteString -> ByteString
f =
    ConduitM ByteString ByteString m ()
loop
  where
    loop :: ConduitM ByteString ByteString m ()
loop = ConduitT ByteString ByteString m (Maybe ByteString)
forall (m :: * -> *) i. Monad m => Consumer i m (Maybe i)
await ConduitT ByteString ByteString m (Maybe ByteString)
-> (Maybe ByteString -> ConduitM ByteString ByteString m ())
-> ConduitM ByteString ByteString m ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ConduitM ByteString ByteString m ()
-> (ByteString -> ConduitM ByteString ByteString m ())
-> Maybe ByteString
-> ConduitM ByteString ByteString m ()
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (() -> ConduitM ByteString ByteString m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()) ByteString -> ConduitM ByteString ByteString m ()
push

    loopWith :: ByteString -> ConduitM ByteString ByteString m ()
loopWith bs :: ByteString
bs
        | ByteString -> Bool
S.null ByteString
bs = ConduitM ByteString ByteString m ()
loop
        | Bool
otherwise = ConduitT ByteString ByteString m (Maybe ByteString)
forall (m :: * -> *) i. Monad m => Consumer i m (Maybe i)
await ConduitT ByteString ByteString m (Maybe ByteString)
-> (Maybe ByteString -> ConduitM ByteString ByteString m ())
-> ConduitM ByteString ByteString m ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ConduitM ByteString ByteString m ()
-> (ByteString -> ConduitM ByteString ByteString m ())
-> Maybe ByteString
-> ConduitM ByteString ByteString m ()
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (ByteString -> ConduitM ByteString ByteString m ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield (ByteString -> ByteString
f ByteString
bs)) (ByteString -> ByteString -> ConduitM ByteString ByteString m ()
pushWith ByteString
bs)

    push :: ByteString -> ConduitM ByteString ByteString m ()
push bs :: ByteString
bs = do
        let (x :: ByteString
x, y :: ByteString
y) = Int -> ByteString -> (ByteString, ByteString)
S.splitAt (Int
len Int -> Int -> Int
forall a. Num a => a -> a -> a
- (Int
len Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
size)) ByteString
bs
        Bool
-> ConduitM ByteString ByteString m ()
-> ConduitM ByteString ByteString m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (ByteString -> Bool
S.null ByteString
x) (ConduitM ByteString ByteString m ()
 -> ConduitM ByteString ByteString m ())
-> ConduitM ByteString ByteString m ()
-> ConduitM ByteString ByteString m ()
forall a b. (a -> b) -> a -> b
$ ByteString -> ConduitM ByteString ByteString m ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield (ByteString -> ConduitM ByteString ByteString m ())
-> ByteString -> ConduitM ByteString ByteString m ()
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
f ByteString
x
        ByteString -> ConduitM ByteString ByteString m ()
loopWith ByteString
y
      where
        len :: Int
len = ByteString -> Int
S.length ByteString
bs

    pushWith :: ByteString -> ByteString -> ConduitM ByteString ByteString m ()
pushWith bs1 :: ByteString
bs1 bs2 :: ByteString
bs2 | ByteString -> Int
S.length ByteString
bs1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ ByteString -> Int
S.length ByteString
bs2 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
size = ByteString -> ConduitM ByteString ByteString m ()
loopWith (ByteString -> ByteString -> ByteString
S.append ByteString
bs1 ByteString
bs2)
    pushWith bs1 :: ByteString
bs1 bs2 :: ByteString
bs2 = ConduitM ByteString ByteString m ()
-> ConduitM ByteString ByteString m ()
assertion1 (ConduitM ByteString ByteString m ()
 -> ConduitM ByteString ByteString m ())
-> ConduitM ByteString ByteString m ()
-> ConduitM ByteString ByteString m ()
forall a b. (a -> b) -> a -> b
$ ConduitM ByteString ByteString m ()
-> ConduitM ByteString ByteString m ()
assertion2 (ConduitM ByteString ByteString m ()
 -> ConduitM ByteString ByteString m ())
-> ConduitM ByteString ByteString m ()
-> ConduitM ByteString ByteString m ()
forall a b. (a -> b) -> a -> b
$ do
        ByteString -> ConduitM ByteString ByteString m ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield (ByteString -> ConduitM ByteString ByteString m ())
-> ByteString -> ConduitM ByteString ByteString m ()
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
f ByteString
bs1'
        ByteString -> ConduitM ByteString ByteString m ()
push ByteString
y
      where
        m :: Int
m = ByteString -> Int
S.length ByteString
bs1 Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
size
        (x :: ByteString
x, y :: ByteString
y) = Int -> ByteString -> (ByteString, ByteString)
S.splitAt (Int
size Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
m) ByteString
bs2
        bs1' :: ByteString
bs1' = ByteString -> ByteString -> ByteString
S.append ByteString
bs1 ByteString
x

        assertion1 :: ConduitM ByteString ByteString m ()
-> ConduitM ByteString ByteString m ()
assertion1 = Bool
-> ConduitM ByteString ByteString m ()
-> ConduitM ByteString ByteString m ()
forall a. (?callStack::CallStack) => Bool -> a -> a
assert (Bool
 -> ConduitM ByteString ByteString m ()
 -> ConduitM ByteString ByteString m ())
-> Bool
-> ConduitM ByteString ByteString m ()
-> ConduitM ByteString ByteString m ()
forall a b. (a -> b) -> a -> b
$ ByteString -> Int
S.length ByteString
bs1 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
size
        assertion2 :: ConduitM ByteString ByteString m ()
-> ConduitM ByteString ByteString m ()
assertion2 = Bool
-> ConduitM ByteString ByteString m ()
-> ConduitM ByteString ByteString m ()
forall a. (?callStack::CallStack) => Bool -> a -> a
assert (Bool
 -> ConduitM ByteString ByteString m ()
 -> ConduitM ByteString ByteString m ())
-> Bool
-> ConduitM ByteString ByteString m ()
-> ConduitM ByteString ByteString m ()
forall a b. (a -> b) -> a -> b
$ ByteString -> Int
S.length ByteString
bs1' Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
size Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== 0