-- |Interpreters for 'Settings'
module Ribosome.Interpreter.Settings where

import Exon (exon)

import Ribosome.Data.PluginName (PluginName (PluginName))
import Ribosome.Data.Setting (Setting (Setting))
import qualified Ribosome.Data.SettingError as SettingError
import Ribosome.Data.SettingError (SettingError)
import Ribosome.Effect.Settings (Settings (Get, Update))
import Ribosome.Host.Api.Effect (nvimGetVar, nvimSetVar)
import Ribosome.Host.Class.Msgpack.Decode (MsgpackDecode)
import qualified Ribosome.Host.Data.RpcError as RpcError
import Ribosome.Host.Data.RpcError (RpcError)
import Ribosome.Host.Effect.Rpc (Rpc)
import Ribosome.PluginName (pluginName)

-- |Assemble the name of a setting variable by prefixing the 'Setting' key with the plugin name if the flag is set.
settingVariableName ::
  Member (Reader PluginName) r =>
  Setting a ->
  Sem r Text
settingVariableName :: forall (r :: EffectRow) a.
Member (Reader PluginName) r =>
Setting a -> Sem r Text
settingVariableName = \case
  Setting Text
key Bool
False Maybe a
_ ->
    Text -> Sem r Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
key
  Setting Text
key Bool
True Maybe a
_ -> do
    PluginName Text
name <- Sem r PluginName
forall (r :: EffectRow).
Member (Reader PluginName) r =>
Sem r PluginName
pluginName
    Text -> Sem r Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure [exon|#{name}_#{key}|]

-- |Fetch the value for a setting stored in its Neovim variable.
settingRaw ::
  Members [Rpc, Reader PluginName] r =>
  MsgpackDecode a =>
  Setting a ->
  Sem r a
settingRaw :: forall (r :: EffectRow) a.
(Members '[Rpc, Reader PluginName] r, MsgpackDecode a) =>
Setting a -> Sem r a
settingRaw Setting a
s =
  Text -> Sem r a
forall a (r :: EffectRow).
(Member Rpc r, MsgpackDecode a) =>
Text -> Sem r a
nvimGetVar (Text -> Sem r a) -> Sem r Text -> Sem r a
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Setting a -> Sem r Text
forall (r :: EffectRow) a.
Member (Reader PluginName) r =>
Setting a -> Sem r Text
settingVariableName Setting a
s

-- |Return the default value for a setting or stop with an error if none is set.
fallback ::
  Members [Reader PluginName, Stop SettingError] r =>
  Setting a ->
  Sem r a
fallback :: forall (r :: EffectRow) a.
Members '[Reader PluginName, Stop SettingError] r =>
Setting a -> Sem r a
fallback = \case
  Setting Text
_ Bool
_ (Just a
a) ->
    a -> Sem r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
a
  s :: Setting a
s@(Setting Text
_ Bool
_ Maybe a
Nothing) -> do
    Text
n <- Setting a -> Sem r Text
forall (r :: EffectRow) a.
Member (Reader PluginName) r =>
Setting a -> Sem r Text
settingVariableName Setting a
s
    SettingError -> Sem r a
forall e (r :: EffectRow) a. Member (Stop e) r => e -> Sem r a
stop (Text -> SettingError
SettingError.Unset Text
n)

-- |Interpret 'Settings' natively, using Neovim variables.
interpretSettingsRpc ::
  Members [Rpc !! RpcError, Reader PluginName] r =>
  InterpreterFor (Settings !! SettingError) r
interpretSettingsRpc :: forall (r :: EffectRow).
Members '[Rpc !! RpcError, Reader PluginName] r =>
InterpreterFor (Settings !! SettingError) r
interpretSettingsRpc =
  (forall x (r0 :: EffectRow).
 Settings (Sem r0) x -> Sem (Stop SettingError : r) x)
-> InterpreterFor (Settings !! SettingError) r
forall err (eff :: (* -> *) -> * -> *) (r :: EffectRow).
FirstOrder eff "interpretResumable" =>
(forall x (r0 :: EffectRow).
 eff (Sem r0) x -> Sem (Stop err : r) x)
-> InterpreterFor (Resumable err eff) r
interpretResumable \case
    Get Setting x
s ->
      Setting x -> Sem (Rpc : Stop SettingError : r) x
forall (r :: EffectRow) a.
(Members '[Rpc, Reader PluginName] r, MsgpackDecode a) =>
Setting a -> Sem r a
settingRaw Setting x
s Sem (Rpc : Stop SettingError : r) x
-> (RpcError -> Sem (Stop SettingError : r) x)
-> Sem (Stop SettingError : r) x
forall err (eff :: (* -> *) -> * -> *) (r :: EffectRow) a.
Member (Resumable err eff) r =>
Sem (eff : r) a -> (err -> Sem r a) -> Sem r a
!! \case
        RpcError.Decode DecodeError
e -> do
          Text
n <- Setting x -> Sem (Stop SettingError : r) Text
forall (r :: EffectRow) a.
Member (Reader PluginName) r =>
Setting a -> Sem r Text
settingVariableName Setting x
s
          SettingError -> Sem (Stop SettingError : r) x
forall e (r :: EffectRow) a. Member (Stop e) r => e -> Sem r a
stop (Text -> DecodeError -> SettingError
SettingError.Decode Text
n DecodeError
e)
        RpcError.Api RpcMethod
_ [Object]
_ Text
_ ->
          Setting x -> Sem (Stop SettingError : r) x
forall (r :: EffectRow) a.
Members '[Reader PluginName, Stop SettingError] r =>
Setting a -> Sem r a
fallback Setting x
s
        RpcError.Unexpected Text
_ ->
          Setting x -> Sem (Stop SettingError : r) x
forall (r :: EffectRow) a.
Members '[Reader PluginName, Stop SettingError] r =>
Setting a -> Sem r a
fallback Setting x
s
    Update Setting a1
s a1
a -> do
      Text
n <- Setting a1 -> Sem (Stop SettingError : r) Text
forall (r :: EffectRow) a.
Member (Reader PluginName) r =>
Setting a -> Sem r Text
settingVariableName Setting a1
s
      (RpcError -> SettingError)
-> Sem (Rpc : Stop SettingError : r) ()
-> Sem (Stop SettingError : r) ()
forall err (eff :: (* -> *) -> * -> *) err' (r :: EffectRow) a.
Members '[Resumable err eff, Stop err'] r =>
(err -> err') -> Sem (eff : r) a -> Sem r a
resumeHoist (Text -> RpcError -> SettingError
SettingError.UpdateFailed Text
n) (Text -> a1 -> Sem (Rpc : Stop SettingError : r) ()
forall p_1 (r :: EffectRow).
(Member Rpc r, MsgpackEncode p_1) =>
Text -> p_1 -> Sem r ()
nvimSetVar Text
n a1
a)