content: -----------------------------------------------------------------------------
  -- |
  -- Module      :  NgxExport.Tools.Combinators
  -- Copyright   :  (c) Alexey Radkov 2023-2024
  -- License     :  BSD-style
  --
  -- Maintainer  :  alexey.radkov@gmail.com
  -- Stability   :  stable
  -- Portability :  portable
  --
  -----------------------------------------------------------------------------
  
  
  module NgxExport.Tools.Combinators (
      -- * Combinators of effectful actions
      -- $description
  
      -- * Exported functions
                                      voidHandler
                                     ,voidHandler'
                                     ,voidService
                                     ,rareService
                                     ,restartPromptly
      -- * Split services
                                     ,module NgxExport.Tools.SplitService
                                     ) where
  
  import           NgxExport.Tools.SimpleService
  import           NgxExport.Tools.SplitService
  import           NgxExport.Tools.TimeInterval
  
  import qualified Data.ByteString.Lazy as L
  import           Control.Monad
  
  -- $description
  --
  -- A set of functions to combine effectful actions for building handlers and
  -- services tuned for special purposes.
  
  -- | Runs an effectful computation and then returns an empty 'L.ByteString'.
  --
  -- This function saves printing the final @return L.empty@ action in handlers
  -- that return unused or empty 'L.ByteString'.
  --
  -- For example, service /signalUpconf/ being used as an
  -- [/update callback/](https://github.com/lyokha/nginx-haskell-module#update-callbacks)
  -- in
  --
  -- @
  -- type Upconf = [Text]
  --
  -- signalUpconf :: Upconf -> t'NgxExport.Tools.Types.NgxExportService'
  -- signalUpconf upconf = const $ do
  --     mapConcurrently_ getUrl upconf
  --     return L.empty
  --
  -- 'ngxExportSimpleServiceTyped' \'signalUpconf \'\'Upconf $
  --     'PersistentService' Nothing
  -- @
  --
  -- returns an empty bytestring which is not used in a meaningful way, therefore
  -- it can be rewritten as
  --
  -- @
  -- signalUpconf :: Upconf -> t'NgxExport.Tools.Types.NgxExportService'
  -- signalUpconf = const . __/voidHandler/__ . mapConcurrently_ getUrl
  -- @
  --
  -- which helps to focus better on the computation itself.
  --
  -- @since 1.2.0
  voidHandler :: IO a                         -- ^ Target computation
              -> IO L.ByteString
  voidHandler = (>> return L.empty)
  
  -- | Runs an effectful computation and then returns an empty 'L.ByteString'.
  --
  -- The same as 'voidHandler' except it accepts an additional value which is
  -- ignored. Implemented as
  --
  -- @
  -- voidHandler' = const . 'voidHandler'
  -- @
  --
  -- This can be useful in declarations of services that accept a boolean flag
  -- which marks whether the service is running for the first time. This flag is
  -- often ignored though, in which case using @voidHandler'@ can simplify code.
  --
  -- For instance, service /signalUpconf/ from the example for 'voidHandler' can
  -- be further simplified as
  --
  -- @
  -- signalUpconf :: Upconf -> t'NgxExport.Tools.Types.NgxExportService'
  -- signalUpconf = __/voidHandler'/__ . mapConcurrently_ getUrl
  -- @
  --
  -- @since 1.2.1
  voidHandler' :: IO a                        -- ^ Target computation
               -> b                           -- ^ Ignored value
               -> IO L.ByteString
  voidHandler' = const . voidHandler
  
  -- | A void service which does nothing and returns an empty 'L.ByteString'.
  --
  -- The service is implemented as a /split/ service in terms of module
  -- "NgxExport.Tools.SplitService". On the first iteration the service returns
  -- immediately, on the next iteration it sleeps until the worker process
  -- terminates it during the shutdown.
  --
  -- This can be used for loading global data from the Nginx configuration in a
  -- more concise and declarative way.
  --
  -- For example, if data /Conf/ in
  --
  -- @
  -- newtype Conf = Conf Int deriving (Read, Show)
  --
  -- testLoadConf :: Conf -> t'NgxExport.Tools.Types.NgxExportService'
  -- testLoadConf = __/voidService/__
  --
  -- 'ngxExportSimpleServiceTyped' \'testLoadConf \'\'Conf 'restartPromptly'
  -- @
  --
  -- gets loaded by service /testLoadConf/ from the Nginx configuration, then it
  -- can be accessed in the Haskell code via t'Data.IORef.IORef' data storage
  -- /storage_Conf_testLoadConf/.
  --
  -- Declaration of 'restartPromptly' establishes a /persistent/ service mode
  -- without delay. The short iteration at the start of the service can be used
  -- for calling a /service update hook/.
  --
  -- Note that /voidService/ is still an /asynchronous/ service which means that
  -- the global data it loads may appear uninitialized in very early client
  -- requests. To ensure that the data gets loaded before processing client
  -- requests, consider using the /synchronous/ initialization hook
  -- 'NgxExport.ngxExportInitHook' as a distinct solution or in conjunction with
  -- other services.
  --
  -- @since 1.2.3
  voidService :: a                            -- ^ Ignored configuration
              -> Bool                         -- ^ Ignored boolean value
              -> IO L.ByteString
  voidService = splitService (voidHandler' $ return ()) $
      voidHandler' $ forever $ threadDelaySec $ toSec $ Hr 24
  
  -- | A persistent service which waits for 24 hours before restart.
  --
  -- This declaration had been recommended for using with 'voidService' until the
  -- latter was reimplemented as a split service. Nevertheless, it still can be
  -- used for this purpose.
  --
  -- @since 1.2.5
  rareService :: ServiceMode
  rareService = PersistentService $ Just $ Hr 24
  
  -- | A persistent service which restarts without delay.
  --
  -- This convenient declaration can be used for loading global data from the
  -- Nginx configuration with 'voidService'.
  --
  -- @since 1.2.6
  restartPromptly :: ServiceMode
  restartPromptly = PersistentService Nothing
  
  