{-# language DataKinds #-}
{-# language ScopedTypeVariables #-}

module System.Nix.ReadonlyStore where


import qualified Data.ByteString               as BS
import qualified Data.HashSet                  as HS
import           System.Nix.Hash
import           System.Nix.Nar
import           System.Nix.StorePath
import           Crypto.Hash                    ( Context
                                                , Digest
                                                , hash
                                                , hashlazy
                                                , hashInit
                                                , hashUpdate
                                                , hashFinalize
                                                , SHA256
                                                )


makeStorePath
  :: forall h
   . (NamedAlgo h)
  => FilePath
  -> ByteString
  -> Digest h
  -> StorePathName
  -> StorePath
makeStorePath :: forall h.
NamedAlgo h =>
FilePath -> ByteString -> Digest h -> StorePathName -> StorePath
makeStorePath FilePath
fp ByteString
ty Digest h
h StorePathName
nm = StorePathHashPart -> StorePathName -> FilePath -> StorePath
StorePath (coerce :: forall a b. Coercible a b => a -> b
coerce ByteString
storeHash) StorePathName
nm FilePath
fp
 where
  storeHash :: ByteString
storeHash = forall a. HashAlgorithm a => ByteString -> ByteString
mkStorePathHash @h ByteString
s

  s :: ByteString
s =
    ByteString -> [ByteString] -> ByteString
BS.intercalate ByteString
":" forall a b. (a -> b) -> a -> b
$
      ByteString
tyforall a. a -> [a] -> [a]
:forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a b. ConvertUtf8 a b => a -> b
encodeUtf8
        [ forall a. NamedAlgo a => Text
algoName @h
        , forall a. BaseEncoding -> Digest a -> Text
encodeDigestWith BaseEncoding
Base16 Digest h
h
        , forall a. ToText a => a -> Text
toText FilePath
fp
        , coerce :: forall a b. Coercible a b => a -> b
coerce StorePathName
nm
        ]

makeTextPath
  :: FilePath -> StorePathName -> Digest SHA256 -> StorePathSet -> StorePath
makeTextPath :: FilePath
-> StorePathName -> Digest SHA256 -> StorePathSet -> StorePath
makeTextPath FilePath
fp StorePathName
nm Digest SHA256
h StorePathSet
refs = forall h.
NamedAlgo h =>
FilePath -> ByteString -> Digest h -> StorePathName -> StorePath
makeStorePath FilePath
fp ByteString
ty Digest SHA256
h StorePathName
nm
 where
  ty :: ByteString
ty =
    ByteString -> [ByteString] -> ByteString
BS.intercalate ByteString
":" forall a b. (a -> b) -> a -> b
$ ByteString
"text" forall a. a -> [a] -> [a]
: forall a. Ord a => [a] -> [a]
sort (StorePath -> ByteString
storePathToRawFilePath forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. HashSet a -> [a]
HS.toList StorePathSet
refs)

makeFixedOutputPath
  :: forall hashAlgo
  .  NamedAlgo hashAlgo
  => FilePath
  -> Bool
  -> Digest hashAlgo
  -> StorePathName
  -> StorePath
makeFixedOutputPath :: forall hashAlgo.
NamedAlgo hashAlgo =>
FilePath -> Bool -> Digest hashAlgo -> StorePathName -> StorePath
makeFixedOutputPath FilePath
fp Bool
recursive Digest hashAlgo
h =
  if Bool
recursive Bool -> Bool -> Bool
&& (forall a. NamedAlgo a => Text
algoName @hashAlgo) forall a. Eq a => a -> a -> Bool
== Text
"sha256"
    then forall h.
NamedAlgo h =>
FilePath -> ByteString -> Digest h -> StorePathName -> StorePath
makeStorePath FilePath
fp ByteString
"source" Digest hashAlgo
h
    else forall h.
NamedAlgo h =>
FilePath -> ByteString -> Digest h -> StorePathName -> StorePath
makeStorePath FilePath
fp ByteString
"output:out" Digest SHA256
h'
 where
  h' :: Digest SHA256
h' =
    forall ba a.
(ByteArrayAccess ba, HashAlgorithm a) =>
ba -> Digest a
hash @ByteString @SHA256
      forall a b. (a -> b) -> a -> b
$  ByteString
"fixed:out:"
      forall a. Semigroup a => a -> a -> a
<> forall a b. ConvertUtf8 a b => a -> b
encodeUtf8 (forall a. NamedAlgo a => Text
algoName @hashAlgo)
      forall a. Semigroup a => a -> a -> a
<> (if Bool
recursive then ByteString
":r:" else ByteString
":")
      forall a. Semigroup a => a -> a -> a
<> forall a b. ConvertUtf8 a b => a -> b
encodeUtf8 (forall a. BaseEncoding -> Digest a -> Text
encodeDigestWith BaseEncoding
Base16 Digest hashAlgo
h)
      forall a. Semigroup a => a -> a -> a
<> ByteString
":"

computeStorePathForText
  :: FilePath -> StorePathName -> ByteString -> (StorePathSet -> StorePath)
computeStorePathForText :: FilePath
-> StorePathName -> ByteString -> StorePathSet -> StorePath
computeStorePathForText FilePath
fp StorePathName
nm = FilePath
-> StorePathName -> Digest SHA256 -> StorePathSet -> StorePath
makeTextPath FilePath
fp StorePathName
nm forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall ba a.
(ByteArrayAccess ba, HashAlgorithm a) =>
ba -> Digest a
hash

computeStorePathForPath
  :: StorePathName        -- ^ Name part of the newly created `StorePath`
  -> FilePath             -- ^ Local `FilePath` to add
  -> Bool                 -- ^ Add target directory recursively
  -> (FilePath -> Bool)   -- ^ Path filter function
  -> Bool                 -- ^ Only used by local store backend
  -> IO StorePath
computeStorePathForPath :: StorePathName
-> FilePath -> Bool -> (FilePath -> Bool) -> Bool -> IO StorePath
computeStorePathForPath StorePathName
name FilePath
pth Bool
recursive FilePath -> Bool
_pathFilter Bool
_repair = do
  Digest SHA256
selectedHash <- if Bool
recursive then IO (Digest SHA256)
recursiveContentHash else IO (Digest SHA256)
flatContentHash
  forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall hashAlgo.
NamedAlgo hashAlgo =>
FilePath -> Bool -> Digest hashAlgo -> StorePathName -> StorePath
makeFixedOutputPath FilePath
"/nix/store" Bool
recursive Digest SHA256
selectedHash StorePathName
name
 where
  recursiveContentHash :: IO (Digest SHA256)
  recursiveContentHash :: IO (Digest SHA256)
recursiveContentHash = forall a. HashAlgorithm a => Context a -> Digest a
hashFinalize forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m s
execStateT StateT (Context SHA256) IO ()
streamNarUpdate (forall a. HashAlgorithm a => Context a
hashInit @SHA256)
  streamNarUpdate :: StateT (Context SHA256) IO ()
  streamNarUpdate :: StateT (Context SHA256) IO ()
streamNarUpdate = forall (m :: * -> *).
MonadIO m =>
NarEffects IO -> FilePath -> NarSource m
streamNarIO forall (m :: * -> *).
(MonadIO m, MonadFail m, MonadBaseControl IO m) =>
NarEffects m
narEffectsIO FilePath
pth (forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b c. (a -> b -> c) -> b -> a -> c
flip (forall ba a.
(ByteArrayAccess ba, HashAlgorithm a) =>
Context a -> ba -> Context a
hashUpdate @ByteString @SHA256))

  flatContentHash :: IO (Digest SHA256)
  flatContentHash :: IO (Digest SHA256)
flatContentHash = forall a. HashAlgorithm a => ByteString -> Digest a
hashlazy forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *). NarEffects m -> FilePath -> m ByteString
narReadFile forall (m :: * -> *).
(MonadIO m, MonadFail m, MonadBaseControl IO m) =>
NarEffects m
narEffectsIO FilePath
pth