    --- * -*- outline-regexp:"--- \\*"; -*-
    --- ** doc
    -- In Emacs, use TAB on lines beginning with "-- *" to collapse/expand sections.
    {-|

    A reader for the timeclock file format generated by timeclock.el
    (<http://www.emacswiki.org/emacs/TimeClock>). Example:

    @
    i 2007\/03\/10 12:26:00 hledger
    o 2007\/03\/10 17:26:02
    @

    From timeclock.el 2.6:

    @
    A timeclock contains data in the form of a single entry per line.
    Each entry has the form:

      CODE YYYY/MM/DD HH:MM:SS [COMMENT]

    CODE is one of: b, h, i, o or O.  COMMENT is optional when the code is
    i, o or O.  The meanings of the codes are:

      b  Set the current time balance, or \"time debt\".  Useful when
         archiving old log data, when a debt must be carried forward.
         The COMMENT here is the number of seconds of debt.

      h  Set the required working time for the given day.  This must
         be the first entry for that day.  The COMMENT in this case is
         the number of hours in this workday.  Floating point amounts
         are allowed.

      i  Clock in.  The COMMENT in this case should be the name of the
         project worked on.

      o  Clock out.  COMMENT is unnecessary, but can be used to provide
         a description of how the period went, for example.

      O  Final clock out.  Whatever project was being worked on, it is
         now finished.  Useful for creating summary reports.
    @

    -}

    --- ** language
    {-# LANGUAGE OverloadedStrings #-}

    --- ** exports
    module Hledger.Read.TimeclockReader (
      -- * Reader
      reader,
      -- * Misc other exports
      timeclockfilep,
    )
    where

    --- ** imports
    import           Control.Monad
    import           Control.Monad.Except (ExceptT, liftEither)
    import           Control.Monad.State.Strict
    import           Data.Maybe (fromMaybe)
    import           Data.Text (Text)
    import           Text.Megaparsec hiding (parse)

    import           Hledger.Data
    -- XXX too much reuse ?
    import           Hledger.Read.Common
    import           Hledger.Utils
    import Data.Text as T (strip)

    --- ** doctest setup
    -- $setup
    -- >>> :set -XOverloadedStrings

    --- ** reader

    reader :: MonadIO m => Reader m
    reader :: forall (m :: * -> *). MonadIO m => Reader m
    reader = Reader
      {rFormat :: String
    rFormat     = String
    "timeclock"
      ,rExtensions :: [String]
    rExtensions = [String
    "timeclock"]
      ,rReadFn :: InputOpts -> String -> Text -> ExceptT String IO Journal
    rReadFn     = InputOpts -> String -> Text -> ExceptT String IO Journal
    parse
      ,rParser :: MonadIO m => ErroringJournalParser m Journal
    rParser    = ErroringJournalParser m Journal
    MonadIO m => ErroringJournalParser m Journal
    forall (m :: * -> *). MonadIO m => JournalParser m Journal
    timeclockfilep
      }

    -- | Parse and post-process a "Journal" from timeclock.el's timeclock
    -- format, saving the provided file path and the current time, or give an
    -- error.
    parse :: InputOpts -> FilePath -> Text -> ExceptT String IO Journal
    parse :: InputOpts -> String -> Text -> ExceptT String IO Journal
    parse InputOpts
    iopts String
    fp Text
    t = ErroringJournalParser IO Journal
    -> InputOpts -> String -> Text -> ExceptT String IO Journal
    initialiseAndParseJournal ErroringJournalParser IO Journal
    forall (m :: * -> *). MonadIO m => JournalParser m Journal
    timeclockfilep InputOpts
    iopts String
    fp Text
    t
                       ExceptT String IO Journal
    -> (Journal -> ExceptT String IO Journal)
    -> ExceptT String IO Journal
    forall a b.
    ExceptT String IO a
    -> (a -> ExceptT String IO b) -> ExceptT String IO b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= Either String Journal -> ExceptT String IO Journal
    forall e (m :: * -> *) a. MonadError e m => Either e a -> m a
    liftEither (Either String Journal -> ExceptT String IO Journal)
    -> (Journal -> Either String Journal)
    -> Journal
    -> ExceptT String IO Journal
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [AccountAlias] -> Journal -> Either String Journal
    journalApplyAliases (InputOpts -> [AccountAlias]
    aliasesFromOpts InputOpts
    iopts)
                       ExceptT String IO Journal
    -> (Journal -> ExceptT String IO Journal)
    -> ExceptT String IO Journal
    forall a b.
    ExceptT String IO a
    -> (a -> ExceptT String IO b) -> ExceptT String IO b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= InputOpts -> String -> Text -> Journal -> ExceptT String IO Journal
    journalFinalise InputOpts
    iopts String
    fp Text
    t

    --- ** parsers

    timeclockfilep :: MonadIO m => JournalParser m ParsedJournal
    timeclockfilep :: forall (m :: * -> *). MonadIO m => JournalParser m Journal
    timeclockfilep = do StateT Journal (ParsecT HledgerParseErrorData Text m) ()
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) [()]
    forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
    many StateT Journal (ParsecT HledgerParseErrorData Text m) ()
    forall {m :: * -> *}.
    StateT Journal (ParsecT HledgerParseErrorData Text m) ()
    timeclockitemp
                        StateT Journal (ParsecT HledgerParseErrorData Text m) ()
    forall e s (m :: * -> *). MonadParsec e s m => m ()
    eof
                        j :: Journal
    j@Journal{jparsetimeclockentries :: Journal -> [TimeclockEntry]
    jparsetimeclockentries=[TimeclockEntry]
    es} <- JournalParser m Journal
    forall s (m :: * -> *). MonadState s m => m s
    get
                        -- Convert timeclock entries in this journal to transactions, closing any unfinished sessions.
                        -- Doing this here rather than in journalFinalise means timeclock sessions can't span file boundaries,
                        -- but it simplifies code above.
                        LocalTime
    now <- IO LocalTime
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) LocalTime
    forall a.
    IO a -> StateT Journal (ParsecT HledgerParseErrorData Text m) a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO IO LocalTime
    getCurrentLocalTime
                        -- entries have been parsed in reverse order. timeclockEntriesToTransactions
                        -- expects them to be in normal order, then we must reverse again since
                        -- journalFinalise expects them in reverse order
                        let j' :: Journal
    j' = Journal
    j{jtxns = reverse $ timeclockEntriesToTransactions now $ reverse es, jparsetimeclockentries = []}
                        Journal -> JournalParser m Journal
    forall a.
    a -> StateT Journal (ParsecT HledgerParseErrorData Text m) a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Journal
    j'
        where
          -- As all ledger line types can be distinguished by the first
          -- character, excepting transactions versus empty (blank or
          -- comment-only) lines, can use choice w/o try
          timeclockitemp :: StateT Journal (ParsecT HledgerParseErrorData Text m) ()
    timeclockitemp = [StateT Journal (ParsecT HledgerParseErrorData Text m) ()]
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) ()
    forall (f :: * -> *) (m :: * -> *) a.
    (Foldable f, Alternative m) =>
    f (m a) -> m a
    choice [
                                StateT Journal (ParsecT HledgerParseErrorData Text m) ()
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) ()
    forall (f :: * -> *) a. Functor f => f a -> f ()
    void (ParsecT HledgerParseErrorData Text m ()
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) ()
    forall (m :: * -> *) a. Monad m => m a -> StateT Journal m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift ParsecT HledgerParseErrorData Text m ()
    forall (m :: * -> *). TextParser m ()
    emptyorcommentlinep)
                              , JournalParser m TimeclockEntry
    forall (m :: * -> *). JournalParser m TimeclockEntry
    timeclockentryp JournalParser m TimeclockEntry
    -> (TimeclockEntry
        -> StateT Journal (ParsecT HledgerParseErrorData Text m) ())
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) ()
    forall a b.
    StateT Journal (ParsecT HledgerParseErrorData Text m) a
    -> (a -> StateT Journal (ParsecT HledgerParseErrorData Text m) b)
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \TimeclockEntry
    e -> (Journal -> Journal)
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) ()
    forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
    modify' (\Journal
    j -> Journal
    j{jparsetimeclockentries = e : jparsetimeclockentries j})
                              ] StateT Journal (ParsecT HledgerParseErrorData Text m) ()
    -> String
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) ()
    forall e s (m :: * -> *) a.
    MonadParsec e s m =>
    m a -> String -> m a
    <?> String
    "timeclock entry, comment line, or empty line"

    -- | Parse a timeclock entry.
    timeclockentryp :: JournalParser m TimeclockEntry
    timeclockentryp :: forall (m :: * -> *). JournalParser m TimeclockEntry
    timeclockentryp = do
      SourcePos
    pos <- StateT Journal (ParsecT HledgerParseErrorData Text m) SourcePos
    forall s e (m :: * -> *).
    (TraversableStream s, MonadParsec e s m) =>
    m SourcePos
    getSourcePos
      Char
    code <- [Token Text]
    -> StateT
         Journal (ParsecT HledgerParseErrorData Text m) (Token Text)
    forall (f :: * -> *) e s (m :: * -> *).
    (Foldable f, MonadParsec e s m) =>
    f (Token s) -> m (Token s)
    oneOf (String
    "bhioO" :: [Char])
      ParsecT HledgerParseErrorData Text m ()
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) ()
    forall (m :: * -> *) a. Monad m => m a -> StateT Journal m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift ParsecT HledgerParseErrorData Text m ()
    forall s (m :: * -> *).
    (Stream s, Token s ~ Char) =>
    ParsecT HledgerParseErrorData s m ()
    skipNonNewlineSpaces1
      LocalTime
    datetime <- JournalParser m LocalTime
    forall (m :: * -> *). JournalParser m LocalTime
    datetimep
      Text
    account     <- (Maybe Text -> Text)
    -> StateT
         Journal (ParsecT HledgerParseErrorData Text m) (Maybe Text)
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) Text
    forall a b.
    (a -> b)
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) a
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (Text -> Maybe Text -> Text
    forall a. a -> Maybe a -> a
    fromMaybe Text
    "") (StateT Journal (ParsecT HledgerParseErrorData Text m) (Maybe Text)
     -> StateT Journal (ParsecT HledgerParseErrorData Text m) Text)
    -> StateT
         Journal (ParsecT HledgerParseErrorData Text m) (Maybe Text)
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) Text
    forall a b. (a -> b) -> a -> b
    $ StateT Journal (ParsecT HledgerParseErrorData Text m) Text
    -> StateT
         Journal (ParsecT HledgerParseErrorData Text m) (Maybe Text)
    forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
    optional (StateT Journal (ParsecT HledgerParseErrorData Text m) Text
     -> StateT
          Journal (ParsecT HledgerParseErrorData Text m) (Maybe Text))
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) Text
    -> StateT
         Journal (ParsecT HledgerParseErrorData Text m) (Maybe Text)
    forall a b. (a -> b) -> a -> b
    $ ParsecT HledgerParseErrorData Text m ()
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) ()
    forall (m :: * -> *) a. Monad m => m a -> StateT Journal m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift ParsecT HledgerParseErrorData Text m ()
    forall s (m :: * -> *).
    (Stream s, Token s ~ Char) =>
    ParsecT HledgerParseErrorData s m ()
    skipNonNewlineSpaces1 StateT Journal (ParsecT HledgerParseErrorData Text m) ()
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) Text
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) Text
    forall a b.
    StateT Journal (ParsecT HledgerParseErrorData Text m) a
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) b
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) b
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> StateT Journal (ParsecT HledgerParseErrorData Text m) Text
    forall (m :: * -> *). JournalParser m Text
    modifiedaccountnamep
      Text
    description <- (Maybe Text -> Text)
    -> StateT
         Journal (ParsecT HledgerParseErrorData Text m) (Maybe Text)
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) Text
    forall a b.
    (a -> b)
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) a
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (Text -> (Text -> Text) -> Maybe Text -> Text
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe Text
    "" Text -> Text
    T.strip) (StateT Journal (ParsecT HledgerParseErrorData Text m) (Maybe Text)
     -> StateT Journal (ParsecT HledgerParseErrorData Text m) Text)
    -> StateT
         Journal (ParsecT HledgerParseErrorData Text m) (Maybe Text)
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) Text
    forall a b. (a -> b) -> a -> b
    $ StateT Journal (ParsecT HledgerParseErrorData Text m) Text
    -> StateT
         Journal (ParsecT HledgerParseErrorData Text m) (Maybe Text)
    forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
    optional (StateT Journal (ParsecT HledgerParseErrorData Text m) Text
     -> StateT
          Journal (ParsecT HledgerParseErrorData Text m) (Maybe Text))
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) Text
    -> StateT
         Journal (ParsecT HledgerParseErrorData Text m) (Maybe Text)
    forall a b. (a -> b) -> a -> b
    $ ParsecT HledgerParseErrorData Text m Text
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) Text
    forall (m :: * -> *) a. Monad m => m a -> StateT Journal m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (ParsecT HledgerParseErrorData Text m Text
     -> StateT Journal (ParsecT HledgerParseErrorData Text m) Text)
    -> ParsecT HledgerParseErrorData Text m Text
    -> StateT Journal (ParsecT HledgerParseErrorData Text m) Text
    forall a b. (a -> b) -> a -> b
    $ ParsecT HledgerParseErrorData Text m ()
    forall s (m :: * -> *).
    (Stream s, Token s ~ Char) =>
    ParsecT HledgerParseErrorData s m ()
    skipNonNewlineSpaces1 ParsecT HledgerParseErrorData Text m ()
    -> ParsecT HledgerParseErrorData Text m Text
    -> ParsecT HledgerParseErrorData Text m Text
    forall a b.
    ParsecT HledgerParseErrorData Text m a
    -> ParsecT HledgerParseErrorData Text m b
    -> ParsecT HledgerParseErrorData Text m b
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> ParsecT HledgerParseErrorData Text m Text
    forall (m :: * -> *). TextParser m Text
    descriptionp
      (Text
    comment, [Tag]
    tags) <- ParsecT HledgerParseErrorData Text m (Text, [Tag])
    -> StateT
         Journal (ParsecT HledgerParseErrorData Text m) (Text, [Tag])
    forall (m :: * -> *) a. Monad m => m a -> StateT Journal m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift ParsecT HledgerParseErrorData Text m (Text, [Tag])
    forall (m :: * -> *). TextParser m (Text, [Tag])
    transactioncommentp
      TimeclockEntry -> JournalParser m TimeclockEntry
    forall a.
    a -> StateT Journal (ParsecT HledgerParseErrorData Text m) a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (TimeclockEntry -> JournalParser m TimeclockEntry)
    -> TimeclockEntry -> JournalParser m TimeclockEntry
    forall a b. (a -> b) -> a -> b
    $ SourcePos
    -> TimeclockCode
    -> LocalTime
    -> Text
    -> Text
    -> Text
    -> [Tag]
    -> TimeclockEntry
    TimeclockEntry SourcePos
    pos (String -> TimeclockCode
    forall a. Read a => String -> a
    read [Char
    code]) LocalTime
    datetime Text
    account Text
    description Text
    comment [Tag]
    tags