{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}

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

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

-- | The core rule of nvchecker.
-- all rules are wired here.
coreRules :: Rules ()
coreRules :: Rules ()
coreRules = do
  Rules ()
nvcheckerRule
  Rules ()
prefetchRule
  Rules ()
extractSrcRule
  Rules ()
fetchRustGitDepsRule
  BuiltinLint (WithPackageKey Core) NixExpr
-> BuiltinIdentity (WithPackageKey Core) NixExpr
-> BuiltinRun (WithPackageKey Core) NixExpr
-> Rules ()
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 BuiltinLint (WithPackageKey Core) NixExpr
forall key value. BuiltinLint key value
noLint BuiltinIdentity (WithPackageKey Core) NixExpr
forall key value. BuiltinIdentity key value
noIdentity (BuiltinRun (WithPackageKey Core) NixExpr -> Rules ())
-> BuiltinRun (WithPackageKey Core) NixExpr -> Rules ()
forall a b. (a -> b) -> a -> b
$ \(WithPackageKey (Core
Core, PackageKey
pkg)) Maybe ByteString
mOld RunMode
mode -> case RunMode
mode of
    RunMode
RunDependenciesSame
      | Just ByteString
old <- Maybe ByteString
mOld,
        (NixExpr
expr, Version
_) <- ByteString -> (NixExpr, Version)
forall a. Binary a => ByteString -> a
decode' @(NixExpr, Version) ByteString
old ->
        RunResult NixExpr -> Action (RunResult NixExpr)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (RunResult NixExpr -> Action (RunResult NixExpr))
-> RunResult NixExpr -> Action (RunResult NixExpr)
forall a b. (a -> b) -> a -> b
$ RunChanged -> ByteString -> NixExpr -> RunResult NixExpr
forall value. RunChanged -> ByteString -> value -> RunResult value
RunResult RunChanged
ChangedNothing ByteString
old NixExpr
expr
    RunMode
_ ->
      PackageKey -> Action (Maybe Package)
lookupPackage PackageKey
pkg Action (Maybe Package)
-> (Maybe Package -> Action (RunResult NixExpr))
-> Action (RunResult NixExpr)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        Maybe Package
Nothing -> String -> Action (RunResult NixExpr)
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> Action (RunResult NixExpr))
-> String -> Action (RunResult NixExpr)
forall a b. (a -> b) -> a -> b
$ String
"Unkown package key: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> PackageKey -> String
forall a. Show a => a -> String
show PackageKey
pkg
        Just
          Package
            { _pversion :: Package -> NvcheckerQ
_pversion = NvcheckerQ VersionSource
versionSource NvcheckerOptions
options,
              _pextract :: Package -> PackageExtractSrc
_pextract = PackageExtractSrc [String]
extract,
              Maybe PackageCargoFilePath
NixExpr
PackageFetcher
_pcargo :: Package -> Maybe PackageCargoFilePath
_pfetcher :: Package -> PackageFetcher
_pname :: Package -> NixExpr
_pcargo :: Maybe PackageCargoFilePath
_pfetcher :: PackageFetcher
_pname :: NixExpr
..
            } -> do
            (NvcheckerA Version
version Maybe Version
mOldV) <- VersionSource
-> NvcheckerOptions -> PackageKey -> Action NvcheckerA
checkVersion VersionSource
versionSource NvcheckerOptions
options PackageKey
pkg
            NixFetcher 'Fetched
prefetched <- NixFetcher 'Fresh -> Action (NixFetcher 'Fetched)
prefetch (NixFetcher 'Fresh -> Action (NixFetcher 'Fetched))
-> NixFetcher 'Fresh -> Action (NixFetcher 'Fetched)
forall a b. (a -> b) -> a -> b
$ PackageFetcher
_pfetcher Version
version
            String
shakeDir <- Action String
getShakeDir
            NixExpr
appending1 <-
              if [String] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [String]
extract
                then NixExpr -> Action NixExpr
forall (f :: * -> *) a. Applicative f => a -> f a
pure NixExpr
""
                else do
                  [(String, NixExpr)]
result <- HashMap String NixExpr -> [(String, NixExpr)]
forall k v. HashMap k v -> [(k, v)]
HMap.toList (HashMap String NixExpr -> [(String, NixExpr)])
-> Action (HashMap String NixExpr) -> Action [(String, NixExpr)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NixFetcher 'Fetched -> [String] -> Action (HashMap String NixExpr)
extractSrc NixFetcher 'Fetched
prefetched [String]
extract
                  [NixExpr] -> NixExpr
T.unlines
                    ([NixExpr] -> NixExpr) -> Action [NixExpr] -> Action NixExpr
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Action NixExpr] -> Action [NixExpr]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence
                      [ do
                          -- write extracted files to shake dir
                          -- and read them in nix using 'builtins.readFile'
                          String -> String -> Action ()
forall (m :: * -> *).
(MonadIO m, Partial) =>
String -> String -> m ()
writeFile' (String
shakeDir String -> String -> String
</> String
path) (NixExpr -> String
T.unpack NixExpr
v)
                          NixExpr -> Action NixExpr
forall (f :: * -> *) a. Applicative f => a -> f a
pure (NixExpr -> Action NixExpr) -> NixExpr -> Action NixExpr
forall a b. (a -> b) -> a -> b
$ String -> NixExpr
forall a. ToNixExpr a => a -> NixExpr
toNixExpr String
k NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
" = builtins.readFile ./" NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> String -> NixExpr
T.pack String
path
                        | (String
k, NixExpr
v) <- [(String, NixExpr)]
result,
                          let path :: String
path =
                                NixExpr -> String
T.unpack NixExpr
_pname
                                  String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"-"
                                  String -> String -> String
forall a. Semigroup a => a -> a -> a
<> NixExpr -> String
T.unpack (Version -> NixExpr
coerce Version
version)
                                  String -> String -> String
</> String
k
                      ]
            NixExpr
appending2 <-
              case Maybe PackageCargoFilePath
_pcargo of
                Just (PackageCargoFilePath String
lockPath) -> do
                  (String
_, NixExpr
lockData) <- [(String, NixExpr)] -> (String, NixExpr)
forall a. [a] -> a
head ([(String, NixExpr)] -> (String, NixExpr))
-> (HashMap String NixExpr -> [(String, NixExpr)])
-> HashMap String NixExpr
-> (String, NixExpr)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HashMap String NixExpr -> [(String, NixExpr)]
forall k v. HashMap k v -> [(k, v)]
HMap.toList (HashMap String NixExpr -> (String, NixExpr))
-> Action (HashMap String NixExpr) -> Action (String, NixExpr)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NixFetcher 'Fetched -> [String] -> Action (HashMap String NixExpr)
extractSrc NixFetcher 'Fetched
prefetched [String
lockPath]
                  HashMap NixExpr Checksum
result <- NixFetcher 'Fetched -> String -> Action (HashMap NixExpr Checksum)
fetchRustGitDeps NixFetcher 'Fetched
prefetched String
lockPath
                  let body :: NixExpr
body = [NixExpr] -> NixExpr
T.unlines [NixExpr -> NixExpr
asString NixExpr
k NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
" = " NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr -> NixExpr
coerce (NixExpr -> NixExpr
asString (NixExpr -> NixExpr) -> NixExpr -> NixExpr
forall a b. (a -> b) -> a -> b
$ Checksum -> NixExpr
coerce Checksum
v) NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
";" | (NixExpr
k, Checksum
v) <- HashMap NixExpr Checksum -> [(NixExpr, Checksum)]
forall k v. HashMap k v -> [(k, v)]
HMap.toList HashMap NixExpr Checksum
result]
                      lockPath' :: String
lockPath' =
                        NixExpr -> String
T.unpack NixExpr
_pname
                          String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"-"
                          String -> String -> String
forall a. Semigroup a => a -> a -> a
<> NixExpr -> String
T.unpack (Version -> NixExpr
coerce Version
version)
                          String -> String -> String
</> String
lockPath
                      lockPathNix :: NixExpr
lockPathNix = NixExpr
"./" NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> String -> NixExpr
T.pack String
lockPath'
                  -- similar to extract src, write lock file to shake dir
                  String -> String -> Action ()
forall (m :: * -> *).
(MonadIO m, Partial) =>
String -> String -> m ()
writeFile' (String
shakeDir String -> String -> String
</> String
lockPath') (String -> Action ()) -> String -> Action ()
forall a b. (a -> b) -> a -> b
$ NixExpr -> String
T.unpack NixExpr
lockData
                  NixExpr -> Action NixExpr
forall (f :: * -> *) a. Applicative f => a -> f a
pure
                    [trimming|
                  cargoLock = {
                    lockFile = $lockPathNix;
                    outputHashes = {
                      $body
                    };
                  };
                |]
                Maybe PackageCargoFilePath
_ -> NixExpr -> Action NixExpr
forall (f :: * -> *) a. Applicative f => a -> f a
pure NixExpr
""
            case Maybe Version
mOldV of
              Maybe Version
Nothing ->
                NixExpr -> Maybe Version -> Version -> Action ()
recordVersionChange NixExpr
_pname Maybe Version
forall a. Maybe a
Nothing Version
version
              Just Version
old
                | Version
old Version -> Version -> Bool
forall a. Eq a => a -> a -> Bool
/= Version
version ->
                  NixExpr -> Maybe Version -> Version -> Action ()
recordVersionChange NixExpr
_pname (Version -> Maybe Version
forall a. a -> Maybe a
Just Version
old) Version
version
              Maybe Version
_ -> () -> Action ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
            let result :: NixExpr
result = NixExpr -> Version -> NixFetcher 'Fetched -> NixExpr -> NixExpr
gen NixExpr
_pname Version
version NixFetcher 'Fetched
prefetched (NixExpr -> NixExpr) -> NixExpr -> NixExpr
forall a b. (a -> b) -> a -> b
$ NixExpr
appending1 NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
appending2
            RunResult NixExpr -> Action (RunResult NixExpr)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (RunResult NixExpr -> Action (RunResult NixExpr))
-> RunResult NixExpr -> Action (RunResult NixExpr)
forall a b. (a -> b) -> a -> b
$ RunChanged -> ByteString -> NixExpr -> RunResult NixExpr
forall value. RunChanged -> ByteString -> value -> RunResult value
RunResult RunChanged
ChangedRecomputeDiff ((NixExpr, Version) -> ByteString
forall a. Binary a => a -> ByteString
encode' (NixExpr
result, Version
version)) NixExpr
result

-- | Run the core rule.
-- Given a 'PackageKey', run "NvFetcher.Nvchecker", "NvFetcher.NixFetcher"
-- (may also run "NvFetcher.ExtractSrc" or "FetchRustGitDeps")
-- resulting a nix source snippet like:
--
-- @
-- feeluown-core = {
--     pname = "feeluown-core";
--     version = "3.7.7";
--     src = fetchurl {
--       sha256 = "06d3j39ff9znqxkhp9ly81lcgajkhg30hyqxy2809yn23xixg3x2";
--       url = "https://pypi.io/packages/source/f/feeluown/feeluown-3.7.7.tar.gz";
--     };
--   };
-- @
generateNixSourceExpr :: PackageKey -> Action NixExpr
generateNixSourceExpr :: PackageKey -> Action NixExpr
generateNixSourceExpr PackageKey
k = WithPackageKey Core -> Action NixExpr
forall key value.
(Partial, RuleResult key ~ value, ShakeValue key,
 Typeable value) =>
key -> Action value
apply1 (WithPackageKey Core -> Action NixExpr)
-> WithPackageKey Core -> Action NixExpr
forall a b. (a -> b) -> a -> b
$ (Core, PackageKey) -> WithPackageKey Core
forall k. (k, PackageKey) -> WithPackageKey k
WithPackageKey (Core
Core, PackageKey
k)

gen :: PackageName -> Version -> NixFetcher Fetched -> Text -> Text
gen :: NixExpr -> Version -> NixFetcher 'Fetched -> NixExpr -> NixExpr
gen NixExpr
name (Version -> NixExpr
coerce -> NixExpr
ver) (NixFetcher 'Fetched -> NixExpr
forall a. ToNixExpr a => a -> NixExpr
toNixExpr -> NixExpr
srcP) NixExpr
appending =
  [trimming|
  $name = {
    pname = "$name";
    version = "$ver";
    src = $srcP;
    $appending
  };
|]