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

-- | Copyright: (c) 2021-2022 berberman
-- SPDX-License-Identifier: MIT
-- Maintainer: berberman <[email protected]>
-- Stability: experimental
-- Portability: portable
--
-- The main module of nvfetcher. If you want to create CLI program with it, it's enough to import only this module.
--
-- Example:
--
-- @
-- module Main where
--
-- import NvFetcher
--
-- main :: IO ()
-- main = runNvFetcher packageSet
--
-- packageSet :: PackageSet ()
-- packageSet = do
--   define $ package "feeluown-core" `fromPypi` "feeluown"
--   define $ package "qliveplayer" `fromGitHub` ("THMonster", "QLivePlayer")
-- @
--
-- You can find more examples of packages in @[email protected]
--
-- Running the created program:
--
-- * @[email protected] -- abbreviation of @main [email protected]
-- * @main [email protected] -- build nix sources expr from given @[email protected]
-- * @main cl[email protected] -- delete .shake dir and generated nix file
--
-- All shake options are inherited.
module NvFetcher
  ( runNvFetcher,
    runNvFetcher',
    runNvFetcherNoCLI,
    applyCliOptions,
    parseLastVersions,
    module NvFetcher.PackageSet,
    module NvFetcher.Types,
    module NvFetcher.Types.ShakeExtras,
  )
where

import Control.Monad.Extra (forM_, when, whenJust)
import qualified Data.Aeson as A
import qualified Data.Aeson.Encode.Pretty as A
import qualified Data.Aeson.Types as A
import qualified Data.ByteString.Lazy.Char8 as LBS
import Data.Default
import Data.List ((\\))
import qualified Data.Map.Strict as Map
import Data.Maybe (catMaybes, fromMaybe)
import Data.Text (Text)
import qualified Data.Text as T
import Development.Shake
import Development.Shake.FilePath
import NeatInterpolation (trimming)
import NvFetcher.Config
import NvFetcher.Core
import NvFetcher.NixExpr (ToNixExpr (toNixExpr))
import NvFetcher.NixFetcher
import NvFetcher.Nvchecker
import NvFetcher.Options
import NvFetcher.PackageSet
import NvFetcher.Types
import NvFetcher.Types.ShakeExtras
import NvFetcher.Utils (aesonKey, getDataDir)
import qualified System.Directory.Extra as D
import Text.Regex.TDFA ((=~))

-- | Run nvfetcher with CLI options
--
-- This function calls 'runNvFetcherNoCLI', using 'def' 'Config' overridden by 'CLIOptions'.
-- Use this function to create your own Haskell executable program.
runNvFetcher :: PackageSet () -> IO ()
runNvFetcher :: PackageSet () -> IO ()
runNvFetcher = Config -> PackageSet () -> IO ()
runNvFetcher' Config
forall a. Default a => a
def

-- | Similar to 'runNvFetcher', but uses custom @[email protected] instead of 'def' overridden by 'CLIOptions'
runNvFetcher' :: Config -> PackageSet () -> IO ()
runNvFetcher' :: Config -> PackageSet () -> IO ()
runNvFetcher' Config
config PackageSet ()
packageSet =
  Parser CLIOptions -> IO CLIOptions
forall a. Parser a -> IO a
getCLIOptions Parser CLIOptions
cliOptionsParser IO CLIOptions -> (CLIOptions -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \CLIOptions
cli -> Config -> Target -> PackageSet () -> IO ()
runNvFetcherNoCLI (Config -> CLIOptions -> Config
applyCliOptions Config
config CLIOptions
cli) (CLIOptions -> Target
optTarget CLIOptions
cli) PackageSet ()
packageSet

-- | Apply 'CLIOptions' to 'Config'
applyCliOptions :: Config -> CLIOptions -> Config
applyCliOptions :: Config -> CLIOptions -> Config
applyCliOptions Config
config CLIOptions {Bool
Int
FilePath
Maybe FilePath
Target
optPkgNameFilter :: CLIOptions -> Maybe FilePath
optVerbose :: CLIOptions -> Bool
optTiming :: CLIOptions -> Bool
optRetry :: CLIOptions -> Int
optThreads :: CLIOptions -> Int
optLogPath :: CLIOptions -> Maybe FilePath
optCommit :: CLIOptions -> Bool
optBuildDir :: CLIOptions -> FilePath
optTarget :: Target
optPkgNameFilter :: Maybe FilePath
optVerbose :: Bool
optTiming :: Bool
optRetry :: Int
optThreads :: Int
optLogPath :: Maybe FilePath
optCommit :: Bool
optBuildDir :: FilePath
optTarget :: CLIOptions -> Target
..} =
  Config
config
    { buildDir :: FilePath
buildDir = FilePath
optBuildDir,
      actionAfterBuild :: Action ()
actionAfterBuild = do
        Maybe FilePath -> (FilePath -> Action ()) -> Action ()
forall (m :: * -> *) a.
Applicative m =>
Maybe a -> (a -> m ()) -> m ()
whenJust Maybe FilePath
optLogPath FilePath -> Action ()
logChangesToFile
        Bool -> Action () -> Action ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
optCommit Action ()
commitChanges
        Config -> Action ()
actionAfterBuild Config
config,
      shakeConfig :: ShakeOptions
shakeConfig =
        (Config -> ShakeOptions
shakeConfig Config
config)
          { shakeTimings :: Bool
shakeTimings = Bool
optTiming,
            shakeVerbosity :: Verbosity
shakeVerbosity = if Bool
optVerbose then Verbosity
Verbose else Verbosity
Info,
            shakeThreads :: Int
shakeThreads = Int
optThreads
          },
      filterRegex :: Maybe FilePath
filterRegex = Maybe FilePath
optPkgNameFilter,
      retry :: Int
retry = Int
optRetry
    }

logChangesToFile :: FilePath -> Action ()
logChangesToFile :: FilePath -> Action ()
logChangesToFile FilePath
fp = do
  [VersionChange]
changes <- Action [VersionChange]
getVersionChanges
  FilePath -> FilePath -> Action ()
forall (m :: * -> *).
(MonadIO m, Located) =>
FilePath -> FilePath -> m ()
writeFile' FilePath
fp (FilePath -> Action ()) -> FilePath -> Action ()
forall a b. (a -> b) -> a -> b
$ [FilePath] -> FilePath
unlines ([FilePath] -> FilePath) -> [FilePath] -> FilePath
forall a b. (a -> b) -> a -> b
$ VersionChange -> FilePath
forall a. Show a => a -> FilePath
show (VersionChange -> FilePath) -> [VersionChange] -> [FilePath]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [VersionChange]
changes

commitChanges :: Action ()
commitChanges :: Action ()
commitChanges = do
  [VersionChange]
changes <- Action [VersionChange]
getVersionChanges
  let commitMsg :: Maybe FilePath
commitMsg = case [VersionChange]
changes of
        [VersionChange
x] -> FilePath -> Maybe FilePath
forall a. a -> Maybe a
Just (FilePath -> Maybe FilePath) -> FilePath -> Maybe FilePath
forall a b. (a -> b) -> a -> b
$ VersionChange -> FilePath
forall a. Show a => a -> FilePath
show VersionChange
x
        xs :: [VersionChange]
xs@(VersionChange
_ : [VersionChange]
_) -> FilePath -> Maybe FilePath
forall a. a -> Maybe a
Just (FilePath -> Maybe FilePath) -> FilePath -> Maybe FilePath
forall a b. (a -> b) -> a -> b
$ FilePath
"Update\n" FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> [FilePath] -> FilePath
unlines (VersionChange -> FilePath
forall a. Show a => a -> FilePath
show (VersionChange -> FilePath) -> [VersionChange] -> [FilePath]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [VersionChange]
xs)
        [] -> Maybe FilePath
forall a. Maybe a
Nothing
  Maybe FilePath -> (FilePath -> Action ()) -> Action ()
forall (m :: * -> *) a.
Applicative m =>
Maybe a -> (a -> m ()) -> m ()
whenJust Maybe FilePath
commitMsg ((FilePath -> Action ()) -> Action ())
-> (FilePath -> Action ()) -> Action ()
forall a b. (a -> b) -> a -> b
$ \FilePath
msg -> do
    FilePath -> Action ()
putInfo FilePath
"Commiting changes"
    Action FilePath
getBuildDir Action FilePath -> (FilePath -> Action ()) -> Action ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \FilePath
dir -> Located => [CmdOption] -> FilePath -> [FilePath] -> Action ()
[CmdOption] -> FilePath -> [FilePath] -> Action ()
command_ [] FilePath
"git" [FilePath
"add", FilePath
dir]
    Located => [CmdOption] -> FilePath -> [FilePath] -> Action ()
[CmdOption] -> FilePath -> [FilePath] -> Action ()
command_ [] FilePath
"git" [FilePath
"commit", FilePath
"-m", FilePath
msg]

-- | @Parse [email protected]
parseLastVersions :: FilePath -> IO (Maybe (Map.Map PackageKey Version))
parseLastVersions :: FilePath -> IO (Maybe (Map PackageKey Version))
parseLastVersions FilePath
jsonFile =
  FilePath -> IO Bool
D.doesFileExist FilePath
jsonFile IO Bool
-> (Bool -> IO (Maybe (Map PackageKey Version)))
-> IO (Maybe (Map PackageKey Version))
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Bool
True -> do
      Maybe (Map PackageName Object)
objs <- FilePath -> IO (Maybe (Map PackageName Object))
forall a. FromJSON a => FilePath -> IO (Maybe a)
A.decodeFileStrict' FilePath
jsonFile
      Maybe (Map PackageKey Version)
-> IO (Maybe (Map PackageKey Version))
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe (Map PackageKey Version)
 -> IO (Maybe (Map PackageKey Version)))
-> Maybe (Map PackageKey Version)
-> IO (Maybe (Map PackageKey Version))
forall a b. (a -> b) -> a -> b
$
        ((Map PackageName Object -> Map PackageKey Version)
 -> Maybe (Map PackageName Object)
 -> Maybe (Map PackageKey Version))
-> Maybe (Map PackageName Object)
-> (Map PackageName Object -> Map PackageKey Version)
-> Maybe (Map PackageKey Version)
forall a b c. (a -> b -> c) -> b -> a -> c
flip (Map PackageName Object -> Map PackageKey Version)
-> Maybe (Map PackageName Object) -> Maybe (Map PackageKey Version)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Maybe (Map PackageName Object)
objs ((Map PackageName Object -> Map PackageKey Version)
 -> Maybe (Map PackageKey Version))
-> (Map PackageName Object -> Map PackageKey Version)
-> Maybe (Map PackageKey Version)
forall a b. (a -> b) -> a -> b
$
          ( \[(PackageName, Object)]
xs ->
              [(PackageKey, Version)] -> Map PackageKey Version
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList
                ([(PackageKey, Version)] -> Map PackageKey Version)
-> ([Maybe (PackageKey, Version)] -> [(PackageKey, Version)])
-> [Maybe (PackageKey, Version)]
-> Map PackageKey Version
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Maybe (PackageKey, Version)] -> [(PackageKey, Version)]
forall a. [Maybe a] -> [a]
catMaybes
                ([Maybe (PackageKey, Version)] -> Map PackageKey Version)
-> [Maybe (PackageKey, Version)] -> Map PackageKey Version
forall a b. (a -> b) -> a -> b
$ [(PackageName -> PackageKey
PackageKey PackageName
k,) (Version -> (PackageKey, Version))
-> Maybe Version -> Maybe (PackageKey, Version)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Object -> Parser Version) -> Object -> Maybe Version
forall a b. (a -> Parser b) -> a -> Maybe b
A.parseMaybe (Object -> Key -> Parser Version
forall a. FromJSON a => Object -> Key -> Parser a
A..: Key
"version") Object
obj | (PackageName
k, Object
obj) <- [(PackageName, Object)]
xs]
          )
            ([(PackageName, Object)] -> Map PackageKey Version)
-> (Map PackageName Object -> [(PackageName, Object)])
-> Map PackageName Object
-> Map PackageKey Version
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Map PackageName Object -> [(PackageName, Object)]
forall k a. Map k a -> [(k, a)]
Map.toList
    Bool
_ -> Maybe (Map PackageKey Version)
-> IO (Maybe (Map PackageKey Version))
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe (Map PackageKey Version)
forall a. Monoid a => a
mempty

-- | Entry point of nvfetcher
runNvFetcherNoCLI :: Config -> Target -> PackageSet () -> IO ()
runNvFetcherNoCLI :: Config -> Target -> PackageSet () -> IO ()
runNvFetcherNoCLI config :: Config
config@Config {Bool
Int
FilePath
Maybe FilePath
Rules ()
Action ()
ShakeOptions
cacheNvchecker :: Config -> Bool
actionAfterClean :: Config -> Action ()
customRules :: Config -> Rules ()
cacheNvchecker :: Bool
filterRegex :: Maybe FilePath
retry :: Int
actionAfterClean :: Action ()
actionAfterBuild :: Action ()
customRules :: Rules ()
buildDir :: FilePath
shakeConfig :: ShakeOptions
retry :: Config -> Int
filterRegex :: Config -> Maybe FilePath
shakeConfig :: Config -> ShakeOptions
actionAfterBuild :: Config -> Action ()
buildDir :: Config -> FilePath
..} Target
target PackageSet ()
packageSet = do
  Map PackageKey Package
pkgs <- (Package -> Package)
-> Map PackageKey Package -> Map PackageKey Package
forall a b k. (a -> b) -> Map k a -> Map k b
Map.map Package -> Package
pinIfUnmatch (Map PackageKey Package -> Map PackageKey Package)
-> IO (Map PackageKey Package) -> IO (Map PackageKey Package)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> PackageSet () -> IO (Map PackageKey Package)
runPackageSet PackageSet ()
packageSet
  Maybe (Map PackageKey Version)
lastVersions <- FilePath -> IO (Maybe (Map PackageKey Version))
parseLastVersions (FilePath -> IO (Maybe (Map PackageKey Version)))
-> FilePath -> IO (Maybe (Map PackageKey Version))
forall a b. (a -> b) -> a -> b
$ FilePath
buildDir FilePath -> FilePath -> FilePath
</> FilePath
generatedJsonFileName
  FilePath
shakeDir <- IO FilePath
getDataDir
  -- Set shakeFiles
  let shakeOptions1 :: ShakeOptions
shakeOptions1 = ShakeOptions
shakeConfig {shakeFiles :: FilePath
shakeFiles = FilePath
shakeDir}
  -- shakeConfig in Config will be shakeOptions1 (not including shake extra)
  ShakeExtras
shakeExtras <- Config
-> Map PackageKey Package
-> Map PackageKey Version
-> IO ShakeExtras
initShakeExtras (Config
config {shakeConfig :: ShakeOptions
shakeConfig = ShakeOptions
shakeOptions1}) Map PackageKey Package
pkgs (Map PackageKey Version -> IO ShakeExtras)
-> Map PackageKey Version -> IO ShakeExtras
forall a b. (a -> b) -> a -> b
$ Map PackageKey Version
-> Maybe (Map PackageKey Version) -> Map PackageKey Version
forall a. a -> Maybe a -> a
fromMaybe Map PackageKey Version
forall a. Monoid a => a
mempty Maybe (Map PackageKey Version)
lastVersions
  -- Set shakeExtra
  let shakeOptions2 :: ShakeOptions
shakeOptions2 = ShakeOptions
shakeOptions1 {shakeExtra :: HashMap TypeRep Dynamic
shakeExtra = ShakeExtras -> HashMap TypeRep Dynamic -> HashMap TypeRep Dynamic
forall a.
Typeable a =>
a -> HashMap TypeRep Dynamic -> HashMap TypeRep Dynamic
addShakeExtra ShakeExtras
shakeExtras (ShakeOptions -> HashMap TypeRep Dynamic
shakeExtra ShakeOptions
shakeConfig)}
      rules :: Rules ()
rules = Config -> Rules ()
mainRules Config
config
  ShakeOptions -> Rules () -> IO ()
shake ShakeOptions
shakeOptions2 (Rules () -> IO ()) -> Rules () -> IO ()
forall a b. (a -> b) -> a -> b
$ Located => [FilePath] -> Rules ()
[FilePath] -> Rules ()
want [Target -> FilePath
forall a. Show a => a -> FilePath
show Target
target] Rules () -> Rules () -> Rules ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Rules ()
rules
  where
    -- Don't touch already pinned packages
    pinIfUnmatch :: Package -> Package
pinIfUnmatch x :: Package
x@Package {Maybe PackageCargoLockFiles
Maybe PackageExtractSrc
PackageName
UseStaleVersion
PackagePassthru
CheckVersion
PackageFetcher
_ppinned :: Package -> UseStaleVersion
_ppassthru :: Package -> PackagePassthru
_pcargo :: Package -> Maybe PackageCargoLockFiles
_pextract :: Package -> Maybe PackageExtractSrc
_pfetcher :: Package -> PackageFetcher
_pversion :: Package -> CheckVersion
_pname :: Package -> PackageName
_ppinned :: UseStaleVersion
_ppassthru :: PackagePassthru
_pcargo :: Maybe PackageCargoLockFiles
_pextract :: Maybe PackageExtractSrc
_pfetcher :: PackageFetcher
_pversion :: CheckVersion
_pname :: PackageName
..}
      | Just FilePath
regex <- Maybe FilePath
filterRegex =
        Package
x
          { _ppinned :: UseStaleVersion
_ppinned = case UseStaleVersion
_ppinned of
              UseStaleVersion
PermanentStale -> UseStaleVersion
PermanentStale
              UseStaleVersion
_ ->
                if PackageName
_pname PackageName -> FilePath -> Bool
forall source source1 target.
(RegexMaker Regex CompOption ExecOption source,
 RegexContext Regex source1 target) =>
source1 -> source -> target
=~ FilePath
regex
                  then UseStaleVersion
NoStale
                  else UseStaleVersion
TemporaryStale
          }
      | Bool
otherwise = Package
x

--------------------------------------------------------------------------------

mainRules :: Config -> Rules ()
mainRules :: Config -> Rules ()
mainRules Config {Bool
Int
FilePath
Maybe FilePath
Rules ()
Action ()
ShakeOptions
cacheNvchecker :: Bool
filterRegex :: Maybe FilePath
retry :: Int
actionAfterClean :: Action ()
actionAfterBuild :: Action ()
customRules :: Rules ()
buildDir :: FilePath
shakeConfig :: ShakeOptions
cacheNvchecker :: Config -> Bool
actionAfterClean :: Config -> Action ()
customRules :: Config -> Rules ()
retry :: Config -> Int
filterRegex :: Config -> Maybe FilePath
shakeConfig :: Config -> ShakeOptions
actionAfterBuild :: Config -> Action ()
buildDir :: Config -> FilePath
..} = do
  FilePath
"clean" Located => FilePath -> Action () -> Rules ()
FilePath -> Action () -> Rules ()
~> do
    Action FilePath
getBuildDir Action FilePath -> (FilePath -> Action ()) -> Action ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (FilePath -> [FilePath] -> Action ())
-> [FilePath] -> FilePath -> Action ()
forall a b c. (a -> b -> c) -> b -> a -> c
flip FilePath -> [FilePath] -> Action ()
removeFilesAfter [FilePath
"//*"]
    Action ()
actionAfterClean

  FilePath
"build" Located => FilePath -> Action () -> Rules ()
FilePath -> Action () -> Rules ()
~> do
    [PackageKey]
allKeys <- Action [PackageKey]
getAllPackageKeys
    [PackageResult]
results <- [Action PackageResult] -> Action [PackageResult]
forall a. [Action a] -> Action [a]
parallel ([Action PackageResult] -> Action [PackageResult])
-> [Action PackageResult] -> Action [PackageResult]
forall a b. (a -> b) -> a -> b
$ PackageKey -> Action PackageResult
runPackage (PackageKey -> Action PackageResult)
-> [PackageKey] -> [Action PackageResult]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [PackageKey]
allKeys
    -- Record removed packages to version changes
    Action (Map PackageKey Version)
getAllOnDiskVersions
      Action (Map PackageKey Version)
-> (Map PackageKey Version -> Action ()) -> Action ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \Map PackageKey Version
oldPkgs -> [PackageKey] -> (PackageKey -> Action ()) -> Action ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ (Map PackageKey Version -> [PackageKey]
forall k a. Map k a -> [k]
Map.keys Map PackageKey Version
oldPkgs [PackageKey] -> [PackageKey] -> [PackageKey]
forall a. Eq a => [a] -> [a] -> [a]
\\ [PackageKey]
allKeys) ((PackageKey -> Action ()) -> Action ())
-> (PackageKey -> Action ()) -> Action ()
forall a b. (a -> b) -> a -> b
$
        \PackageKey
pkg -> PackageName -> Maybe Version -> Version -> Action ()
recordVersionChange (PackageKey -> PackageName
coerce PackageKey
pkg) (Map PackageKey Version
oldPkgs Map PackageKey Version -> PackageKey -> Maybe Version
forall k a. Ord k => Map k a -> k -> Maybe a
Map.!? PackageKey
pkg) Version
"∅"
    Action [VersionChange]
getVersionChanges Action [VersionChange]
-> ([VersionChange] -> Action ()) -> Action ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \[VersionChange]
changes ->
      if [VersionChange] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [VersionChange]
changes
        then FilePath -> Action ()
putInfo FilePath
"Up to date"
        else do
          FilePath -> Action ()
putInfo FilePath
"Changes:"
          FilePath -> Action ()
putInfo (FilePath -> Action ()) -> FilePath -> Action ()
forall a b. (a -> b) -> a -> b
$ [FilePath] -> FilePath
unlines ([FilePath] -> FilePath) -> [FilePath] -> FilePath
forall a b. (a -> b) -> a -> b
$ VersionChange -> FilePath
forall a. Show a => a -> FilePath
show (VersionChange -> FilePath) -> [VersionChange] -> [FilePath]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [VersionChange]
changes
    FilePath
buildDir <- Action FilePath
getBuildDir
    let generatedNixPath :: FilePath
generatedNixPath = FilePath
buildDir FilePath -> FilePath -> FilePath
</> FilePath
generatedNixFileName
        generatedJSONPath :: FilePath
generatedJSONPath = FilePath
buildDir FilePath -> FilePath -> FilePath
</> FilePath
generatedJsonFileName
    FilePath -> Action ()
putVerbose (FilePath -> Action ()) -> FilePath -> Action ()
forall a b. (a -> b) -> a -> b
$ FilePath
"Generating " FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath
generatedNixPath
    FilePath -> FilePath -> Action ()
forall (m :: * -> *).
(MonadIO m, Located) =>
FilePath -> FilePath -> m ()
writeFileChanged FilePath
generatedNixPath (FilePath -> Action ()) -> FilePath -> Action ()
forall a b. (a -> b) -> a -> b
$ PackageName -> FilePath
T.unpack (PackageName -> FilePath) -> PackageName -> FilePath
forall a b. (a -> b) -> a -> b
$ PackageName -> PackageName
srouces ([PackageName] -> PackageName
T.unlines ([PackageName] -> PackageName) -> [PackageName] -> PackageName
forall a b. (a -> b) -> a -> b
$ PackageResult -> PackageName
forall a. ToNixExpr a => a -> PackageName
toNixExpr (PackageResult -> PackageName) -> [PackageResult] -> [PackageName]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [PackageResult]
results) PackageName -> PackageName -> PackageName
forall a. Semigroup a => a -> a -> a
<> PackageName
"\n"
    FilePath -> Action ()
putVerbose (FilePath -> Action ()) -> FilePath -> Action ()
forall a b. (a -> b) -> a -> b
$ FilePath
"Generating " FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath
generatedJSONPath
    FilePath -> FilePath -> Action ()
forall (m :: * -> *).
(MonadIO m, Located) =>
FilePath -> FilePath -> m ()
writeFileChanged FilePath
generatedJSONPath (FilePath -> Action ()) -> FilePath -> Action ()
forall a b. (a -> b) -> a -> b
$ ByteString -> FilePath
LBS.unpack (ByteString -> FilePath) -> ByteString -> FilePath
forall a b. (a -> b) -> a -> b
$ Value -> ByteString
forall a. ToJSON a => a -> ByteString
A.encodePretty (Value -> ByteString) -> Value -> ByteString
forall a b. (a -> b) -> a -> b
$ [Pair] -> Value
A.object [PackageName -> Key
aesonKey (PackageResult -> PackageName
_prname PackageResult
r) Key -> PackageResult -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
A..= PackageResult
r | PackageResult
r <- [PackageResult]
results]
    Action ()
actionAfterBuild

  Rules ()
customRules
  Rules ()
coreRules

srouces :: Text -> Text
srouces :: PackageName -> PackageName
srouces PackageName
body =
  [trimming|
    # This file was generated by nvfetcher, please do not modify it manually.
    { fetchgit, fetchurl, fetchFromGitHub }:
    {
      $body
    }
  |]

generatedNixFileName :: String
generatedNixFileName :: FilePath
generatedNixFileName = FilePath
"generated.nix"

generatedJsonFileName :: String
generatedJsonFileName :: FilePath
generatedJsonFileName = FilePath
"generated.json"