{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

-- | Copyright: (c) 2021-2022 berberman
-- SPDX-License-Identifier: MIT
-- Maintainer: berberman <berberman@yandex.com>
-- Stability: experimental
-- Portability: portable
module NvFetcher.Core
  ( Core (..),
    coreRules,
    runPackage,
  )
where

import Data.Coerce (coerce)
import qualified Data.HashMap.Strict as HMap
import qualified Data.Text as T
import Development.Shake
import Development.Shake.FilePath
import Development.Shake.Rule
import NvFetcher.ExtractSrc
import NvFetcher.FetchRustGitDeps
import NvFetcher.GetGitCommitDate
import NvFetcher.NixFetcher
import NvFetcher.Nvchecker
import NvFetcher.Types
import NvFetcher.Types.ShakeExtras

-- | The core rule of nvchecker.
-- all rules are wired here.
coreRules :: Rules ()
coreRules :: Rules ()
coreRules = do
  Rules ()
nvcheckerRule
  Rules ()
prefetchRule
  Rules ()
extractSrcRule
  Rules ()
fetchRustGitDepsRule
  Rules ()
getGitCommitDateRule
  forall key value.
(RuleResult key ~ value, ShakeValue key, Typeable value,
 NFData value, Show value, Partial) =>
BuiltinLint key value
-> BuiltinIdentity key value -> BuiltinRun key value -> Rules ()
addBuiltinRule forall key value. BuiltinLint key value
noLint forall key value. BuiltinIdentity key value
noIdentity forall a b. (a -> b) -> a -> b
$ \(WithPackageKey (Core
Core, PackageKey
pkg)) Maybe ByteString
_ RunMode
_ -> do
    -- it's important to always rerun
    -- since the package definition is not tracked at all
    Action ()
alwaysRerun
    PackageKey -> Action (Maybe Package)
lookupPackage PackageKey
pkg forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
      Maybe Package
Nothing -> forall (m :: * -> *) a. MonadFail m => String -> m a
fail forall a b. (a -> b) -> a -> b
$ String
"Unkown package key: " forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> String
show PackageKey
pkg
      Just
        Package
          { _pversion :: Package -> CheckVersion
_pversion = CheckVersion VersionSource
versionSource NvcheckerOptions
options,
            _ppassthru :: Package -> PackagePassthru
_ppassthru = (PackagePassthru HashMap Text Text
passthru),
            Maybe PackageCargoLockFiles
Maybe PackageExtractSrc
Text
UseStaleVersion
DateFormat
ForceFetch
PackageFetcher
_pforcefetch :: Package -> ForceFetch
_pgitdateformat :: Package -> DateFormat
_ppinned :: Package -> UseStaleVersion
_pcargo :: Package -> Maybe PackageCargoLockFiles
_pextract :: Package -> Maybe PackageExtractSrc
_pfetcher :: Package -> PackageFetcher
_pname :: Package -> Text
_pforcefetch :: ForceFetch
_pgitdateformat :: DateFormat
_ppinned :: UseStaleVersion
_pcargo :: Maybe PackageCargoLockFiles
_pextract :: Maybe PackageExtractSrc
_pfetcher :: PackageFetcher
_pname :: Text
..
          } -> do
          _prversion :: NvcheckerResult
_prversion@(NvcheckerResult Version
version Maybe Version
_mOldV Bool
_isStale) <- VersionSource
-> NvcheckerOptions -> PackageKey -> Action NvcheckerResult
checkVersion VersionSource
versionSource NvcheckerOptions
options PackageKey
pkg
          NixFetcher 'Fetched
_prfetched <- NixFetcher 'Fresh -> ForceFetch -> Action (NixFetcher 'Fetched)
prefetch (PackageFetcher
_pfetcher Version
version) ForceFetch
_pforcefetch
          String
buildDir <- Action String
getBuildDir
          -- extract src
          Maybe (HashMap String Text)
_prextract <-
            case Maybe PackageExtractSrc
_pextract of
              Just (PackageExtractSrc NonEmpty String
extract) -> do
                [(String, Text)]
result <- forall k v. HashMap k v -> [(k, v)]
HMap.toList forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NixFetcher 'Fetched
-> NonEmpty String -> Action (HashMap String Text)
extractSrcs NixFetcher 'Fetched
_prfetched NonEmpty String
extract
                forall a. a -> Maybe a
Just forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HMap.fromList
                  forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence
                    [ do
                        -- write extracted files to build dir
                        -- and read them in nix using 'builtins.readFile'
                        forall (m :: * -> *).
(MonadIO m, Partial) =>
String -> String -> m ()
writeFile' (String
buildDir String -> String -> String
</> String
path) (Text -> String
T.unpack Text
v)
                        forall (f :: * -> *) a. Applicative f => a -> f a
pure (String
k, String -> Text
T.pack String
path)
                      | (String
k, Text
v) <- [(String, Text)]
result,
                        let path :: String
path =
                              String
"./"
                                forall a. Semigroup a => a -> a -> a
<> Text -> String
T.unpack Text
_pname
                                forall a. Semigroup a => a -> a -> a
<> String
"-"
                                forall a. Semigroup a => a -> a -> a
<> Text -> String
T.unpack (coerce :: forall a b. Coercible a b => a -> b
coerce Version
version)
                                String -> String -> String
</> String
k
                    ]
              Maybe PackageExtractSrc
_ -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing
          -- cargo locks
          Maybe (HashMap String (Text, HashMap Text Checksum))
_prcargolock <-
            case Maybe PackageCargoLockFiles
_pcargo of
              Just (PackageCargoLockFiles NonEmpty String
lockPath) -> do
                [(String, Text)]
lockFiles <- forall k v. HashMap k v -> [(k, v)]
HMap.toList forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NixFetcher 'Fetched
-> NonEmpty String -> Action (HashMap String Text)
extractSrcs NixFetcher 'Fetched
_prfetched NonEmpty String
lockPath
                [(String, (Text, HashMap Text Checksum))]
result <- forall a. [Action a] -> Action [a]
parallel forall a b. (a -> b) -> a -> b
$
                  forall a b c. (a -> b -> c) -> b -> a -> c
flip forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [(String, Text)]
lockFiles forall a b. (a -> b) -> a -> b
$ \(String
lockPath, Text
lockData) -> do
                    HashMap Text Checksum
result <- NixFetcher 'Fetched -> String -> Action (HashMap Text Checksum)
fetchRustGitDeps NixFetcher 'Fetched
_prfetched String
lockPath
                    let lockPath' :: String
lockPath' =
                          Text -> String
T.unpack Text
_pname
                            forall a. Semigroup a => a -> a -> a
<> String
"-"
                            forall a. Semigroup a => a -> a -> a
<> Text -> String
T.unpack (coerce :: forall a b. Coercible a b => a -> b
coerce Version
version)
                            String -> String -> String
</> String
lockPath
                        lockPathNix :: Text
lockPathNix = Text
"./" forall a. Semigroup a => a -> a -> a
<> String -> Text
T.pack String
lockPath'
                    -- similar to extract src, write lock file to build dir
                    forall (m :: * -> *).
(MonadIO m, Partial) =>
String -> String -> m ()
writeFile' (String
buildDir String -> String -> String
</> String
lockPath') forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack Text
lockData
                    forall (f :: * -> *) a. Applicative f => a -> f a
pure (String
lockPath, (Text
lockPathNix, HashMap Text Checksum
result))
                forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HMap.fromList [(String, (Text, HashMap Text Checksum))]
result
              Maybe PackageCargoLockFiles
_ -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing

          -- Only git version source supports git commit date
          Maybe Text
_prgitdate <- case VersionSource
versionSource of
            Git {Text
Branch
_vbranch :: VersionSource -> Branch
_vurl :: VersionSource -> Text
_vbranch :: Branch
_vurl :: Text
..} -> forall a. a -> Maybe a
Just forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Text -> DateFormat -> Action Text
getGitCommitDate Text
_vurl (coerce :: forall a b. Coercible a b => a -> b
coerce Version
version) DateFormat
_pgitdateformat
            VersionSource
_ -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing

          -- update changelog
          -- always use on disk version
          Maybe Version
mOldV <- PackageKey -> Action (Maybe Version)
getLastVersionOnDisk PackageKey
pkg
          case Maybe Version
mOldV of
            Maybe Version
Nothing ->
              Text -> Maybe Version -> Version -> Action ()
recordVersionChange Text
_pname forall a. Maybe a
Nothing Version
version
            Just Version
old
              | Version
old forall a. Eq a => a -> a -> Bool
/= Version
version ->
                Text -> Maybe Version -> Version -> Action ()
recordVersionChange Text
_pname (forall a. a -> Maybe a
Just Version
old) Version
version
            Maybe Version
_ -> forall (f :: * -> *) a. Applicative f => a -> f a
pure ()

          let _prpassthru :: Maybe (HashMap Text Text)
_prpassthru = if forall k v. HashMap k v -> Bool
HMap.null HashMap Text Text
passthru then forall a. Maybe a
Nothing else forall a. a -> Maybe a
Just HashMap Text Text
passthru
              _prname :: Text
_prname = Text
_pname
              _prpinned :: UseStaleVersion
_prpinned = UseStaleVersion
_ppinned
          -- Since we don't save the previous result, we are not able to know if the result changes
          -- Depending on this rule leads to RunDependenciesChanged
          forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall value. RunChanged -> ByteString -> value -> RunResult value
RunResult RunChanged
ChangedRecomputeDiff forall a. Monoid a => a
mempty PackageResult {Maybe Text
Maybe (HashMap String (Text, HashMap Text Checksum))
Maybe (HashMap String Text)
Maybe (HashMap Text Text)
Text
UseStaleVersion
NixFetcher 'Fetched
NvcheckerResult
_prgitdate :: Maybe Text
_prpinned :: UseStaleVersion
_prcargolock :: Maybe (HashMap String (Text, HashMap Text Checksum))
_prextract :: Maybe (HashMap String Text)
_prpassthru :: Maybe (HashMap Text Text)
_prfetched :: NixFetcher 'Fetched
_prversion :: NvcheckerResult
_prname :: Text
_prpinned :: UseStaleVersion
_prname :: Text
_prpassthru :: Maybe (HashMap Text Text)
_prgitdate :: Maybe Text
_prcargolock :: Maybe (HashMap String (Text, HashMap Text Checksum))
_prextract :: Maybe (HashMap String Text)
_prfetched :: NixFetcher 'Fetched
_prversion :: NvcheckerResult
..}

-- | 'Core' rule take a 'PackageKey', find the corresponding 'Package', and run all needed rules to get 'PackageResult'
runPackage :: PackageKey -> Action PackageResult
runPackage :: PackageKey -> Action PackageResult
runPackage PackageKey
k = forall key value.
(Partial, RuleResult key ~ value, ShakeValue key,
 Typeable value) =>
key -> Action value
apply1 forall a b. (a -> b) -> a -> b
$ forall k. (k, PackageKey) -> WithPackageKey k
WithPackageKey (Core
Core, PackageKey
k)