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

-- | Copyright: (c) 2021 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.ShakeExtras
  ( -- * Types
    ShakeExtras (..),
    initShakeExtras,
    getShakeExtras,

    -- * Packages
    lookupPackage,
    getAllPackageKeys,
    isPackageKeyTarget,

    -- * Version changes
    recordVersionChange,
    getVersionChanges,
  )
where

import Control.Concurrent.Extra
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HMap
import Development.Shake
import NvFetcher.Types

-- | Values we use during the build. It's stored in 'shakeExtra'
data ShakeExtras = ShakeExtras
  { ShakeExtras -> Var [VersionChange]
versionChanges :: Var [VersionChange],
    ShakeExtras -> HashMap PackageKey Package
targetPackages :: HashMap PackageKey Package
  }

-- | Get our values from shake
getShakeExtras :: Action ShakeExtras
getShakeExtras :: Action ShakeExtras
getShakeExtras =
  Typeable ShakeExtras => Action (Maybe ShakeExtras)
forall a. Typeable a => Action (Maybe a)
getShakeExtra @ShakeExtras Action (Maybe ShakeExtras)
-> (Maybe ShakeExtras -> Action ShakeExtras) -> Action ShakeExtras
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Just ShakeExtras
x -> ShakeExtras -> Action ShakeExtras
forall (f :: * -> *) a. Applicative f => a -> f a
pure ShakeExtras
x
    Maybe ShakeExtras
_ -> String -> Action ShakeExtras
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"ShakeExtras is missing!"

-- | Create an empty 'ShakeExtras' from packages to build
initShakeExtras :: HashMap PackageKey Package -> IO ShakeExtras
initShakeExtras :: HashMap PackageKey Package -> IO ShakeExtras
initShakeExtras HashMap PackageKey Package
targetPackages = do
  Var [VersionChange]
versionChanges <- [VersionChange] -> IO (Var [VersionChange])
forall a. a -> IO (Var a)
newVar [VersionChange]
forall a. Monoid a => a
mempty
  ShakeExtras -> IO ShakeExtras
forall (f :: * -> *) a. Applicative f => a -> f a
pure ShakeExtras :: Var [VersionChange] -> HashMap PackageKey Package -> ShakeExtras
ShakeExtras {HashMap PackageKey Package
Var [VersionChange]
versionChanges :: Var [VersionChange]
targetPackages :: HashMap PackageKey Package
targetPackages :: HashMap PackageKey Package
versionChanges :: Var [VersionChange]
..}

-- | Get keys of all packages to build
getAllPackageKeys :: Action [PackageKey]
getAllPackageKeys :: Action [PackageKey]
getAllPackageKeys = do
  ShakeExtras {HashMap PackageKey Package
Var [VersionChange]
targetPackages :: HashMap PackageKey Package
versionChanges :: Var [VersionChange]
targetPackages :: ShakeExtras -> HashMap PackageKey Package
versionChanges :: ShakeExtras -> Var [VersionChange]
..} <- Action ShakeExtras
getShakeExtras
  [PackageKey] -> Action [PackageKey]
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([PackageKey] -> Action [PackageKey])
-> [PackageKey] -> Action [PackageKey]
forall a b. (a -> b) -> a -> b
$ HashMap PackageKey Package -> [PackageKey]
forall k v. HashMap k v -> [k]
HMap.keys HashMap PackageKey Package
targetPackages

-- | Find a package given its key
lookupPackage :: PackageKey -> Action (Maybe Package)
lookupPackage :: PackageKey -> Action (Maybe Package)
lookupPackage PackageKey
key = do
  ShakeExtras {HashMap PackageKey Package
Var [VersionChange]
targetPackages :: HashMap PackageKey Package
versionChanges :: Var [VersionChange]
targetPackages :: ShakeExtras -> HashMap PackageKey Package
versionChanges :: ShakeExtras -> Var [VersionChange]
..} <- Action ShakeExtras
getShakeExtras
  Maybe Package -> Action (Maybe Package)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe Package -> Action (Maybe Package))
-> Maybe Package -> Action (Maybe Package)
forall a b. (a -> b) -> a -> b
$ PackageKey -> HashMap PackageKey Package -> Maybe Package
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HMap.lookup PackageKey
key HashMap PackageKey Package
targetPackages

-- | Check if we need build this package
isPackageKeyTarget :: PackageKey -> Action Bool
isPackageKeyTarget :: PackageKey -> Action Bool
isPackageKeyTarget PackageKey
k = PackageKey -> HashMap PackageKey Package -> Bool
forall k a. (Eq k, Hashable k) => k -> HashMap k a -> Bool
HMap.member PackageKey
k (HashMap PackageKey Package -> Bool)
-> (ShakeExtras -> HashMap PackageKey Package)
-> ShakeExtras
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShakeExtras -> HashMap PackageKey Package
targetPackages (ShakeExtras -> Bool) -> Action ShakeExtras -> Action Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Action ShakeExtras
getShakeExtras

-- | Record version change of a package
recordVersionChange :: PackageName -> Maybe Version -> Version -> Action ()
recordVersionChange :: PackageName -> Maybe Version -> Version -> Action ()
recordVersionChange PackageName
vcName Maybe Version
vcOld Version
vcNew = do
  ShakeExtras {HashMap PackageKey Package
Var [VersionChange]
targetPackages :: HashMap PackageKey Package
versionChanges :: Var [VersionChange]
targetPackages :: ShakeExtras -> HashMap PackageKey Package
versionChanges :: ShakeExtras -> Var [VersionChange]
..} <- Action ShakeExtras
getShakeExtras
  IO () -> Action ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> Action ()) -> IO () -> Action ()
forall a b. (a -> b) -> a -> b
$ Var [VersionChange]
-> ([VersionChange] -> IO [VersionChange]) -> IO ()
forall a. Var a -> (a -> IO a) -> IO ()
modifyVar_ Var [VersionChange]
versionChanges ([VersionChange] -> IO [VersionChange]
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([VersionChange] -> IO [VersionChange])
-> ([VersionChange] -> [VersionChange])
-> [VersionChange]
-> IO [VersionChange]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([VersionChange] -> [VersionChange] -> [VersionChange]
forall a. [a] -> [a] -> [a]
++ [VersionChange :: PackageName -> Maybe Version -> Version -> VersionChange
VersionChange {Maybe Version
PackageName
Version
vcNew :: Version
vcOld :: Maybe Version
vcName :: PackageName
vcNew :: Version
vcOld :: Maybe Version
vcName :: PackageName
..}]))

-- | Get version changes since the last run
getVersionChanges :: Action [VersionChange]
getVersionChanges :: Action [VersionChange]
getVersionChanges = do
  ShakeExtras {HashMap PackageKey Package
Var [VersionChange]
targetPackages :: HashMap PackageKey Package
versionChanges :: Var [VersionChange]
targetPackages :: ShakeExtras -> HashMap PackageKey Package
versionChanges :: ShakeExtras -> Var [VersionChange]
..} <- Action ShakeExtras
getShakeExtras
  IO [VersionChange] -> Action [VersionChange]
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO [VersionChange] -> Action [VersionChange])
-> IO [VersionChange] -> Action [VersionChange]
forall a b. (a -> b) -> a -> b
$ Var [VersionChange] -> IO [VersionChange]
forall a. Var a -> IO a
readVar Var [VersionChange]
versionChanges