packages feed

nvfetcher-0.7.0.0: src/NvFetcher/Types/ShakeExtras.hs

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}

-- | Copyright: (c) 2021-2022 berberman
-- SPDX-License-Identifier: MIT
-- Maintainer: berberman <berberman@yandex.com>
-- Stability: experimental
-- Portability: portable
--
-- This module is about global information we use in rules.
module NvFetcher.Types.ShakeExtras
  ( -- * Types
    ShakeExtras (..),
    initShakeExtras,
    getShakeExtras,

    -- * Packages
    lookupPackage,
    getAllPackageKeys,
    isPackageKeyTarget,

    -- * Version changes
    recordVersionChange,
    getVersionChanges,

    -- * Retry
    withRetry,

    -- * Build dir
    getBuildDir,

    -- * Keyfile
    getKeyfilePath,

    -- * Last versions
    getLastVersionOnDisk,
    getRecentLastVersion,
    updateLastVersion,
    getAllOnDiskVersions,
    getLastVersionUpdated,

    -- * Booleans
    nvcheckerCacheEnabled,
    nvcheckerKeepGoing,
  )
where

import Control.Concurrent.Extra
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Development.Shake
import NvFetcher.Config
import NvFetcher.Types

data LastVersion
  = OnDisk Version
  | Updated
      (Maybe Version)
      -- ^ on disk if has
      Version

-- | Values we use during the build. It's stored in 'shakeExtra'
data ShakeExtras = ShakeExtras
  { config :: Config,
    versionChanges :: Var [VersionChange],
    targetPackages :: Map PackageKey Package,
    lastVersions :: Var (Map PackageKey LastVersion)
  }

-- | Get our values from shake
getShakeExtras :: Action ShakeExtras
getShakeExtras =
  getShakeExtra @ShakeExtras >>= \case
    Just x -> pure x
    _ -> fail "ShakeExtras is missing!"

-- | Create an empty 'ShakeExtras' from packages to build, times to retry for each rule,
-- build dir, and on disk versions
initShakeExtras :: Config -> Map PackageKey Package -> Map PackageKey Version -> IO ShakeExtras
initShakeExtras config targetPackages lv = do
  versionChanges <- newVar mempty
  lastVersions <- newVar $ Map.map OnDisk lv
  pure ShakeExtras {..}

-- | Get keys of all packages to build
getAllPackageKeys :: Action [PackageKey]
getAllPackageKeys = do
  ShakeExtras {..} <- getShakeExtras
  pure $ Map.keys targetPackages

-- | Find a package given its key
lookupPackage :: PackageKey -> Action (Maybe Package)
lookupPackage key = do
  ShakeExtras {..} <- getShakeExtras
  pure $ Map.lookup key targetPackages

-- | Check if we need build this package
isPackageKeyTarget :: PackageKey -> Action Bool
isPackageKeyTarget k = Map.member k . targetPackages <$> getShakeExtras

-- | Record version change of a package
recordVersionChange :: PackageName -> Maybe Version -> Version -> Action ()
recordVersionChange vcName vcOld vcNew = do
  ShakeExtras {..} <- getShakeExtras
  liftIO $ modifyVar_ versionChanges (pure . (++ [VersionChange {..}]))

-- | Get version changes since the last run
getVersionChanges :: Action [VersionChange]
getVersionChanges = do
  ShakeExtras {..} <- getShakeExtras
  liftIO $ readVar versionChanges

-- | Run an action, retry at most 'retry' times (defined in config) if it throws an exception
withRetry :: Action a -> Action a
withRetry a = getShakeExtras >>= \ShakeExtras {..} -> actionRetry (retry config) a

-- | Get build dir
getBuildDir :: Action FilePath
getBuildDir = buildDir . config <$> getShakeExtras

-- | Get keyfile path
getKeyfilePath :: Action (Maybe FilePath)
getKeyfilePath = keyfile . config <$> getShakeExtras

-- | Get initial version of a package
getLastVersionOnDisk :: PackageKey -> Action (Maybe Version)
getLastVersionOnDisk k = do
  ShakeExtras {..} <- getShakeExtras
  versions <- liftIO $ readVar lastVersions
  pure $ case versions Map.!? k of
    Just (Updated v _) -> v
    Just (OnDisk v) -> Just v
    _ -> Nothing

-- | Get version of a package, no matter it was initial one or rule result
getRecentLastVersion :: PackageKey -> Action (Maybe Version)
getRecentLastVersion k = do
  ShakeExtras {..} <- getShakeExtras
  versions <- liftIO $ readVar lastVersions
  pure $ case versions Map.!? k of
    Just (Updated _ v) -> Just v
    Just (OnDisk v) -> Just v
    _ -> Nothing

-- | Get updated version of a package
getLastVersionUpdated :: PackageKey -> Action (Maybe Version)
getLastVersionUpdated k = do
  ShakeExtras {..} <- getShakeExtras
  versions <- liftIO $ readVar lastVersions
  pure $ case versions Map.!? k of
    Just (Updated _ v) -> Just v
    _ -> Nothing

-- | Add nvchecker result of a package
updateLastVersion :: PackageKey -> Version -> Action ()
updateLastVersion k v = do
  ShakeExtras {..} <- getShakeExtras
  liftIO $
    modifyVar_ lastVersions $ \versions -> pure $ case versions Map.!? k of
      Just (Updated o _) -> Map.insert k (Updated o v) versions
      Just (OnDisk lv) -> Map.insert k (Updated (Just lv) v) versions
      _ -> Map.insert k (Updated Nothing v) versions

-- | Get all initial versions
getAllOnDiskVersions :: Action (Map PackageKey Version)
getAllOnDiskVersions = do
  ShakeExtras {..} <- getShakeExtras
  versions <- liftIO $ readVar lastVersions
  let xs = Map.toList $
        flip Map.map versions $ \case
          OnDisk v -> Just v
          Updated v _ -> v
  pure $ Map.fromList [(k, v) | (k, Just v) <- xs]

-- | Get if 'cacheNvchecker' is enabled
nvcheckerCacheEnabled :: Action Bool
nvcheckerCacheEnabled = cacheNvchecker . config <$> getShakeExtras

-- | Get if 'keepGoing' is enabled
nvcheckerKeepGoing :: Action Bool
nvcheckerKeepGoing = keepGoing . config <$> getShakeExtras