packages feed

eventlog-live-0.9.0.0: src-app/GHC/Eventlog/Live/Otlp/Options.hs

module GHC.Eventlog.Live.Otlp.Options (
  Options (..),
  MyDebugOptions (..),
  options,
) where

import Data.Char (isSpace)
import Data.Default (Default (..))
import Data.List qualified as L
import Data.Text qualified as T
import Data.Version (showVersion)
import GHC.Debug.Stub.Compat (MyGhcDebugSocket, maybeMyGhcDebugSocketParser)
import GHC.Eventlog.Live.Options
import GHC.Eventlog.Live.Otlp.Config qualified as C
import GHC.Eventlog.Live.Otlp.Config.Default.Raw (defaultConfigJSONSchemaString, defaultConfigString)
import GHC.Eventlog.Live.Otlp.Config.Types (Config)
import GHC.Eventlog.Live.Otlp.Control (ControlOptions, controlOptionsParser)
import GHC.Eventlog.Live.Otlp.Options.Raw (footerString, headerString, progDescString)
import GHC.Eventlog.Live.Source.Core (EventlogSourceOptions (..))
import GHC.Eventlog.Socket.Compat (MyEventlogSocket (..), maybeMyEventlogSocketParser)
import GHC.RTS.Events (HeapProfBreakdown (..))
import Options.Applicative qualified as O
import Options.Applicative.Compat qualified as OC
import Options.Applicative.Extra qualified as OE
import Options.Applicative.Help.Chunk qualified as OHC
import Options.Applicative.Help.Pretty qualified as OHP
import Paths_eventlog_live qualified as EventlogLive

options :: O.ParserInfo Options
options =
  O.info
    ( optionsParser
        O.<**> defaultsPrinter
        O.<**> debugDefaultsPrinter
        O.<**> configJSONSchemaPrinter
        O.<**> OE.helperWith (O.long "help" <> O.help "Show this help text.")
        O.<**> OC.simpleVersioner (showVersion EventlogLive.version)
    )
    ( O.headerDoc (helpDoc headerString)
        <> O.progDescDoc (helpDoc progDescString)
        <> O.footerDoc (helpDoc footerString)
    )

{- |
Internal helper.

Render a text as an `OHP.Doc`.
-}
helpDoc :: String -> Maybe OHP.Doc
helpDoc doc
  | OHC.isEmpty (OHC.vcatChunks docChunks) = Nothing
  | otherwise = OHC.unChunk $ vcatChunks docChunks
 where
  docChunks :: [OHC.Chunk OHP.Doc]
  docChunks = helpDocLine <$> lines doc

  -- NOTE: Variant of vcatChunks that uses hardline
  vcatChunks :: [OHC.Chunk OHP.Doc] -> OHC.Chunk OHP.Doc
  vcatChunks = mconcat . L.intersperse (OHC.Chunk $ Just OHP.hardline)

{- |
Internal helper.

Internal accumulated state for `helpDocLine`.
-}
data LineAcc
  = Space {count :: !Int, chunk :: !(OHC.Chunk OHP.Doc)}
  | Token {token :: !String, chunk :: !(OHC.Chunk OHP.Doc)}

{- |
Internal helper.

Render a line as an `OHP.Doc`.
-}
helpDocLine :: String -> OHC.Chunk OHP.Doc
helpDocLine =
  asDoc True . L.foldl' trans empty . reverse
 where
  empty :: LineAcc
  empty = Space 0 mempty

  trans :: LineAcc -> Char -> LineAcc
  trans la@Space{..} c
    | isSpace c = Space{count = count + 1, ..}
    | count >= 2 = Token{token = [c], chunk = asDoc False la}
    | otherwise = Token{token = [c], ..}
  trans la@Token{..} c
    | isSpace c = Space{count = 1, chunk = asDoc False la}
    | otherwise = Token{token = c : token, ..}

  asDoc :: Bool -> LineAcc -> OHC.Chunk OHP.Doc
  asDoc isFinal = \case
    Space{..} ->
      OHP.indent (if isFinal then count else count - 1) <$> chunk
    Token{..} ->
      OHC.stringChunk token OHC.<</>> chunk

data Options = Options
  { eventlogSourceOptions :: EventlogSourceOptions
  , eventlogSocketTimeoutS :: Double
  , eventlogSocketTimeoutExponent :: Double
  , eventlogFlushIntervalS :: Double
  , maybeEventlogLogFile :: Maybe FilePath
  , maybeHeapProfBreakdown :: Maybe HeapProfBreakdown
  , maybeIpeDBPath :: Maybe FilePath
  , maybeCCDBPath :: Maybe FilePath
  , stats :: Bool
  , maybeConfigFile :: Maybe FilePath
  , controlOptions :: ControlOptions
  , myDebugOptions :: MyDebugOptions
  }

optionsParser :: O.Parser Options
optionsParser =
  Options
    <$> eventlogSourceOptionsParser
    <*> eventlogSocketTimeoutSParser
    <*> eventlogSocketTimeoutExponentParser
    <*> eventlogFlushIntervalSParser
    <*> O.optional eventlogLogFileParser
    <*> O.optional heapProfBreakdownParser
    <*> O.optional ipeDBPathParser
    <*> O.optional ccDBPathParser
    <*> statsParser
    <*> O.optional configFileParser
    <*> controlOptionsParser
    <*> myDebugOptionsParser

--------------------------------------------------------------------------------
-- Configuration

configFileParser :: O.Parser FilePath
configFileParser =
  O.strOption
    ( O.long "config"
        <> O.metavar "FILE"
        <> O.help "The path to a detailed configuration file."
    )

defaultsPrinter :: O.Parser (a -> a)
defaultsPrinter =
  O.infoOption defaultConfigString . mconcat $
    [ O.long "print-defaults"
    , O.help "Print default configuration options."
    ]

configJSONSchemaPrinter :: O.Parser (a -> a)
configJSONSchemaPrinter =
  O.infoOption defaultConfigJSONSchemaString . mconcat $
    [ O.long "print-config-json-schema"
    , O.help "Print JSON Schema for configuration format."
    ]

debugDefaultsPrinter :: O.Parser (a -> a)
debugDefaultsPrinter =
  O.infoOption defaultConfigDebugString . mconcat $
    [ O.long "print-defaults-debug"
    , O.help "Print default configuration options using the parsed representation."
    , O.internal
    ]
 where
  defaultConfigDebugString =
    T.unpack . C.prettyConfig $ (def :: Config)

--------------------------------------------------------------------------------
-- InfoProv Tables

ipeDBPathParser :: O.Parser FilePath
ipeDBPathParser =
  O.strOption
    ( O.long "ipedb"
        <> O.metavar "FILE"
        <> O.help "The path to an IPE database."
    )

--------------------------------------------------------------------------------
-- CostCentre Tables

ccDBPathParser :: O.Parser FilePath
ccDBPathParser =
  O.strOption
    ( O.long "ccdb"
        <> O.metavar "FILE"
        <> O.help "The path a cost-centre database."
    )

--------------------------------------------------------------------------------
-- Debug Options

data MyDebugOptions = MyDebugOptions
  { maybeMyEventlogSocket :: Maybe MyEventlogSocket
  , maybeMyGhcDebugSocket :: Maybe MyGhcDebugSocket
  }

myDebugOptionsParser :: O.Parser MyDebugOptions
myDebugOptionsParser =
  OC.parserOptionGroup "Debug options:" $
    MyDebugOptions
      <$> maybeMyEventlogSocketParser
      <*> maybeMyGhcDebugSocketParser