packages feed

amqp-utils-0.3.2.0: Network/AMQP/Utils/Options.hs

module Network.AMQP.Utils.Options where

import Data.Default.Class
import qualified Data.Map as M
import Data.Maybe
import Data.Text (Text, pack)
import Data.Version (showVersion)
import Network.AMQP
import Network.AMQP.Types
import Paths_amqp_utils (version)
import System.Console.GetOpt

-- | A data type for our options
data Args = Args
  { server :: String
  , port :: Int
  , tls :: Bool
  , vHost :: String
  , currentExchange :: String
  , bindings :: [(String, String)]
  , rKey :: String
  , anRiss :: Maybe Int
  , fileProcess :: Maybe String
  , qName :: Maybe String
  , cert :: Maybe String
  , key :: Maybe String
  , user :: String
  , pass :: String
  , preFetch :: Int
  , heartBeat :: Maybe Int
  , tempDir :: Maybe String
  , additionalArgs :: [String]
  , connectionName :: Maybe String
  , tmpQName :: String
  , inputFile :: String
  , lineMode :: Bool
  , confirm :: Bool
  , msgid :: Maybe Text
  , msgtype :: Maybe Text
  , userid :: Maybe Text
  , appid :: Maybe Text
  , clusterid :: Maybe Text
  , contenttype :: Maybe Text
  , contentencoding :: Maybe Text
  , replyto :: Maybe Text
  , prio :: Maybe Octet
  , corrid :: Maybe Text
  , msgexp :: Maybe Text
  , msgheader :: Maybe FieldTable
  , fnheader :: [String]
  , suffix :: [String]
  , magic :: Bool
  , persistent :: Maybe DeliveryMode
  }

instance Default Args where
  def =
    Args
      "localhost"
      5672
      False
      "/"
      "default"
      []
      ""
      Nothing
      Nothing
      Nothing
      Nothing
      Nothing
      "guest"
      "guest"
      1
      Nothing
      Nothing
      []
      Nothing
      ""
      "/dev/stdin"
      False
      False
      Nothing
      Nothing
      Nothing
      Nothing
      Nothing
      Nothing
      Nothing
      Nothing
      Nothing
      Nothing
      Nothing
      Nothing
      []
      []
      False
      Nothing

-- | Common options
cOptions :: [OptDescr (Args -> Args)]
cOptions =
  [ Option
      ['o']
      ["server"]
      (ReqArg (\s o -> o {server = s}) "SERVER")
      ("AMQP Server (default: " ++ server def ++ ")")
  , Option
      ['y']
      ["vhost"]
      (ReqArg (\s o -> o {vHost = s}) "VHOST")
      ("AMQP Virtual Host (default: " ++ vHost def ++ ")")
  , Option
      ['x']
      ["exchange"]
      (ReqArg (\s o -> o {currentExchange = s}) "EXCHANGE")
      ("AMQP Exchange (default: default)")
  , Option
      ['Q']
      ["qname"]
      (ReqArg (\s o -> o {tmpQName = s}) "TEMPQNAME")
      "Name for temporary exclusive Queue"
  , Option
      ['p']
      ["port"]
      (ReqArg (\s o -> o {port = read s}) "PORT")
      ("Server Port Number (default: " ++ show (port def) ++ ")")
  , Option
      ['T']
      ["tls"]
      (NoArg (\o -> o {tls = not (tls o)}))
      ("Toggle TLS (default: " ++ show (tls def) ++ ")")
  , Option
      ['q']
      ["queue"]
      (ReqArg (\s o -> o {qName = Just s}) "QUEUENAME")
      "Ignore Exchange and bind to existing Queue"
  , Option
      ['c']
      ["cert"]
      (ReqArg (\s o -> o {cert = Just s}) "CERTFILE")
      ("TLS Client Certificate File")
  , Option
      ['k']
      ["key"]
      (ReqArg (\s o -> o {key = Just s}) "KEYFILE")
      ("TLS Client Private Key File")
  , Option
      ['U']
      ["user"]
      (ReqArg (\s o -> o {user = s}) "USERNAME")
      ("Username for Auth")
  , Option
      ['P']
      ["pass"]
      (ReqArg (\s o -> o {pass = s}) "PASSWORD")
      ("Password for Auth")
  , Option
      ['s']
      ["heartbeats"]
      (ReqArg (\s o -> o {heartBeat = (Just (read s))}) "INT")
      "heartbeat interval (0=disable, default: set by server)"
  , Option
      ['n']
      ["name"]
      (ReqArg (\s o -> o {connectionName = Just s}) "NAME")
      "connection name, will be shown in RabbitMQ web interface"
  ]

-- | Options for konsum
kOptions :: [OptDescr (Args -> Args)]
kOptions =
  [ Option
      ['r']
      ["bindingkey"]
      (ReqArg
         (\s o -> o {bindings = (currentExchange o, s) : (bindings o)})
         "BINDINGKEY")
      ("AMQP binding key (default: #)")
  , Option
      ['X']
      ["execute"]
      (OptArg
         (\s o ->
            o
              { fileProcess = Just (fromMaybe callback s)
              , tempDir = Just (fromMaybe "/tmp" (tempDir o))
              })
         "EXE")
      ("Callback Script File (implies -t) (-X without arg: " ++ callback ++ ")")
  , Option
      ['a']
      ["args"]
      (ReqArg (\s o -> o {additionalArgs = s : (additionalArgs o)}) "ARG")
      "additional argument for -X callback"
  , Option
      ['l']
      ["charlimit"]
      (ReqArg (\s o -> o {anRiss = Just (read s :: Int)}) "INT")
      "limit number of shown body chars (default: unlimited)"
  , Option
      ['t']
      ["tempdir", "target"]
      (OptArg (\s o -> o {tempDir = Just (fromMaybe "/tmp" s)}) "DIR")
      "tempdir (default: no file creation, -t without arg: /tmp)"
  , Option
      ['f']
      ["prefetch"]
      (ReqArg (\s o -> o {preFetch = read s}) "INT")
      ("Prefetch count. (0=unlimited, 1=off, default: " ++
       show (preFetch def) ++ ")")
  ]

-- | Options for agitprop
aOptions :: [OptDescr (Args -> Args)]
aOptions =
  [ Option
      ['r']
      ["routingkey"]
      (ReqArg (\s o -> o {rKey = s}) "ROUTINGKEY")
      "AMQP routing key"
  , Option
      ['f']
      ["inputfile"]
      (ReqArg (\s o -> o {inputFile = s}) "INPUTFILE")
      ("Message input file (default: " ++ (inputFile def) ++ ")")
  , Option
      ['l']
      ["linemode"]
      (NoArg (\o -> o {lineMode = not (lineMode o)}))
      ("Toggle line-by-line mode (default: " ++ show (lineMode def) ++ ")")
  , Option
      ['C']
      ["confirm"]
      (NoArg (\o -> o {confirm = not (confirm o)}))
      ("Toggle confirms (default: " ++ show (confirm def) ++ ")")
  , Option
      []
      ["msgid"]
      (ReqArg (\s o -> o {msgid = Just $ pack s}) "ID")
      "Message ID"
  , Option
      []
      ["type"]
      (ReqArg (\s o -> o {msgtype = Just $ pack s}) "TYPE")
      "Message Type"
  , Option
      []
      ["userid"]
      (ReqArg (\s o -> o {userid = Just $ pack s}) "USERID")
      "Message User-ID"
  , Option
      []
      ["appid"]
      (ReqArg (\s o -> o {appid = Just $ pack s}) "APPID")
      "Message App-ID"
  , Option
      []
      ["clusterid"]
      (ReqArg (\s o -> o {clusterid = Just $ pack s}) "CLUSTERID")
      "Message Cluster-ID"
  , Option
      []
      ["contenttype"]
      (ReqArg (\s o -> o {contenttype = Just $ pack s}) "CONTENTTYPE")
      "Message Content-Type"
  , Option
      []
      ["contentencoding"]
      (ReqArg (\s o -> o {contentencoding = Just $ pack s}) "CONTENTENCODING")
      "Message Content-Encoding"
  , Option
      []
      ["replyto"]
      (ReqArg (\s o -> o {replyto = Just $ pack s}) "REPLYTO")
      "Message Reply-To"
  , Option
      []
      ["prio"]
      (ReqArg (\s o -> o {prio = Just $ read s}) "PRIO")
      "Message Priority"
  , Option
      []
      ["corrid"]
      (ReqArg (\s o -> o {corrid = Just $ pack s}) "CORRID")
      "Message CorrelationID"
  , Option
      []
      ["exp"]
      (ReqArg (\s o -> o {msgexp = Just $ pack s}) "EXP")
      "Message Expiration"
  , Option
      ['h']
      ["header"]
      (ReqArg (\s o -> o {msgheader = addheader (msgheader o) s}) "HEADER=VALUE")
      "Message Headers"
  , Option
      ['F']
      ["fnheader"]
      (ReqArg (\s o -> o {fnheader = s : (fnheader o)}) "HEADERNAME")
      "Put filename into this header"
  , Option
      ['S']
      ["suffix"]
      (ReqArg (\s o -> o {suffix = s : (suffix o)}) "SUFFIX")
      "Allowed file suffixes in hotfolder mode"
  , Option
      ['m']
      ["magic"]
      (NoArg (\o -> o {magic = not (magic o)}))
      ("Toggle setting content-type and -encoding from file contents (default: " ++
       show (magic def) ++ ")")
  , Option
      ['e']
      ["persistent"]
      (NoArg (\o -> o {persistent = Just Persistent}))
      "Set persistent delivery"
  , Option
      ['E']
      ["nonpersistent"]
      (NoArg (\o -> o {persistent = Just NonPersistent}))
      "Set nonpersistent delivery"
  ]

-- | Options for the executables
options :: String -> [OptDescr (Args -> Args)]
options "konsum" = kOptions ++ cOptions
options "agitprop" = aOptions ++ cOptions
options _ = cOptions

-- | Add a header with a String value
addheader :: Maybe FieldTable -> String -> Maybe FieldTable
addheader Nothing string = Just $ FieldTable $ M.singleton (k string) (v string)
addheader (Just (FieldTable oldheader)) string =
  Just $ FieldTable $ M.insert (k string) (v string) oldheader

k :: String -> Text
k s = pack $ takeWhile (/= '=') s

v :: String -> FieldValue
v s = FVString $ pack $ tail $ dropWhile (/= '=') s

-- | 'parseargs' exename argstring
-- applies options onto argstring
parseargs :: String -> [String] -> IO Args
parseargs exename argstring =
  case getOpt Permute opt argstring of
    (o, [], []) -> return $ foldl (flip id) def o
    (_, _, errs) ->
      ioError $ userError $ concat errs ++ usageInfo (usage exename) opt
  where
    opt = options exename

-- | the default callback for the -X option
callback :: String
callback = "/usr/lib/haskell-amqp-utils/callback"

usage :: String -> String
usage exename =
  "\n\
  \amqp-utils " ++
  (showVersion version) ++
  "\n\n\
  \Usage:\n" ++
  exename ++
  " [options]\n\n\
  \Options:"