1- {-# LANGUAGE DeriveAnyClass #-}
21{-# LANGUAGE OverloadedStrings #-}
32
43module System.Nix.ContentAddress (
54 ContentAddress
65 , contentAddressBuilder
76 , contentAddressParser
7+ , buildContentAddress
8+ , parseContentAddress
89 ) where
910
1011import Control.Applicative
11- import Crypto.Hash (Digest , SHA256 )
12- import Data.Hashable (Hashable )
12+ import Crypto.Hash (Digest )
13+ import Data.Attoparsec.Text (Parser )
14+ import Data.Dependent.Sum (DSum )
15+ import Data.Text (Text )
1316import Data.Text.Lazy.Builder (Builder )
1417import GHC.Generics (Generic )
15- import System.Nix.Base (BaseEncoding (NixBase32 ))
16- import System.Nix.Hash (SomeNamedDigest (SomeDigest ))
18+ import System.Nix.Hash (HashAlgo )
1719import Test.QuickCheck (Arbitrary )
1820import Test.QuickCheck.Arbitrary.Generic (GenericArbitrary (.. ))
1921import Test.QuickCheck.Instances ()
2022
21- import qualified Data.Attoparsec.ByteString.Char8
22- import qualified Data.Text.Encoding
23+ import qualified Data.Attoparsec.Text
24+ import qualified Data.Text.Lazy
25+ import qualified Data.Text.Lazy.Builder
2326import qualified System.Nix.Hash
2427
28+ data FileIngestionMethod
29+ = Flat
30+ | FileRecursive
31+ deriving (Eq , Bounded , Generic , Enum , Ord , Show )
32+
33+ deriving via GenericArbitrary FileIngestionMethod
34+ instance Arbitrary FileIngestionMethod
35+
36+ data ContentAddressMethod
37+ = FileIngestionMethod ! FileIngestionMethod
38+ -- ^ The path was added to the store via makeFixedOutputPath or
39+ -- addToStore. It is addressed according to some hash algorithm
40+ -- applied to the nar serialization via some 'NarHashMode'.
41+ | TextIngestionMethod
42+ -- ^ The path is a plain file added via makeTextPath or
43+ -- addTextToStore. It is addressed according to a sha256sum of the
44+ -- file contents.
45+ deriving (Eq , Generic , Ord , Show )
46+
47+ deriving via GenericArbitrary ContentAddressMethod
48+ instance Arbitrary ContentAddressMethod
49+
2550-- | An address for a content-addressable store path, i.e. one whose
2651-- store path hash is purely a function of its contents (as opposed to
2752-- paths that are derivation outputs, whose hashes are a function of
@@ -31,74 +56,63 @@ import qualified System.Nix.Hash
3156-- encodable in multiple ways, depending on the method used to add the
3257-- path to the store. These unfortunately result in separate store
3358-- paths.
34- data ContentAddress
35- = -- | The path is a plain file added via makeTextPath or
36- -- addTextToStore. It is addressed according to a sha256sum of the
37- -- file contents.
38- Text ! (Digest SHA256 )
39- | -- | The path was added to the store via makeFixedOutputPath or
40- -- addToStore. It is addressed according to some hash algorithm
41- -- applied to the nar serialization via some 'NarHashMode'.
42- Fixed ! NarHashMode ! SomeNamedDigest
59+ data ContentAddress = ContentAddress
60+ ContentAddressMethod
61+ (DSum HashAlgo Digest )
4362 deriving (Eq , Generic , Ord , Show )
4463
4564deriving via GenericArbitrary ContentAddress
4665 instance Arbitrary ContentAddress
4766
48- -- | Builder for `ContentAddress`
67+ -- | Marshall `ContentAddressableAddress` to `Text`
68+ -- in form suitable for remote protocol usage.
69+ buildContentAddress :: ContentAddress -> Text
70+ buildContentAddress =
71+ Data.Text.Lazy. toStrict
72+ . Data.Text.Lazy.Builder. toLazyText
73+ . contentAddressBuilder
74+
4975contentAddressBuilder :: ContentAddress -> Builder
50- contentAddressBuilder (Text digest) =
51- " text:"
52- <> System.Nix.Hash. digestBuilder digest
53- contentAddressBuilder (Fixed narHashMode (SomeDigest (digest :: Digest hashAlgo ))) =
54- " fixed:"
55- <> (if narHashMode == Recursive then " r:" else mempty )
56- -- <> Data.Text.Lazy.Builder.fromText (System.Nix.Hash.algoName @hashAlgo)
57- <> System.Nix.Hash. digestBuilder digest
76+ contentAddressBuilder (ContentAddress method digest) = case method of
77+ TextIngestionMethod ->
78+ " text:"
79+ <> System.Nix.Hash. algoDigestBuilder digest
80+ FileIngestionMethod r ->
81+ " fixed:"
82+ <> fileIngestionMethodBuilder r
83+ <> System.Nix.Hash. algoDigestBuilder digest
84+
85+ fileIngestionMethodBuilder :: FileIngestionMethod -> Builder
86+ fileIngestionMethodBuilder = \ case
87+ Flat -> " "
88+ FileRecursive -> " r:"
89+
90+ -- | Parse `ContentAddressableAddress` from `ByteString`
91+ parseContentAddress
92+ :: Text -> Either String ContentAddress
93+ parseContentAddress =
94+ Data.Attoparsec.Text. parseOnly contentAddressParser
5895
5996-- | Parser for content addressable field
60- contentAddressParser :: Data.Attoparsec.ByteString.Char8. Parser ContentAddress
61- contentAddressParser = caText <|> caFixed
97+ contentAddressParser :: Parser ContentAddress
98+ contentAddressParser = do
99+ method <- parseContentAddressMethod
100+ digest <- parseTypedDigest
101+ case digest of
102+ Left e -> fail e
103+ Right x -> return $ ContentAddress method x
104+
105+ parseContentAddressMethod :: Parser ContentAddressMethod
106+ parseContentAddressMethod =
107+ TextIngestionMethod <$ " text:"
108+ <|> FileIngestionMethod <$ " fixed:" <*> (FileRecursive <$ " r:" <|> pure Flat )
109+
110+ parseTypedDigest :: Parser (Either String (DSum HashAlgo Digest ))
111+ parseTypedDigest = System.Nix.Hash. mkNamedDigest <$> parseHashType <*> parseHash
62112 where
63- -- | Parser for @text:sha256:<h>@
64- -- caText :: Parser ContentAddress
65- caText = do
66- _ <- " text:sha256:"
67- digest <- System.Nix.Hash. decodeDigestWith @ SHA256 NixBase32 <$> parseHash
68- either fail pure $ Text <$> digest
69-
70- -- | Parser for @fixed:<r?>:<ht>:<h>@
71- -- caFixed :: Parser ContentAddress
72- caFixed = do
73- _ <- " fixed:"
74- narHashMode <- (Recursive <$ " r:" ) <|> (RegularFile <$ " " )
75- digest <- parseTypedDigest
76- either fail pure $ Fixed narHashMode <$> digest
77-
78- -- parseTypedDigest :: Parser (Either String SomeNamedDigest)
79- parseTypedDigest = System.Nix.Hash. mkNamedDigest <$> parseHashType <*> parseHash
80-
81- -- parseHashType :: Parser Text
82- parseHashType =
83- Data.Text.Encoding. decodeUtf8
84- <$> (" sha256" <|> " sha512" <|> " sha1" <|> " md5" ) <* (" :" <|> " -" )
85-
86- -- parseHash :: Parser Text
87- parseHash =
88- Data.Text.Encoding. decodeUtf8
89- <$> Data.Attoparsec.ByteString.Char8. takeWhile1 (/= ' :' )
90-
91- -- | Schemes for hashing a Nix archive.
92- --
93- -- For backwards-compatibility reasons, there are two different modes
94- -- here, even though 'Recursive' should be able to cover both.
95- data NarHashMode
96- = -- | Require the nar to represent a non-executable regular file.
97- RegularFile
98- | -- | Hash an arbitrary nar, including a non-executable regular
99- -- file if so desired.
100- Recursive
101- deriving (Eq , Enum , Generic , Hashable , Ord , Show )
102-
103- deriving via GenericArbitrary NarHashMode
104- instance Arbitrary NarHashMode
113+ parseHashType :: Parser Text
114+ parseHashType =
115+ (" sha256" <|> " sha512" <|> " sha1" <|> " md5" ) <* (" :" <|> " -" )
116+
117+ parseHash :: Parser Text
118+ parseHash = Data.Attoparsec.Text. takeWhile1 (/= ' :' )
0 commit comments