packages feed

caliper-0.1.0.0: src/Caliper/Cli/Parsing.hs

{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE RecordWildCards #-}

module Caliper.Cli.Parsing where

import Control.Applicative
import Data.List.NonEmpty qualified as NE
import Data.Text qualified as T
import Data.Time.Calendar
import Data.Time.LocalTime
import Options.Applicative qualified as Cli
import Text.Read

import Caliper.Syntax

data CliArgs = CliArgs
  { action :: CliAction
  , filepaths :: NE.NonEmpty String
  , filters :: [TagPredicate]
  , begin :: Maybe LocalTime
  , end :: Maybe LocalTime
  }

data CliAction
  = Summation SummationArgs
  | Histogram

newtype SummationArgs = SummationArgs
  { summationFormat :: Maybe String
  }

cliParser :: Cli.ParserInfo CliArgs
cliParser = Cli.info subcommands (Cli.progDesc "Track the amount of work time you have done")
  where
    subcommands :: Cli.Parser CliArgs
    subcommands =
      (Cli.helper <*>) $
        Cli.subparser
          ( Cli.command
              "sum"
              (Cli.info (parseCommonCommandWith summationArgsP) (Cli.progDesc "Sum the amount of work time"))
              <> Cli.command
                "histogram"
                (Cli.info (parseCommonCommandWith (pure Histogram)) (Cli.progDesc "Show a histogram"))
          )

    summationArgsP :: Cli.Parser CliAction
    summationArgsP = do
      fmt <- optional formatP
      pure $ Summation (SummationArgs fmt)

    formatP :: Cli.Parser String
    formatP =
      Cli.strOption $
        Cli.long "format"
          <> Cli.metavar "FORMAT"
          <> Cli.help "Format of the output time, defaults to \"%hh %Mm %Ss\""

    parseCommonCommandWith :: Cli.Parser CliAction -> Cli.Parser CliArgs
    parseCommonCommandWith actionP =
      Cli.helper <*> do
        filepaths <-
          NE.some1 $
            Cli.strArgument $
              Cli.metavar "FILE"
                <> Cli.help "Path to the timetrack file"

        action <- actionP

        begin <-
          optional $
            Cli.option localTimeReader $
              Cli.long "begin"
                <> Cli.short 'b'
                <> Cli.metavar "BEGIN"
                <> Cli.help "Truncate operation from a given moment"

        end <-
          optional $
            Cli.option localTimeReader $
              Cli.long "end"
                <> Cli.short 'e'
                <> Cli.metavar "END"
                <> Cli.help "Truncate operation to a given moment"

        filters <-
          many . Cli.option keyValueReader $
            Cli.metavar "QUERY"
              <> Cli.short 'q'
              <> Cli.long "query"
              <> Cli.help "A \"key:value\" pair to constraint the result"

        pure $ CliArgs {..}

    localTimeReader :: Cli.ReadM LocalTime
    localTimeReader = Cli.maybeReader $ \x ->
      let readDay :: Maybe Day
          readDay = readMaybe x
          readLocalTime :: Maybe LocalTime
          readLocalTime = readMaybe x
      in  readLocalTime <|> (LocalTime <$> readDay <*> pure (TimeOfDay 0 0 0))

    keyValueReader :: Cli.ReadM TagPredicate
    keyValueReader =
      let f s = case break (== ':') s of
            ("not", ':' : s') -> Negate <$> f s'
            (k, ':' : v) -> Just $ HasKeyValue (T.pack k) (T.pack v)
            _ -> Nothing
      in  Cli.maybeReader f