module Sos
( Sos
, runSos
, sosEnqueueJobs
, module Sos.Exception
, module Sos.FileEvent
, module Sos.Job
, module Sos.JobQueue
, module Sos.Rule
, module Sos.Template
) where
import Sos.Exception
import Sos.FileEvent
import Sos.Job
import Sos.JobQueue
import Sos.Rule
import Sos.Template
import Control.Applicative
import Control.Monad.Except
import Control.Monad.Trans.Resource
import Data.List.NonEmpty (NonEmpty(..))
import Streaming
import System.Exit
import Text.Regex.TDFA (match)
import qualified Streaming.Prelude as S
type Sos a = ResourceT (ExceptT SosException IO) a
-- | Run an 'Sos' action in IO, exiting if any 'SosException's are thrown.
runSos :: Sos a -> IO a
runSos act =
runExceptT (runResourceT act) >>= \case
Left err -> do
print err
exitFailure
Right x -> return x
-- | Enqueue jobs on the given job queue resulting from to apply the given
-- rules to each emitted file event from the given stream. This function
-- returns when the stream is exhausted.
sosEnqueueJobs
:: (Applicative m, MonadError SosException m, MonadResource m)
=> [Rule]
-> Stream (Of FileEvent) m a
-> JobQueue
-> m a
sosEnqueueJobs rules events queue =
S.mapM_
(\(event, cmds) -> liftIO (enqueueJob event cmds queue))
(jobStream rules events)
jobStream
:: forall m a.
(Applicative m, MonadError SosException m)
=> [Rule]
-> Stream (Of FileEvent) m a
-> Stream (Of (FileEvent, NonEmpty ShellCommand)) m a
jobStream rules events =
S.for events
(\event ->
lift (commands event) >>= \case
[] -> pure ()
(c:cs) -> S.yield (event, c :| cs))
where
commands :: FileEvent -> m [ShellCommand]
commands event = concat <$> mapM go rules
where
go :: Rule -> m [ShellCommand]
go rule =
case match (ruleRegex rule) (fileEventPath event) of
[] -> pure []
(xs:_) -> mapM (instantiateTemplate xs) (ruleTemplates rule)