{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ViewPatterns #-}

-- | Copyright: (c) 2021-2022 berberman
-- SPDX-License-Identifier: MIT
-- Maintainer: berberman <[email protected]>
-- Stability: experimental
-- Portability: portable
--
-- This module contains a type class 'ToNixExpr' and some its instances associated with either Haskell
-- primitive types or our "NvFetcher.Types".
module NvFetcher.NixExpr
  ( NixExpr,
    ToNixExpr (..),
    fetcherToDrv,
  )
where

import Data.Coerce (coerce)
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HMap
import qualified Data.List.NonEmpty as NE
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import qualified Data.Text as T
import NeatInterpolation (trimming)
import NvFetcher.Types
import NvFetcher.Utils (quote, quoteIfNeeds)

-- | Types can be converted into nix expr
class ToNixExpr a where
  toNixExpr :: a -> NixExpr

instance ToNixExpr (NixFetcher Fresh) where
  toNixExpr :: NixFetcher 'Fresh -> NixExpr
toNixExpr = NixExpr -> NixFetcher 'Fresh -> NixExpr
forall (k :: FetchStatus). NixExpr -> NixFetcher k -> NixExpr
nixFetcher NixExpr
"lib.fakeSha256"

instance ToNixExpr (NixFetcher Fetched) where
  toNixExpr :: NixFetcher 'Fetched -> NixExpr
toNixExpr NixFetcher 'Fetched
f = NixExpr -> NixFetcher 'Fetched -> NixExpr
forall (k :: FetchStatus). NixExpr -> NixFetcher k -> NixExpr
nixFetcher (NixExpr -> NixExpr
quote (NixExpr -> NixExpr) -> NixExpr -> NixExpr
forall a b. (a -> b) -> a -> b
$ Checksum -> NixExpr
coerce (Checksum -> NixExpr) -> Checksum -> NixExpr
forall a b. (a -> b) -> a -> b
$ NixFetcher 'Fetched -> FetchResult 'Fetched
forall (k :: FetchStatus). NixFetcher k -> FetchResult k
_sha256 NixFetcher 'Fetched
f) NixFetcher 'Fetched
f

instance ToNixExpr Bool where
  toNixExpr :: Bool -> NixExpr
toNixExpr Bool
True = NixExpr
"true"
  toNixExpr Bool
False = NixExpr
"false"

instance ToNixExpr a => ToNixExpr [a] where
  toNixExpr :: [a] -> NixExpr
toNixExpr [a]
xs = (NixExpr -> a -> NixExpr) -> NixExpr -> [a] -> NixExpr
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl (\NixExpr
acc a
x -> NixExpr
acc NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
" " NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> a -> NixExpr
forall a. ToNixExpr a => a -> NixExpr
toNixExpr a
x) NixExpr
"[" [a]
xs NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
" ]"

instance ToNixExpr a => ToNixExpr (NE.NonEmpty a) where
  toNixExpr :: NonEmpty a -> NixExpr
toNixExpr = [a] -> NixExpr
forall a. ToNixExpr a => a -> NixExpr
toNixExpr ([a] -> NixExpr) -> (NonEmpty a -> [a]) -> NonEmpty a -> NixExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NonEmpty a -> [a]
forall a. NonEmpty a -> [a]
NE.toList

instance {-# OVERLAPS #-} ToNixExpr String where
  toNixExpr :: String -> NixExpr
toNixExpr = String -> NixExpr
T.pack (String -> NixExpr) -> (String -> String) -> String -> NixExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
forall a. Show a => a -> String
show

instance ToNixExpr NixExpr where
  toNixExpr :: NixExpr -> NixExpr
toNixExpr = NixExpr -> NixExpr
forall a. a -> a
id

instance ToNixExpr Version where
  toNixExpr :: Version -> NixExpr
toNixExpr = Version -> NixExpr
coerce

nixFetcher :: Text -> NixFetcher k -> NixExpr
nixFetcher :: forall (k :: FetchStatus). NixExpr -> NixFetcher k -> NixExpr
nixFetcher NixExpr
sha256 = \case
  FetchGit
    { _sha256 :: forall (k :: FetchStatus). NixFetcher k -> FetchResult k
_sha256 = FetchResult k
_,
      _rev :: forall (k :: FetchStatus). NixFetcher k -> Version
_rev = NixExpr -> NixExpr
quote (NixExpr -> NixExpr) -> (Version -> NixExpr) -> Version -> NixExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Version -> NixExpr
forall a. ToNixExpr a => a -> NixExpr
toNixExpr -> NixExpr
rev,
      _fetchSubmodules :: forall (k :: FetchStatus). NixFetcher k -> Bool
_fetchSubmodules = Bool -> NixExpr
forall a. ToNixExpr a => a -> NixExpr
toNixExpr -> NixExpr
fetchSubmodules,
      _deepClone :: forall (k :: FetchStatus). NixFetcher k -> Bool
_deepClone = Bool -> NixExpr
forall a. ToNixExpr a => a -> NixExpr
toNixExpr -> NixExpr
deepClone,
      _leaveDotGit :: forall (k :: FetchStatus). NixFetcher k -> Bool
_leaveDotGit = Bool -> NixExpr
forall a. ToNixExpr a => a -> NixExpr
toNixExpr -> NixExpr
leaveDotGit,
      _furl :: forall (k :: FetchStatus). NixFetcher k -> NixExpr
_furl = NixExpr -> NixExpr
quote -> NixExpr
url,
      _name :: forall (k :: FetchStatus). NixFetcher k -> Maybe NixExpr
_name = Maybe NixExpr -> NixExpr
nameField -> NixExpr
n
    } ->
      [trimming|
          fetchgit {
            url = $url;
            rev = $rev;
            fetchSubmodules = $fetchSubmodules;
            deepClone = $deepClone;
            leaveDotGit = $leaveDotGit;$n
            sha256 = $sha256;
          }
    |]
  FetchGitHub
    { _sha256 :: forall (k :: FetchStatus). NixFetcher k -> FetchResult k
_sha256 = FetchResult k
_,
      _rev :: forall (k :: FetchStatus). NixFetcher k -> Version
_rev = NixExpr -> NixExpr
quote (NixExpr -> NixExpr) -> (Version -> NixExpr) -> Version -> NixExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Version -> NixExpr
forall a. ToNixExpr a => a -> NixExpr
toNixExpr -> NixExpr
rev,
      _fetchSubmodules :: forall (k :: FetchStatus). NixFetcher k -> Bool
_fetchSubmodules = Bool -> NixExpr
forall a. ToNixExpr a => a -> NixExpr
toNixExpr -> NixExpr
fetchSubmodules,
      _deepClone :: forall (k :: FetchStatus). NixFetcher k -> Bool
_deepClone = Bool -> NixExpr
forall a. ToNixExpr a => a -> NixExpr
toNixExpr -> NixExpr
deepClone,
      _leaveDotGit :: forall (k :: FetchStatus). NixFetcher k -> Bool
_leaveDotGit = Bool -> NixExpr
forall a. ToNixExpr a => a -> NixExpr
toNixExpr -> NixExpr
leaveDotGit,
      _fowner :: forall (k :: FetchStatus). NixFetcher k -> NixExpr
_fowner = NixExpr -> NixExpr
quote -> NixExpr
owner,
      _frepo :: forall (k :: FetchStatus). NixFetcher k -> NixExpr
_frepo = NixExpr -> NixExpr
quote -> NixExpr
repo,
      _name :: forall (k :: FetchStatus). NixFetcher k -> Maybe NixExpr
_name = Maybe NixExpr -> NixExpr
nameField -> NixExpr
n
    } ->
      -- TODO: fix fetchFromGitHub in Nixpkgs so that deepClone and
      -- leaveDotGit won't get passed to fetchzip
      if (NixExpr
deepClone NixExpr -> NixExpr -> Bool
forall a. Eq a => a -> a -> Bool
== NixExpr
"true") Bool -> Bool -> Bool
|| (NixExpr
leaveDotGit NixExpr -> NixExpr -> Bool
forall a. Eq a => a -> a -> Bool
== NixExpr
"true")
        then
          [trimming|
               fetchFromGitHub ({
                 owner = $owner;
                 repo = $repo;
                 rev = $rev;
                 fetchSubmodules = $fetchSubmodules;
                 deepClone = $deepClone;
                 leaveDotGit = $leaveDotGit;$n
                 sha256 = $sha256;
               })
         |]
        else
          [trimming|
               fetchFromGitHub ({
                 owner = $owner;
                 repo = $repo;
                 rev = $rev;
                 fetchSubmodules = $fetchSubmodules;$n
                 sha256 = $sha256;
               })
         |]
  (FetchUrl (NixExpr -> NixExpr
quote -> NixExpr
url) (Maybe NixExpr -> NixExpr
nameField -> NixExpr
n) FetchResult k
_) ->
    [trimming|
          fetchurl {
            url = $url;$n
            sha256 = $sha256;
          }
    |]
  (FetchTarball (NixExpr -> NixExpr
quote -> NixExpr
url) FetchResult k
_) ->
    [trimming|
          fetchTarball {
            url = $url;
            sha256 = $sha256;
          }
    |]
  where
    nameField :: Maybe NixExpr -> NixExpr
nameField = NixExpr -> (NixExpr -> NixExpr) -> Maybe NixExpr -> NixExpr
forall b a. b -> (a -> b) -> Maybe a -> b
maybe NixExpr
"" (\NixExpr
x -> NixExpr
"\nname = " NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr -> NixExpr
quote NixExpr
x NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
";")

-- | Create a trivial drv that extracts the source from a fetcher
-- TODO: Avoid using @[email protected]
fetcherToDrv :: NixFetcher Fetched -> Text -> NixExpr
fetcherToDrv :: NixFetcher 'Fetched -> NixExpr -> NixExpr
fetcherToDrv (NixFetcher 'Fetched -> NixExpr
forall a. ToNixExpr a => a -> NixExpr
toNixExpr -> NixExpr
fetcherExpr) (NixExpr -> NixExpr
quote -> NixExpr
drvName) =
  [trimming|
    with import <nixpkgs> { };
    stdenv.mkDerivation {
      name = $drvName;
      src = $fetcherExpr;
      nativeBuildInputs = [ unzip ];
      doBuild = false;
      installPhase = ''
        mkdir $$out
        cp -r * $$out
      '';
    }
  |]

-- | nix expr 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";
--     };
--     a = "B";
--   };
-- @
instance ToNixExpr PackageResult where
  toNixExpr :: PackageResult -> NixExpr
toNixExpr PackageResult {Maybe NixExpr
Maybe (HashMap String (NixExpr, HashMap NixExpr Checksum))
Maybe (HashMap String NixExpr)
Maybe (HashMap NixExpr NixExpr)
NixExpr
UseStaleVersion
NixFetcher 'Fetched
NvcheckerResult
_prgitdate :: PackageResult -> Maybe NixExpr
_prpinned :: PackageResult -> UseStaleVersion
_prcargolock :: PackageResult
-> Maybe (HashMap String (NixExpr, HashMap NixExpr Checksum))
_prextract :: PackageResult -> Maybe (HashMap String NixExpr)
_prpassthru :: PackageResult -> Maybe (HashMap NixExpr NixExpr)
_prfetched :: PackageResult -> NixFetcher 'Fetched
_prversion :: PackageResult -> NvcheckerResult
_prname :: PackageResult -> NixExpr
_prgitdate :: Maybe NixExpr
_prpinned :: UseStaleVersion
_prcargolock :: Maybe (HashMap String (NixExpr, HashMap NixExpr Checksum))
_prextract :: Maybe (HashMap String NixExpr)
_prpassthru :: Maybe (HashMap NixExpr NixExpr)
_prfetched :: NixFetcher 'Fetched
_prversion :: NvcheckerResult
_prname :: NixExpr
..} =
    [trimming|
        $name = {
          pname = $nameString;
          version = $version;
          src = $src;$appending
        };
    |]
    where
      name :: NixExpr
name = NixExpr -> NixExpr
quoteIfNeeds NixExpr
_prname
      nameString :: NixExpr
nameString = NixExpr -> NixExpr
quote NixExpr
_prname
      version :: NixExpr
version = NixExpr -> NixExpr
quote (NixExpr -> NixExpr)
-> (NvcheckerResult -> NixExpr) -> NvcheckerResult -> NixExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Version -> NixExpr
coerce (Version -> NixExpr)
-> (NvcheckerResult -> Version) -> NvcheckerResult -> NixExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NvcheckerResult -> Version
nvNow (NvcheckerResult -> NixExpr) -> NvcheckerResult -> NixExpr
forall a b. (a -> b) -> a -> b
$ NvcheckerResult
_prversion
      src :: NixExpr
src = NixFetcher 'Fetched -> NixExpr
forall a. ToNixExpr a => a -> NixExpr
toNixExpr NixFetcher 'Fetched
_prfetched
      extract :: NixExpr
extract =
        NixExpr
-> (HashMap String NixExpr -> NixExpr)
-> Maybe (HashMap String NixExpr)
-> NixExpr
forall b a. b -> (a -> b) -> Maybe a -> b
maybe
          NixExpr
""
          ( \HashMap String NixExpr
ex ->
              [NixExpr] -> NixExpr
T.unlines
                [ NixExpr -> NixExpr
quoteIfNeeds (String -> NixExpr
T.pack String
name)
                    NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
" = builtins.readFile "
                    NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
fp
                    NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
";"
                  | (String
name, NixExpr
fp) <- HashMap String NixExpr -> [(String, NixExpr)]
forall k v. HashMap k v -> [(k, v)]
HMap.toList HashMap String NixExpr
ex
                ]
          )
          Maybe (HashMap String NixExpr)
_prextract
      cargo :: NixExpr
cargo = NixExpr -> Maybe NixExpr -> NixExpr
forall a. a -> Maybe a -> a
fromMaybe NixExpr
"" (Maybe NixExpr -> NixExpr) -> Maybe NixExpr -> NixExpr
forall a b. (a -> b) -> a -> b
$ do
        HashMap String (NixExpr, HashMap NixExpr Checksum)
cargoLocks <- Maybe (HashMap String (NixExpr, HashMap NixExpr Checksum))
_prcargolock
        let depsSnippet :: HashMap NixExpr Checksum -> NixExpr
depsSnippet (HashMap NixExpr Checksum
deps :: HashMap Text Checksum) =
              [NixExpr] -> NixExpr
T.unlines
                [ NixExpr -> NixExpr
quoteIfNeeds NixExpr
name
                    NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
" = "
                    NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr -> NixExpr
quote (Checksum -> NixExpr
coerce Checksum
sum)
                    NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
";"
                  | (NixExpr
name, Checksum
sum) <- HashMap NixExpr Checksum -> [(NixExpr, Checksum)]
forall k v. HashMap k v -> [(k, v)]
HMap.toList HashMap NixExpr Checksum
deps
                ]
            lockSnippet :: (String, (NixExpr, HashMap NixExpr Checksum)) -> NixExpr
lockSnippet ((String -> NixExpr
T.pack -> NixExpr
fp) :: FilePath, (NixExpr
nixFP :: NixExpr, HashMap NixExpr Checksum
deps :: HashMap Text Checksum)) =
              let hashes :: NixExpr
hashes = HashMap NixExpr Checksum -> NixExpr
depsSnippet HashMap NixExpr Checksum
deps
               in [trimming|
                    cargoLock."$fp" = {
                      lockFile = $nixFP;
                      outputHashes = {
                        $hashes
                      };
                    };
                |]
        NixExpr -> Maybe NixExpr
forall (f :: * -> *) a. Applicative f => a -> f a
pure (NixExpr -> Maybe NixExpr)
-> ([NixExpr] -> NixExpr) -> [NixExpr] -> Maybe NixExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [NixExpr] -> NixExpr
T.unlines ([NixExpr] -> Maybe NixExpr) -> [NixExpr] -> Maybe NixExpr
forall a b. (a -> b) -> a -> b
$ (String, (NixExpr, HashMap NixExpr Checksum)) -> NixExpr
lockSnippet ((String, (NixExpr, HashMap NixExpr Checksum)) -> NixExpr)
-> [(String, (NixExpr, HashMap NixExpr Checksum))] -> [NixExpr]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HashMap String (NixExpr, HashMap NixExpr Checksum)
-> [(String, (NixExpr, HashMap NixExpr Checksum))]
forall k v. HashMap k v -> [(k, v)]
HMap.toList HashMap String (NixExpr, HashMap NixExpr Checksum)
cargoLocks
      passthru :: NixExpr
passthru =
        NixExpr
-> (HashMap NixExpr NixExpr -> NixExpr)
-> Maybe (HashMap NixExpr NixExpr)
-> NixExpr
forall b a. b -> (a -> b) -> Maybe a -> b
maybe
          NixExpr
""
          ( \HashMap NixExpr NixExpr
pt ->
              [NixExpr] -> NixExpr
T.unlines
                [ NixExpr -> NixExpr
quoteIfNeeds NixExpr
k
                    NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
" = "
                    NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
v
                    NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
";"
                  | (NixExpr
k, NixExpr -> NixExpr
quote -> NixExpr
v) <- HashMap NixExpr NixExpr -> [(NixExpr, NixExpr)]
forall k v. HashMap k v -> [(k, v)]
HMap.toList HashMap NixExpr NixExpr
pt
                ]
          )
          Maybe (HashMap NixExpr NixExpr)
_prpassthru
      date :: NixExpr
date = NixExpr -> (NixExpr -> NixExpr) -> Maybe NixExpr -> NixExpr
forall b a. b -> (a -> b) -> Maybe a -> b
maybe NixExpr
"" (\NixExpr
d -> NixExpr
"date = " NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr -> NixExpr
quote NixExpr
d NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
";") Maybe NixExpr
_prgitdate
      joined :: NixExpr
joined = NixExpr
extract NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
cargo NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
passthru NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
date
      appending :: NixExpr
appending = if NixExpr -> Bool
T.null NixExpr
joined then NixExpr
"" else NixExpr
"\n" NixExpr -> NixExpr -> NixExpr
forall a. Semigroup a => a -> a -> a
<> NixExpr
joined