@@ -12,7 +12,7 @@ module System.Nix.Nar.Streamer
1212
1313import Data.ByteString (ByteString )
1414import Data.Int (Int64 )
15- import Data.Text ( Text )
15+ import qualified Data.Map.Strict as Map
1616
1717import Control.Monad ( forM_
1818 , when
@@ -23,7 +23,7 @@ import qualified Data.ByteString.Lazy as Bytes.Lazy
2323import qualified Data.Foldable
2424import qualified Data.List
2525import qualified Data.Serialize as Serial
26- import qualified Data.Text as T (pack , breakOn )
26+ import qualified Data.Text as T (pack , unpack )
2727import qualified Data.Text.Encoding as TE (encodeUtf8 )
2828import System.FilePath ((</>) )
2929
@@ -92,18 +92,24 @@ streamNarIOWithOptions opts effs basePath yield = do
9292 isDir <- IO. liftIO $ Nar. narIsDir effs path
9393 if isDir then do
9494 fs <- IO. liftIO (Nar. narListDir effs path)
95+ let entries =
96+ foldr (\ f acc ->
97+ let
98+ name =
99+ if Nar. optUseCaseHack opts
100+ then undoCaseHack f
101+ else f
102+ in
103+ case Map. insertLookupWithKey (\ _ n _ -> n) name f acc of
104+ (Nothing , newMap) -> newMap
105+ (Just conflict, _) -> error $ " File name collision between " ++ (path </> name) ++ " and " ++ (path </> conflict)
106+ ) Map. empty fs
95107 yield $ strs [" type" , " directory" ]
96- forM_ (Data.List. sort fs ) $ \ f -> do
108+ forM_ (Map. toAscList entries ) $ \ (unhacked, original) -> do
97109 yield $ str " entry"
98110 parens $ do
99- let fullName = path </> f
100- let serializedPath =
101- if Nar. optUseCaseHack opts then
102- filePathToBSWithCaseHack f
103- else
104- filePathToBS f
105- yield $ strs [" name" , serializedPath, " node" ]
106- parens $ go fullName
111+ yield $ strs [" name" , filePathToBS unhacked, " node" ]
112+ parens $ go (path </> original)
107113 else do
108114 isExec <- IO. liftIO $ Nar. narIsExec effs path
109115 yield $ strs [" type" , " regular" ]
@@ -148,8 +154,10 @@ strs xs = Bytes.concat $ str <$> xs
148154filePathToBS :: FilePath -> ByteString
149155filePathToBS = TE. encodeUtf8 . T. pack
150156
151- filePathToBSWithCaseHack :: FilePath -> ByteString
152- filePathToBSWithCaseHack = TE. encodeUtf8 . undoCaseHack . T. pack
153-
154- undoCaseHack :: Text -> Text
155- undoCaseHack = fst . T. breakOn Nar. caseHackSuffix
157+ undoCaseHack :: FilePath -> FilePath
158+ undoCaseHack f =
159+ case Data.List. findIndex (caseHackSuffix `Data.List.isPrefixOf` ) (Data.List. tails f) of
160+ Just index -> take index f
161+ Nothing -> f
162+ where
163+ caseHackSuffix = T. unpack Nar. caseHackSuffix
0 commit comments