packages feed

imm-2.0.0.0: src/lib/Imm/Callback.hs

{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications  #-}
module Imm.Callback (Callback(..), CallbackMessage(..), runCallback) where

-- {{{ Imports
import           Data.Aeson
import           Data.Aeson.Encode.Pretty
import           Data.Text.Prettyprint.Doc
import           Dhall                     hiding (maybe)
import           Imm.Feed
import           Imm.Logger                as Logger
import           Imm.Pretty
import           System.Exit
import           System.Process.Typed
-- }}}

-- | External program run for each feed element.
--
-- Data is passed to that program through standard input (@stdin@).
data Callback = Callback
  { _executable :: FilePath
  , _arguments  :: [Text]
  } deriving (Eq, Generic, Ord, Read, Show)

instance FromDhall Callback

instance Pretty Callback where
  pretty (Callback executable arguments) = pretty executable <+> sep (pretty <$> arguments)

-- | Data structure passed to the external program, through JSON format.
--
-- The data schema is described in file @schema/imm.json@, provided with this library.
data CallbackMessage = CallbackMessage
  { _callbackFeedDefinition :: FeedDefinition
  , _callbackFeedItem       :: FeedItem
  } deriving(Eq, Generic, Ord, Show, Typeable)

customOptions :: Options
customOptions = defaultOptions
  { fieldLabelModifier = camelTo2 '_' . drop (length @[] "_callback")
  , omitNothingFields = True
  }

instance ToJSON CallbackMessage where
  toJSON     = genericToJSON customOptions
  toEncoding = genericToEncoding customOptions

instance FromJSON CallbackMessage where
  parseJSON = genericParseJSON customOptions

instance Pretty (PrettyShort CallbackMessage) where
  pretty (PrettyShort (CallbackMessage feed item)) = prettyName feed <+> "/" <+> pretty (_itemTitle item)


runCallback :: MonadIO m
  => Logger.Handle m -> Callback -> CallbackMessage -> m (Either (Callback, Int, LByteString, LByteString) (Callback, LByteString, LByteString))
runCallback logger callback@(Callback executable arguments) message = do
  log logger Info $ "Running" <+> cyan (pretty executable) <+> "on" <+> prettyShort message
  log logger Debug $ "Callback message:" <+> pretty (decodeUtf8 @Text $ encodePretty message)

  let processInput = byteStringInput $ encode message
      processConfig = proc executable (toString <$> arguments) & setStdin processInput
  (exitCode, output, errors) <- readProcess processConfig

  case exitCode of
    ExitSuccess   -> return $ Right (callback, output, errors)
    ExitFailure i -> return $ Left (callback, i, output, errors)