packages feed

dhrun-1.0.1: src/Dhrun/Conduit.hs

{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-|
Module      : Dhrun.Run
Description : runner
Copyright   : (c) Valentin Reis, 2018
License     : MIT
Maintainer  : fre@freux.fr
-}

module Dhrun.Conduit
  ( monitor
  , Sink
  , Source
  , P
  , PC
  )
where

import           Dhrun.Types.Cfg
import           Dhrun.Pure
import           Protolude
import qualified Data.ByteString                                   as B
import           Control.Exception.Base
                   ( throw )
import qualified Data.Conduit.Binary                               as CB
                   ( lines )
import qualified Data.Conduit.Combinators                          as CC
                   ( unlinesAscii )
import           Data.Conduit
                   ( ConduitT
                   , yield
                   , await
                   , runConduit
                   , (.|)
                   )
import qualified Data.Conduit.Process.Typed                        as PT
                   ( Process
                   , ProcessConfig
                   )
import qualified Data.Conduit.Lift                                 as CLF
import           Data.Time.Clock.POSIX

type Source = ConduitT () ByteString IO ()
type Sink = ConduitT ByteString Void IO ()
type P  = PT.Process () Source Source
type PC = PT.ProcessConfig () Source Source

-- | output monitoring conduit. THROWS PatternMatched - with a one second wait to
monitor
  :: Check
  -> ConduitT () ByteString IO ()
  -> ConduitT ByteString Void IO ()
  -> IO ()
monitor behavior source sink =
  runConduit
    $  source
    .| CB.lines
    .| void (CLF.runStateC initialState (CLF.runReaderC behavior makeBehavior))
    .| CC.unlinesAscii
    .| sink

initialState :: MonitoringState
initialState = Nothing
type MonitoringState = Maybe Matched
data Matched = Matched
  { matched :: Text
  , atTime :: POSIXTime } deriving (Show)

-- | makeBehavior builds an IO conduit that throws a PatternMatched when
-- all wanted pattern or one avoided pattern are found
makeBehavior
  :: (MonadIO m, MonadState MonitoringState m, MonadReader Check m)
  => ConduitT ByteString ByteString m ()
makeBehavior = wants <$> ask >>= \case
  [] -> cleanLooper
  as -> expectfulLooper $ toS <$> as

expectfulLooper
  :: (MonadIO m, MonadState MonitoringState m, MonadReader Check m)
  => [ByteString]
  -> ConduitT ByteString ByteString m ()
expectfulLooper [] = throw ThrowFoundAllWants
expectfulLooper l  = noAvoidsAndOtherwise $ \b ->
  yield b >> expectfulLooper (filter (not . flip B.isInfixOf b) (toS <$> l))

cleanLooper
  :: (MonadIO m, MonadState MonitoringState m, MonadReader Check m)
  => ConduitT ByteString ByteString m ()
cleanLooper = noAvoidsAndOtherwise $ \b -> yield b >> cleanLooper

noAvoidsAndOtherwise
  :: (MonadIO m, MonadState MonitoringState m, MonadReader Check m)
  => (ByteString -> ConduitT ByteString ByteString m ())
  -> ConduitT ByteString ByteString m ()
noAvoidsAndOtherwise otherwiseConduit = do
  av <- avoids <$> ask
  t  <- liftIO getPOSIXTime
  get >>= \case
    Just m  -> goThrow t m
    Nothing -> return ()
  await >>= \case
    Just b -> case filter (`B.isInfixOf` b) (toS <$> av) of
      []     -> otherwiseConduit b
      xh : _ -> do
        put . Just $ Matched (toS xh) t
        otherwiseConduit b
    Nothing -> return ()
 where
  goThrow :: POSIXTime -> Matched -> ConduitT ByteString ByteString m ()
  goThrow t Matched {..} =
    when (t > 1 + atTime) (throw $ ThrowFoundAnAvoid matched)

{-noAvoidsAndOtherwise-}
  {-:: (MonadIO m)-}
  {-=> Maybe (Integer, a)-}
  {--> Check-}
  {--> (Maybe (Integer, a) -> ByteString -> ConduitT ByteString ByteString m ())-}
  {--> ConduitT ByteString ByteString m ()-}
{-noAvoidsAndOtherwise mt Check {..} otherwiseConduit = do-}
  {-t <- liftIO askPOSIXTime <&> toInteger-}
  {-case shouldThrow t mt of-}
    {-Just xh -> throw $ ThrowFoundAnAvoid $ toS xh-}
    {-Nothing -> await >>= \case-}
      {-Just b -> case filter (`B.isInfixOf` b) (toS <$> avoids) of-}
        {-[]     -> otherwiseConduit t b-}
        {-xh : _ -> otherwiseConduit t b-}
      {-Nothing -> return ()-}