{-# LANGUAGE CPP #-}

-- | Provides an effect for querying system information.
module Navi.Effects.MonadSystemInfo
  ( MonadSystemInfo (..),
  )
where

import Navi.Data.CommandResult (CommandResult)
import Navi.Data.CommandResultParser (CommandResultParser)
import Navi.Data.PollInterval (PollInterval)
import Navi.Event.Types (EventError (MkEventError, long, name, short))
import Navi.Prelude
import Navi.Services.Types
  ( ServiceType
      ( BatteryPercentage,
        BatteryStatus,
        Custom,
        NetworkInterface
      ),
  )
import Navi.Utils qualified as U
import Pythia qualified
import Pythia.Data.Command (Command)
import Pythia.Internal.ShellApp
  ( SimpleShell
      ( MkSimpleShell,
        command,
        isSupported,
        parser
      ),
  )
import Pythia.Internal.ShellApp qualified as ShellApp

{- HLINT ignore MonadSystemInfo "Redundant bracket" -}

-- | This class represents an effect of querying system information.
class (Monad m) => MonadSystemInfo m where
  query :: (HasCallStack) => ServiceType result -> m (result, Maybe PollInterval)

instance MonadSystemInfo IO where
  query :: ServiceType result -> IO (result, Maybe PollInterval)
  query :: forall result.
ServiceType result -> IO (result, Maybe PollInterval)
query = \case
    BatteryPercentage BatteryApp
bp ->
      Text
-> IO (result, Maybe PollInterval)
-> IO (result, Maybe PollInterval)
forall a. Text -> IO a -> IO a
rethrowPythia Text
"Battery Percentage" (IO (result, Maybe PollInterval)
 -> IO (result, Maybe PollInterval))
-> IO (result, Maybe PollInterval)
-> IO (result, Maybe PollInterval)
forall a b. (a -> b) -> a -> b
$ (,Maybe PollInterval
forall a. Maybe a
Nothing) (result -> (result, Maybe PollInterval))
-> IO result -> IO (result, Maybe PollInterval)
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> BatteryApp -> IO Battery
forall (m :: Type -> Type).
(HasCallStack, MonadCatch m, MonadFileReader m, MonadPathReader m,
 MonadTypedProcess m) =>
BatteryApp -> m Battery
Pythia.queryBattery BatteryApp
bp
    BatteryStatus BatteryApp
bp ->
      Text
-> IO (result, Maybe PollInterval)
-> IO (result, Maybe PollInterval)
forall a. Text -> IO a -> IO a
rethrowPythia Text
"Battery Status" (IO (result, Maybe PollInterval)
 -> IO (result, Maybe PollInterval))
-> IO (result, Maybe PollInterval)
-> IO (result, Maybe PollInterval)
forall a b. (a -> b) -> a -> b
$ (,Maybe PollInterval
forall a. Maybe a
Nothing) (BatteryStatus -> (BatteryStatus, Maybe PollInterval))
-> (Battery -> BatteryStatus)
-> Battery
-> (BatteryStatus, Maybe PollInterval)
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. Optic' A_Lens NoIx Battery BatteryStatus
-> Battery -> BatteryStatus
forall k (is :: IxList) s a.
Is k A_Getter =>
Optic' k is s a -> s -> a
view Optic' A_Lens NoIx Battery BatteryStatus
#status (Battery -> (result, Maybe PollInterval))
-> IO Battery -> IO (result, Maybe PollInterval)
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> BatteryApp -> IO Battery
forall (m :: Type -> Type).
(HasCallStack, MonadCatch m, MonadFileReader m, MonadPathReader m,
 MonadTypedProcess m) =>
BatteryApp -> m Battery
Pythia.queryBattery BatteryApp
bp
    NetworkInterface Device
device NetInterfaceApp
cp ->
      Text
-> IO (result, Maybe PollInterval)
-> IO (result, Maybe PollInterval)
forall a. Text -> IO a -> IO a
rethrowPythia Text
"NetInterface" (IO (result, Maybe PollInterval)
 -> IO (result, Maybe PollInterval))
-> IO (result, Maybe PollInterval)
-> IO (result, Maybe PollInterval)
forall a b. (a -> b) -> a -> b
$ (,Maybe PollInterval
forall a. Maybe a
Nothing) (result -> (result, Maybe PollInterval))
-> IO result -> IO (result, Maybe PollInterval)
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> Device -> NetInterfaceApp -> IO NetInterface
forall (m :: Type -> Type).
(HasCallStack, MonadPathReader m, MonadThrow m,
 MonadTypedProcess m) =>
Device -> NetInterfaceApp -> m NetInterface
Pythia.queryNetInterface Device
device NetInterfaceApp
cp
    Custom Command
cmd CommandResultParser
parser -> Text
-> IO (CommandResult, Maybe PollInterval)
-> IO (CommandResult, Maybe PollInterval)
forall a. Text -> IO a -> IO a
rethrowPythia Text
"Custom" (IO (CommandResult, Maybe PollInterval)
 -> IO (CommandResult, Maybe PollInterval))
-> IO (CommandResult, Maybe PollInterval)
-> IO (CommandResult, Maybe PollInterval)
forall a b. (a -> b) -> a -> b
$ Command
-> CommandResultParser -> IO (CommandResult, Maybe PollInterval)
querySimple Command
cmd CommandResultParser
parser

rethrowPythia :: Text -> IO a -> IO a
rethrowPythia :: forall a. Text -> IO a -> IO a
rethrowPythia Text
n IO a
io =
  IO a
io IO a -> (SomeException -> IO a) -> IO a
forall (m :: Type -> Type) a.
(HasCallStack, MonadCatch m) =>
m a -> (SomeException -> m a) -> m a
`catchSync` \SomeException
e ->
    EventError -> IO a
forall e a. (HasCallStack, Exception e) => e -> IO a
forall (m :: Type -> Type) e a.
(MonadThrow m, HasCallStack, Exception e) =>
e -> m a
throwM
      (EventError -> IO a) -> EventError -> IO a
forall a b. (a -> b) -> a -> b
$ MkEventError
        { name :: Text
name = Text
n,
          short :: Text
short = Text
"PythiaException",
          long :: Text
long = String -> Text
packText (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ SomeException -> String
forall e. Exception e => e -> String
U.displayInner SomeException
e
        }

instance (MonadSystemInfo m) => MonadSystemInfo (ReaderT e m) where
  query :: forall result.
HasCallStack =>
ServiceType result -> ReaderT e m (result, Maybe PollInterval)
query = m (result, Maybe PollInterval)
-> ReaderT e m (result, Maybe PollInterval)
forall (m :: Type -> Type) a. Monad m => m a -> ReaderT e m a
forall (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (result, Maybe PollInterval)
 -> ReaderT e m (result, Maybe PollInterval))
-> (ServiceType result -> m (result, Maybe PollInterval))
-> ServiceType result
-> ReaderT e m (result, Maybe PollInterval)
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. ServiceType result -> m (result, Maybe PollInterval)
forall result.
HasCallStack =>
ServiceType result -> m (result, Maybe PollInterval)
forall (m :: Type -> Type) result.
(MonadSystemInfo m, HasCallStack) =>
ServiceType result -> m (result, Maybe PollInterval)
query
  {-# INLINEABLE query #-}

querySimple :: Command -> CommandResultParser -> IO (CommandResult, Maybe PollInterval)
querySimple :: Command
-> CommandResultParser -> IO (CommandResult, Maybe PollInterval)
querySimple Command
cmd CommandResultParser
parser = (\CommandResult
cr -> (CommandResult
cr, CommandResult
cr CommandResult
-> Optic' A_Lens NoIx CommandResult (Maybe PollInterval)
-> Maybe PollInterval
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx CommandResult (Maybe PollInterval)
#pollInterval)) (CommandResult -> (CommandResult, Maybe PollInterval))
-> IO CommandResult -> IO (CommandResult, Maybe PollInterval)
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> SimpleShell IO EventError CommandResult -> IO CommandResult
forall (m :: Type -> Type) err result.
(Exception err, HasCallStack, MonadThrow m, MonadTypedProcess m) =>
SimpleShell m err result -> m result
ShellApp.runSimple SimpleShell IO EventError CommandResult
shellApp
  where
    shellApp :: SimpleShell IO EventError CommandResult
shellApp = Command
-> (Text -> Either EventError CommandResult)
-> SimpleShell IO EventError CommandResult
forall (f :: Type -> Type).
Applicative f =>
Command
-> (Text -> Either EventError CommandResult)
-> SimpleShell f EventError CommandResult
mkApp Command
cmd (CommandResultParser
parser CommandResultParser
-> Optic'
     An_Iso
     NoIx
     CommandResultParser
     (Text -> Either EventError CommandResult)
-> Text
-> Either EventError CommandResult
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic'
  An_Iso
  NoIx
  CommandResultParser
  (Text -> Either EventError CommandResult)
#unCommandResultParser)

mkApp ::
  (Applicative f) =>
  Command ->
  (Text -> Either EventError CommandResult) ->
  SimpleShell f EventError CommandResult
mkApp :: forall (f :: Type -> Type).
Applicative f =>
Command
-> (Text -> Either EventError CommandResult)
-> SimpleShell f EventError CommandResult
mkApp Command
cmd Text -> Either EventError CommandResult
parser =
  MkSimpleShell
    { command :: Command
command = Command
cmd,
      isSupported :: f Bool
isSupported = Bool -> f Bool
forall a. a -> f a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure Bool
True,
      Text -> Either EventError CommandResult
parser :: Text -> Either EventError CommandResult
parser :: Text -> Either EventError CommandResult
parser
    }