@@ -10,7 +10,8 @@ Description : Representation of Nix store paths.
1010
1111module System.Nix.Internal.StorePath
1212 ( -- * Basic store path types
13- StorePath (.. )
13+ StoreDir (.. )
14+ , StorePath (.. )
1415 , StorePathName (.. )
1516 , StorePathSet
1617 , mkStorePathHashPart
@@ -54,10 +55,9 @@ import Crypto.Hash ( SHA256
5455-- From the Nix thesis: A store path is the full path of a store
5556-- object. It has the following anatomy: storeDir/hashPart-name.
5657--
57- -- @storeDir@: The root of the Nix store (e.g. \/nix\/store).
58- --
59- -- See the 'StoreDir' haddocks for details on why we represent this at
60- -- the type level.
58+ -- The store directory is *not* included, and must be known from the
59+ -- context. This matches modern C++ Nix, and also represents the fact
60+ -- that store paths for different store directories cannot be mixed.
6161data StorePath = StorePath
6262 { -- | The 160-bit hash digest reflecting the "address" of the name.
6363 -- Currently, this is a truncated SHA256 hash.
@@ -66,18 +66,13 @@ data StorePath = StorePath
6666 -- this is typically the package name and version (e.g.
6767 -- hello-1.2.3).
6868 storePathName :: ! StorePathName
69- , -- | Root of the store
70- storePathRoot :: ! FilePath
7169 }
72- deriving (Eq , Ord )
70+ deriving (Eq , Ord , Show )
7371
7472instance Hashable StorePath where
7573 hashWithSalt s StorePath {.. } =
7674 s `hashWithSalt` storePathHash `hashWithSalt` storePathName
7775
78- instance Show StorePath where
79- show p = Bytes.Char8. unpack $ storePathToRawFilePath p
80-
8176-- | The name portion of a Nix path.
8277--
8378-- 'unStorePathName' must only contain a-zA-Z0-9+._?=-, can't start
@@ -86,7 +81,7 @@ instance Show StorePath where
8681newtype StorePathName = StorePathName
8782 { -- | Extract the contents of the name.
8883 unStorePathName :: Text
89- } deriving (Eq , Hashable , Ord )
84+ } deriving (Eq , Hashable , Ord , Show )
9085
9186-- | The hash algorithm used for store path hashes.
9287newtype StorePathHashPart = StorePathHashPart ByteString
@@ -161,22 +156,29 @@ validStorePathNameChar c =
161156-- to avoid the dependency.
162157type RawFilePath = ByteString
163158
159+ -- | The path to the store dir
160+ --
161+ -- Many operations need to be parameterized with this, since store paths
162+ -- do not know their own store dir by design.
163+ newtype StoreDir = StoreDir {
164+ unStoreDir :: RawFilePath
165+ } deriving (Eq , Hashable , Ord , Show )
166+
164167-- | Render a 'StorePath' as a 'RawFilePath'.
165- storePathToRawFilePath :: StorePath -> RawFilePath
166- storePathToRawFilePath StorePath {.. } =
167- root <> " /" <> hashPart <> " -" <> name
168+ storePathToRawFilePath :: StoreDir -> StorePath -> RawFilePath
169+ storePathToRawFilePath storeDir StorePath {.. } =
170+ unStoreDir storeDir <> " /" <> hashPart <> " -" <> name
168171 where
169- root = Bytes.Char8. pack storePathRoot
170172 hashPart = encodeUtf8 $ encodeWith NixBase32 $ coerce storePathHash
171173 name = encodeUtf8 $ unStorePathName storePathName
172174
173175-- | Render a 'StorePath' as a 'FilePath'.
174- storePathToFilePath :: StorePath -> FilePath
175- storePathToFilePath = Bytes.Char8. unpack . storePathToRawFilePath
176+ storePathToFilePath :: StoreDir -> StorePath -> FilePath
177+ storePathToFilePath storeDir = Bytes.Char8. unpack . storePathToRawFilePath storeDir
176178
177179-- | Render a 'StorePath' as a 'Text'.
178- storePathToText :: StorePath -> Text
179- storePathToText = toText . Bytes.Char8. unpack . storePathToRawFilePath
180+ storePathToText :: StoreDir -> StorePath -> Text
181+ storePathToText storeDir = toText . Bytes.Char8. unpack . storePathToRawFilePath storeDir
180182
181183-- | Build `narinfo` suffix from `StorePath` which
182184-- can be used to query binary caches.
@@ -186,7 +188,7 @@ storePathToNarInfo StorePath{..} =
186188
187189-- | Parse `StorePath` from `Bytes.Char8.ByteString`, checking
188190-- that store directory matches `expectedRoot`.
189- parsePath :: FilePath -> Bytes.Char8. ByteString -> Either String StorePath
191+ parsePath :: StoreDir -> Bytes.Char8. ByteString -> Either String StorePath
190192parsePath expectedRoot x =
191193 let
192194 (rootDir, fname) = FilePath. splitFileName . Bytes.Char8. unpack $ x
@@ -196,17 +198,20 @@ parsePath expectedRoot x =
196198 -- rootDir' = dropTrailingPathSeparator rootDir
197199 -- cannot use ^^ as it drops multiple slashes /a/b/// -> /a/b
198200 rootDir' = Unsafe. init rootDir
201+ expectedRootS = Bytes.Char8. unpack (unStoreDir expectedRoot)
199202 storeDir =
200- if expectedRoot == rootDir'
203+ if expectedRootS == rootDir'
201204 then pure rootDir'
202- else Left $ " Root store dir mismatch, expected" <> expectedRoot <> " got" <> rootDir'
205+ else Left $ " Root store dir mismatch, expected" <> expectedRootS <> " got" <> rootDir'
203206 in
204- StorePath <$> coerce storeHash <*> name <*> storeDir
207+ StorePath <$> coerce storeHash <*> name
205208
206- pathParser :: FilePath -> Parser StorePath
209+ pathParser :: StoreDir -> Parser StorePath
207210pathParser expectedRoot = do
211+ let expectedRootS = Bytes.Char8. unpack (unStoreDir expectedRoot)
212+
208213 _ <-
209- Parser.Text.Lazy. string (toText expectedRoot )
214+ Parser.Text.Lazy. string (toText expectedRootS )
210215 <?> " Store root mismatch" -- e.g. /nix/store
211216
212217 _ <- Parser.Text.Lazy. char ' /'
@@ -232,4 +237,4 @@ pathParser expectedRoot = do
232237 either
233238 fail
234239 pure
235- (StorePath <$> coerce digest <*> name <*> pure expectedRoot )
240+ (StorePath <$> coerce digest <*> name)
0 commit comments