{-# 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 :: FilePath -> ByteString -> Digest h -> StorePathName -> StorePath
makeStorePath FilePath
fp ByteString
ty Digest h
h StorePathName
nm = StorePathHashPart -> StorePathName -> FilePath -> StorePath
StorePath (ByteString -> StorePathHashPart
coerce ByteString
storeHash) StorePathName
nm FilePath
fp
where
storeHash :: ByteString
storeHash = ByteString -> ByteString
forall a. HashAlgorithm a => ByteString -> ByteString
mkStorePathHash @h ByteString
s
s :: ByteString
s =
ByteString -> [ByteString] -> ByteString
BS.intercalate ByteString
":" ([ByteString] -> ByteString) -> [ByteString] -> ByteString
forall a b. (a -> b) -> a -> b
$
ByteString
tyByteString -> [ByteString] -> [ByteString]
forall a. a -> [a] -> [a]
:(Text -> ByteString) -> [Text] -> [ByteString]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Text -> ByteString
forall a b. ConvertUtf8 a b => a -> b
encodeUtf8
[ NamedAlgo h => Text
forall a. NamedAlgo a => Text
algoName @h
, BaseEncoding -> Digest h -> Text
forall a. BaseEncoding -> Digest a -> Text
encodeDigestWith BaseEncoding
Base16 Digest h
h
, FilePath -> Text
forall a. ToText a => a -> Text
toText FilePath
fp
, StorePathName -> Text
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 = FilePath
-> ByteString -> Digest SHA256 -> StorePathName -> StorePath
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
":" ([ByteString] -> ByteString) -> [ByteString] -> ByteString
forall a b. (a -> b) -> a -> b
$ ByteString
"text" ByteString -> [ByteString] -> [ByteString]
forall a. a -> [a] -> [a]
: [ByteString] -> [ByteString]
forall a. Ord a => [a] -> [a]
sort (StorePath -> ByteString
storePathToRawFilePath (StorePath -> ByteString) -> [StorePath] -> [ByteString]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StorePathSet -> [StorePath]
forall a. HashSet a -> [a]
HS.toList StorePathSet
refs)
makeFixedOutputPath
:: forall hashAlgo
. NamedAlgo hashAlgo
=> FilePath
-> Bool
-> Digest hashAlgo
-> StorePathName
-> StorePath
makeFixedOutputPath :: FilePath -> Bool -> Digest hashAlgo -> StorePathName -> StorePath
makeFixedOutputPath FilePath
fp Bool
recursive Digest hashAlgo
h =
if Bool
recursive Bool -> Bool -> Bool
&& (NamedAlgo hashAlgo => Text
forall a. NamedAlgo a => Text
algoName @hashAlgo) Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"sha256"
then FilePath
-> ByteString -> Digest hashAlgo -> StorePathName -> StorePath
forall h.
NamedAlgo h =>
FilePath -> ByteString -> Digest h -> StorePathName -> StorePath
makeStorePath FilePath
fp ByteString
"source" Digest hashAlgo
h
else FilePath
-> ByteString -> Digest SHA256 -> StorePathName -> StorePath
forall h.
NamedAlgo h =>
FilePath -> ByteString -> Digest h -> StorePathName -> StorePath
makeStorePath FilePath
fp ByteString
"output:out" Digest SHA256
h'
where
h' :: Digest SHA256
h' =
(ByteArrayAccess ByteString, HashAlgorithm SHA256) =>
ByteString -> Digest SHA256
forall ba a.
(ByteArrayAccess ba, HashAlgorithm a) =>
ba -> Digest a
hash @ByteString @SHA256
(ByteString -> Digest SHA256) -> ByteString -> Digest SHA256
forall a b. (a -> b) -> a -> b
$ ByteString
"fixed:out:"
ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> Text -> ByteString
forall a b. ConvertUtf8 a b => a -> b
encodeUtf8 (NamedAlgo hashAlgo => Text
forall a. NamedAlgo a => Text
algoName @hashAlgo)
ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> (if Bool
recursive then ByteString
":r:" else ByteString
":")
ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> Text -> ByteString
forall a b. ConvertUtf8 a b => a -> b
encodeUtf8 (BaseEncoding -> Digest hashAlgo -> Text
forall a. BaseEncoding -> Digest a -> Text
encodeDigestWith BaseEncoding
Base16 Digest hashAlgo
h)
ByteString -> ByteString -> ByteString
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 (Digest SHA256 -> StorePathSet -> StorePath)
-> (ByteString -> Digest SHA256)
-> ByteString
-> StorePathSet
-> StorePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Digest SHA256
forall ba a.
(ByteArrayAccess ba, HashAlgorithm a) =>
ba -> Digest a
hash
computeStorePathForPath
:: StorePathName
-> FilePath
-> Bool
-> (FilePath -> Bool)
-> Bool
-> 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
StorePath -> IO StorePath
forall (f :: * -> *) a. Applicative f => a -> f a
pure (StorePath -> IO StorePath) -> StorePath -> IO StorePath
forall a b. (a -> b) -> a -> b
$ FilePath -> Bool -> Digest SHA256 -> StorePathName -> StorePath
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 = Context SHA256 -> Digest SHA256
forall a. HashAlgorithm a => Context a -> Digest a
hashFinalize (Context SHA256 -> Digest SHA256)
-> IO (Context SHA256) -> IO (Digest SHA256)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StateT (Context SHA256) IO ()
-> Context SHA256 -> IO (Context SHA256)
forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m s
execStateT StateT (Context SHA256) IO ()
streamNarUpdate (HashAlgorithm SHA256 => Context SHA256
forall a. HashAlgorithm a => Context a
hashInit @SHA256)
streamNarUpdate :: StateT (Context SHA256) IO ()
streamNarUpdate :: StateT (Context SHA256) IO ()
streamNarUpdate = NarEffects IO -> FilePath -> NarSource (StateT (Context SHA256) IO)
forall (m :: * -> *).
MonadIO m =>
NarEffects IO -> FilePath -> NarSource m
streamNarIO NarEffects IO
forall (m :: * -> *).
(MonadIO m, MonadFail m, MonadBaseControl IO m) =>
NarEffects m
narEffectsIO FilePath
pth ((Context SHA256 -> Context SHA256) -> StateT (Context SHA256) IO ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((Context SHA256 -> Context SHA256)
-> StateT (Context SHA256) IO ())
-> (ByteString -> Context SHA256 -> Context SHA256)
-> ByteString
-> StateT (Context SHA256) IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Context SHA256 -> ByteString -> Context SHA256)
-> ByteString -> Context SHA256 -> Context SHA256
forall a b c. (a -> b -> c) -> b -> a -> c
flip ((ByteArrayAccess ByteString, HashAlgorithm SHA256) =>
Context SHA256 -> ByteString -> Context SHA256
forall ba a.
(ByteArrayAccess ba, HashAlgorithm a) =>
Context a -> ba -> Context a
hashUpdate @ByteString @SHA256))
flatContentHash :: IO (Digest SHA256)
flatContentHash :: IO (Digest SHA256)
flatContentHash = ByteString -> Digest SHA256
forall a. HashAlgorithm a => ByteString -> Digest a
hashlazy (ByteString -> Digest SHA256)
-> IO ByteString -> IO (Digest SHA256)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NarEffects IO -> FilePath -> IO ByteString
forall (m :: * -> *). NarEffects m -> FilePath -> m ByteString
narReadFile NarEffects IO
forall (m :: * -> *).
(MonadIO m, MonadFail m, MonadBaseControl IO m) =>
NarEffects m
narEffectsIO FilePath
pth