-- | This modules provides functionality for parsing configuration data
-- from a toml file.
module Navi.Config
  ( -- * Config
    readConfig,
    ConfigErr (..),
    Config (..),

    -- * Logging
    Logging (..),
    LogLoc (..),

    -- * Note System
    NoteSystem (..),
  )
where

import Data.Maybe (catMaybes)
import Navi.Config.Toml (ConfigToml (..))
import Navi.Config.Types
  ( Config (..),
    ConfigErr (..),
    LogLoc (..),
    Logging (..),
    NoteSystem (..),
    defaultLogging,
    defaultNoteSystem,
  )
import Navi.Prelude
import Navi.Services.Battery.Percentage qualified as BattState
import Navi.Services.Battery.Status qualified as BattChargeStatus
import Navi.Services.Custom.Multiple qualified as Multiple
import Navi.Services.Custom.Single qualified as Single
import Navi.Services.Network.NetInterfaces qualified as NetConn

-- | Parses the provided toml file into a 'Config'. Throws 'ConfigErr' if
-- anything goes wrong.
readConfig ::
  ( HasCallStack,
    MonadFileReader m,
    MonadIORef m,
    MonadThrow m
  ) =>
  Path ->
  m Config
readConfig :: forall (m :: Type -> Type).
(HasCallStack, MonadFileReader m, MonadIORef m, MonadThrow m) =>
Path -> m Config
readConfig =
  forall (m :: Type -> Type).
(HasCallStack, MonadFileReader m, MonadThrow m) =>
Path -> m Text
readFileUtf8ThrowM forall (m :: Type -> Type) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> \Text
contents -> do
    -- FIXME: Unused keys do not cause errors. This should probably be addressed
    -- upstream. See https://github.com/brandonchinn178/toml-reader/issues/12
    case forall a. DecodeTOML a => Text -> Either TOMLError a
decode Text
contents of
      Left TOMLError
tomlErr -> forall (m :: Type -> Type) e a.
(Exception e, HasCallStack, MonadThrow m) =>
e -> m a
throwWithCS forall a b. (a -> b) -> a -> b
$ TOMLError -> ConfigErr
TomlError TOMLError
tomlErr
      Right ConfigToml
cfg -> forall (m :: Type -> Type).
(HasCallStack, MonadIORef m, MonadThrow m) =>
ConfigToml -> m Config
tomlToConfig ConfigToml
cfg

tomlToConfig ::
  ( HasCallStack,
    MonadIORef m,
    MonadThrow m
  ) =>
  ConfigToml ->
  m Config
tomlToConfig :: forall (m :: Type -> Type).
(HasCallStack, MonadIORef m, MonadThrow m) =>
ConfigToml -> m Config
tomlToConfig ConfigToml
toml = do
  [AnyEvent]
singleEvents <- forall (t :: Type -> Type) (f :: Type -> Type) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse forall (m :: Type -> Type).
MonadIORef m =>
SingleToml -> m AnyEvent
Single.toEvent [SingleToml]
singleToml
  [AnyEvent]
multipleEvents <- forall (t :: Type -> Type) (f :: Type -> Type) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse forall (m :: Type -> Type).
MonadIORef m =>
MultipleToml -> m AnyEvent
Multiple.toEvent [MultipleToml]
multipleToml
  Maybe AnyEvent
mBatteryLevelEvt <- forall (t :: Type -> Type) (f :: Type -> Type) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse forall (m :: Type -> Type).
MonadIORef m =>
BatteryPercentageToml -> m AnyEvent
BattState.toEvent Maybe BatteryPercentageToml
batteryPercentageToml
  Maybe AnyEvent
mBatteryStatusEvt <- forall (t :: Type -> Type) (f :: Type -> Type) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse forall (m :: Type -> Type).
MonadIORef m =>
BatteryStatusToml -> m AnyEvent
BattChargeStatus.toEvent Maybe BatteryStatusToml
batteryStatusToml
  [AnyEvent]
mNetInterfacesEvt <- forall (t :: Type -> Type) (f :: Type -> Type) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse forall (m :: Type -> Type).
MonadIORef m =>
NetInterfacesToml -> m AnyEvent
NetConn.toEvent [NetInterfacesToml]
netInterfacesToml
  let multipleEvts :: [AnyEvent]
multipleEvts =
        [AnyEvent]
singleEvents
          forall a. Semigroup a => a -> a -> a
<> [AnyEvent]
multipleEvents
          forall a. Semigroup a => a -> a -> a
<> [AnyEvent]
mNetInterfacesEvt
      maybeEvts :: [AnyEvent]
maybeEvts =
        forall a. [Maybe a] -> [a]
catMaybes
          [ Maybe AnyEvent
mBatteryLevelEvt,
            Maybe AnyEvent
mBatteryStatusEvt
          ]
      allEvts :: [AnyEvent]
allEvts = [AnyEvent]
maybeEvts forall a. Semigroup a => a -> a -> a
<> [AnyEvent]
multipleEvts

  case [AnyEvent]
allEvts of
    [] -> forall (m :: Type -> Type) e a.
(Exception e, HasCallStack, MonadThrow m) =>
e -> m a
throwWithCS ConfigErr
NoEvents
    (AnyEvent
e : [AnyEvent]
es) ->
      forall (f :: Type -> Type) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$
        MkConfig
          { $sel:events:MkConfig :: NonEmpty AnyEvent
events = AnyEvent
e forall a. a -> [a] -> NonEmpty a
:| [AnyEvent]
es,
            $sel:logging:MkConfig :: Logging
logging = Logging
logCfg,
            $sel:noteSystem:MkConfig :: NoteSystem
noteSystem = NoteSystem
noteSysCfg
          }
  where
    logCfg :: Logging
logCfg = forall a. a -> Maybe a -> a
fromMaybe Logging
defaultLogging (ConfigToml
toml forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. forall a. IsLabel "logToml" a => a
#logToml)
    noteSysCfg :: NoteSystem
noteSysCfg = forall a. a -> Maybe a -> a
fromMaybe NoteSystem
defaultNoteSystem (ConfigToml
toml forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. forall a. IsLabel "noteSystemToml" a => a
#noteSystemToml)
    singleToml :: [SingleToml]
singleToml = ConfigToml
toml forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. forall a. IsLabel "singleToml" a => a
#singleToml
    multipleToml :: [MultipleToml]
multipleToml = ConfigToml
toml forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. forall a. IsLabel "multipleToml" a => a
#multipleToml
    batteryPercentageToml :: Maybe BatteryPercentageToml
batteryPercentageToml = ConfigToml
toml forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. forall a. IsLabel "batteryPercentageToml" a => a
#batteryPercentageToml
    batteryStatusToml :: Maybe BatteryStatusToml
batteryStatusToml = ConfigToml
toml forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. forall a. IsLabel "batteryStatusToml" a => a
#batteryStatusToml
    netInterfacesToml :: [NetInterfacesToml]
netInterfacesToml = ConfigToml
toml forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. forall a. IsLabel "netInterfacesToml" a => a
#netInterfacesToml