hnix-store-remote-0.8.0.0: src/System/Nix/Store/Remote/Serializer.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-|
Description : NixSerializer
Copyright : (c) John Ericson, 2023
Sorki, 2023
|-}
module System.Nix.Store.Remote.Serializer
(
-- * NixSerializer
NixSerializer
, mapErrorS
-- * Errors
, SError(..)
-- ** Runners
, runExceptT
, runG
, runP
-- * Primitives
, int
, bool
, byteString
, enum
, text
, maybeText
-- * UTCTime
, time
-- * Combinators
, list
, set
, hashSet
, mapS
, mapS'
, vector
, json
-- * ProtoVersion
, protoVersion
, protoFeature
, protoFeatures
-- * StorePath
, storePath
, maybePath
, storePathHashPart
, storePathName
-- * Metadata
, pathMetadata
, validPathInfo
-- * OutputName
, outputName
-- * Signatures
, signature
, narSignature
-- * Some HashAlgo
, someHashAlgo
-- * Digest
, digest
-- * DSum HashAlgo Digest
, namedDigest
-- * Derivation
, basicDerivation
-- * Derivation
, derivedPath
, singleDerivedPath
-- * Build
, buildMode
-- * Logger
, LoggerSError(..)
, activityID
, maybeActivity
, activity
, activityResult
, field
, trace
, errorInfo
, loggerOpCode
, logger
, verbosity
-- * Handshake
, HandshakeSError(..)
, workerMagic
, trustedFlag
-- * Worker protocol
, storeText
, workerOp
-- ** Request
, RequestSError(..)
, storeRequest
-- ** Reply
, ReplySError(..)
, opSuccess
, noop
-- *** Realisation
, realisation
-- *** BuildResult
, buildResult
-- *** GCResult
, gcResult
-- *** GCResult
, gcRoot
-- *** Missing
, missing
-- *** Maybe (Metadata StorePath)
, maybePathMetadata
) where
import Control.Monad qualified
import Control.Monad.Except (MonadError, throwError, )
import Control.Monad.Trans (lift)
import Control.Monad.Trans.Except (ExceptT(..), runExceptT, withExceptT)
import Crypto.Hash (Digest, HashAlgorithm, SHA256)
import Data.Aeson (FromJSON, ToJSON)
import Data.Aeson qualified
import Data.Attoparsec.Text qualified
import Data.Bifunctor qualified
import Data.Bits qualified
import Data.ByteString (ByteString)
import Data.ByteString qualified
import Data.ByteString.Char8 qualified
import Data.ByteString.Lazy qualified
import Data.Dependent.Sum (DSum((:=>)))
import Data.Fixed (Uni)
import Data.Functor.Identity
import Data.HashSet (HashSet)
import Data.HashSet qualified
import Data.Hashable (Hashable)
import Data.Map (Map)
import Data.Map.Strict qualified
import Data.Maybe qualified
import Data.Serialize.Get qualified
import Data.Serialize.Put qualified
import Data.Serializer
import Data.Set (Set)
import Data.Set qualified
import Data.Some (Some(Some))
import Data.Some qualified
import Data.Text (Text)
import Data.Text qualified
import Data.Text.Encoding qualified
import Data.Text.Lazy qualified
import Data.Text.Lazy.Builder (Builder)
import Data.Text.Lazy.Builder qualified
import Data.Time (NominalDiffTime, UTCTime)
import Data.Time.Clock.POSIX qualified
import Data.Vector (Vector)
import Data.Vector qualified
import Data.Word (Word8, Word32, Word64)
import GHC.Generics (Generic)
import System.Nix.Base (BaseEncoding(Base16, NixBase32))
import System.Nix.Base qualified
import System.Nix.Build (BuildMode, BuildResult(..), BuildSuccess(..), BuildFailure(..), BuildSuccessStatus(..), BuildFailureStatus(..))
import System.Nix.ContentAddress (ContentAddress, methodAlgoBuilder, methodAlgoParser)
import System.Nix.ContentAddress qualified
import System.Nix.Derivation.Traditional
import System.Nix.Derivation
import System.Nix.DerivedPath (DerivedPath(..), SingleDerivedPath(..), ParseOutputsError)
import System.Nix.DerivedPath qualified
import System.Nix.Hash (HashAlgo(..))
import System.Nix.Hash qualified
import System.Nix.JSON ()
import System.Nix.OutputName (OutputName)
import System.Nix.OutputName qualified
import System.Nix.Realisation (Realisation(..))
import System.Nix.Signature (Signature, NamedSignature)
import System.Nix.Signature qualified
import System.Nix.Store.Remote.Types
import System.Nix.Store.Types (RepairMode(..))
import System.Nix.StorePath (StoreDir, InvalidNameError, InvalidPathError, StorePath, StorePathHashPart, StorePathName)
import System.Nix.StorePath qualified
import System.Nix.StorePath.Metadata (Metadata(..), StorePathTrust(..))
mapErrorS
:: (e -> e')
-> NixSerializer e a
-> NixSerializer e' a
mapErrorS f s = Serializer
{ getS = withExceptT f $ getS s
, putS = putS s
}
-- * NixSerializer
type NixSerializer e = Serializer (ExceptT e)
-- * Errors
data SError
= SError
| SError_BadPadding
{ badPaddingStr :: ByteString
, badPaddingLen :: Int
, badPaddingPads :: [Word8]
}
| SError_ContentAddress String
| SError_DerivedPath ParseOutputsError
| SError_Digest String
| SError_EnumOutOfMinBound Int
| SError_EnumOutOfMaxBound Int
| SError_HashAlgo String
| SError_IllegalBool Word64
| SError_InvalidNixBase32
| SError_JSONDecoding String
| SError_NarHashMustBeSHA256
| SError_Name InvalidNameError
| SError_Path InvalidPathError
| SError_Signature String
| SError_SingleDerivedPathInvalidTag Word64
| SError_UnknownProtoFeature Text
| SError_InvalidWorkerOp Word64
deriving (Eq, Generic, Show)
-- ** Runners
runG
:: NixSerializer e a
-> ByteString
-> Either (GetSerializerError e) a
runG serializer =
transformGetError
. runGetS
serializer
(runExceptT)
runP
:: NixSerializer e a
-> a
-> ByteString
runP = runPutS
-- * Primitives
int :: Integral a => NixSerializer e a
int = Serializer
{ getS = fromIntegral <$> lift Data.Serialize.Get.getWord64le
, putS = Data.Serialize.Put.putWord64le . fromIntegral
}
bool :: NixSerializer SError Bool
bool = Serializer
{ getS = getS (int @Word64) >>= \case
0 -> pure False
1 -> pure True
x -> throwError $ SError_IllegalBool x
, putS = \case
False -> putS (int @Word8) 0
True -> putS (int @Word8) 1
}
byteString :: NixSerializer SError ByteString
byteString = Serializer
{ getS = do
len <- getS int
st <- lift $ Data.Serialize.Get.getByteString len
Control.Monad.when (len `mod` 8 /= 0) $ do
pads <- unpad $ fromIntegral $ 8 - (len `mod` 8)
Control.Monad.unless
(all (== 0) pads)
$ throwError
$ SError_BadPadding st len pads
pure st
, putS = \x -> do
let len = Data.ByteString.length x
putS int len
Data.Serialize.Put.putByteString x
Control.Monad.when
(len `mod` 8 /= 0)
$ pad $ 8 - (len `mod` 8)
}
where
unpad count =
Control.Monad.replicateM
count
(lift Data.Serialize.Get.getWord8)
pad count =
Control.Monad.replicateM_
count
(Data.Serialize.Put.putWord8 0)
maybeByteString :: NixSerializer SError (Maybe ByteString)
maybeByteString = mapIsoSerializer
(\case
t | Data.ByteString.null t -> Nothing
t | otherwise -> Just t
)
(Data.Maybe.fromMaybe mempty)
byteString
-- | Utility toEnum version checking bounds using Bounded class
toEnumCheckBoundsM
:: forall a m
. ( Bounded a
, Enum a
, MonadError SError m
)
=> Int
-> m a
toEnumCheckBoundsM = \case
x | x < fromEnum (minBound @a) -> throwError $ SError_EnumOutOfMinBound x
x | x > fromEnum (maxBound @a) -> throwError $ SError_EnumOutOfMaxBound x
x | otherwise -> pure $ toEnum x
enum
:: ( Bounded a
, Enum a
)
=> NixSerializer SError a
enum = Serializer
{ getS = getS int >>= toEnumCheckBoundsM
, putS = putS int . fromEnum
}
text :: NixSerializer SError Text
text = mapIsoSerializer
Data.Text.Encoding.decodeUtf8
Data.Text.Encoding.encodeUtf8
byteString
-- TODO Parser Builder
_textBuilder :: NixSerializer SError Builder
_textBuilder = Serializer
{ getS = Data.Text.Lazy.Builder.fromText <$> getS text
, putS = putS text . Data.Text.Lazy.toStrict . Data.Text.Lazy.Builder.toLazyText
}
maybeText :: NixSerializer SError (Maybe Text)
maybeText = mapIsoSerializer
(\case
t | Data.Text.null t -> Nothing
t | otherwise -> Just t
)
(Data.Maybe.fromMaybe mempty)
text
-- * UTCTime
time :: NixSerializer e UTCTime
time = Serializer
{ getS =
Data.Time.Clock.POSIX.posixSecondsToUTCTime
. toPicoSeconds
<$> getS int
, putS =
putS int
. fromPicoSeconds
. Data.Time.Clock.POSIX.utcTimeToPOSIXSeconds
}
where
-- fancy (*10^12), from Int to Uni to Pico(seconds)
toPicoSeconds :: Int -> NominalDiffTime
toPicoSeconds n = realToFrac (toEnum n :: Uni)
-- fancy (`div`10^12), from Pico to Uni to Int
fromPicoSeconds :: NominalDiffTime -> Int
fromPicoSeconds = (fromEnum :: Uni -> Int) . realToFrac
-- * Combinators
list
:: NixSerializer e a
-> NixSerializer e [a]
list s = Serializer
{ getS = do
count <- getS int
Control.Monad.replicateM count (getS s)
, putS = \xs -> do
putS int (length xs)
mapM_ (putS s) xs
}
set
:: Ord a
=> NixSerializer e a
-> NixSerializer e (Set a)
set =
mapIsoSerializer
Data.Set.fromList
Data.Set.toList
. list
hashSet
:: ( Eq a
, Hashable a
)
=> NixSerializer e a
-> NixSerializer e (HashSet a)
hashSet =
mapIsoSerializer
Data.HashSet.fromList
Data.HashSet.toList
. list
mapS'
:: Ord k
=> NixSerializer e (k, v)
-> NixSerializer e (Map k v)
mapS' kv =
mapIsoSerializer
Data.Map.Strict.fromList
Data.Map.Strict.toList
$ list
$ kv
mapS
:: Ord k
=> NixSerializer e k
-> NixSerializer e v
-> NixSerializer e (Map k v)
mapS k v = mapS' $ tup k v
vector
:: Ord a
=> NixSerializer e a
-> NixSerializer e (Vector a)
vector =
mapIsoSerializer
Data.Vector.fromList
Data.Vector.toList
. list
json
:: ( FromJSON a
, ToJSON a
)
=> NixSerializer SError a
json = mapPrismSerializer jsonP
$ mapIsoSerializer
Data.ByteString.Lazy.fromStrict
Data.ByteString.Lazy.toStrict
byteString
jsonP
:: ( FromJSON a
, ToJSON a
)
=> AlmostPrism (ExceptT SError) Data.ByteString.Lazy.ByteString a
jsonP = AlmostPrism
( ExceptT
. Identity
. Data.Bifunctor.first SError_JSONDecoding
. Data.Aeson.eitherDecode
)
Data.Aeson.encode
-- * ProtoVersion
-- protoVersion_major & 0xFF00
-- protoVersion_minor & 0x00FF
-- Features are exchanged separately during handshake, not on wire here.
protoVersion :: NixSerializer e ProtoVersion
protoVersion = Serializer
{ getS = do
v <- getS (int @Word32)
pure ProtoVersion
{ protoVersion_major = fromIntegral $ Data.Bits.shiftR v 8
, protoVersion_minor = fromIntegral $ v Data.Bits..&. 0x00FF
, protoVersion_features = mempty
}
, putS = \p ->
putS (int @Word32)
$ ((Data.Bits.shiftL (fromIntegral $ protoVersion_major p :: Word32) 8)
Data.Bits..|. fromIntegral (protoVersion_minor p))
}
protoFeature :: NixSerializer SError ProtoFeature
protoFeature =
mapPrismSerializer
(AlmostPrism
(\t ->
maybe
(throwError $ SError_UnknownProtoFeature t)
pure
(protoFeatureFromText t)
)
protoFeatureToText
)
text
-- | Features advertised by the other side during handshake.
--
-- Unknown features are dropped rather than rejected. The peer may
-- support features we do not know about, and negotiation intersects
-- with our own feature set anyway.
protoFeatures :: NixSerializer SError (Set ProtoFeature)
protoFeatures = Serializer
{ getS =
Data.Set.fromList
. Data.Maybe.mapMaybe protoFeatureFromText
<$> getS (list text)
, putS =
putS (list text)
. fmap protoFeatureToText
. Data.Set.toList
}
-- * StorePath
storePath :: StoreDir -> NixSerializer SError StorePath
storePath storeDir = mapPrismSerializer (storePathP storeDir) byteString
storePathP :: StoreDir -> AlmostPrism (ExceptT SError) ByteString StorePath
storePathP storeDir = AlmostPrism
{ _almostPrism_get =
ExceptT
. Identity
. Data.Bifunctor.first SError_Path
. System.Nix.StorePath.parsePath storeDir
, _almostPrism_put = System.Nix.StorePath.storePathToRawFilePath storeDir
}
maybePath
:: StoreDir
-> NixSerializer SError (Maybe StorePath)
maybePath storeDir = mapPrismSerializer (maybeAlmostPrism $ storePathP storeDir) maybeByteString
storePathHashPart :: NixSerializer SError StorePathHashPart
storePathHashPart =
mapIsoSerializer
System.Nix.StorePath.unsafeMakeStorePathHashPart
System.Nix.StorePath.unStorePathHashPart
$ mapPrismSerializer
(AlmostPrism
(ExceptT
. Identity
. Data.Bifunctor.first (pure SError_InvalidNixBase32)
. System.Nix.Base.decodeWith NixBase32)
(System.Nix.Base.encodeWith NixBase32)
)
text
storePathName :: NixSerializer SError StorePathName
storePathName =
mapPrismSerializer
(AlmostPrism
(ExceptT
. Identity
. Data.Bifunctor.first SError_Name
. System.Nix.StorePath.mkStorePathName)
System.Nix.StorePath.unStorePathName
)
text
pathMetadata
:: StoreDir
-> NixSerializer SError (Metadata StorePath)
pathMetadata storeDir = Serializer
{ getS = do
metadataDeriverPath <- getS $ maybePath storeDir
digest' <- getS $ digest Base16
let metadataNarHash = System.Nix.Hash.HashAlgo_SHA256 :=> digest'
metadataReferences <- getS $ hashSet $ storePath storeDir
metadataRegistrationTime <- getS time
metadataNarBytes <-
(\case
0 -> Nothing
size -> Just size
) <$> getS int
metadataTrust <- getS storePathTrust
metadataSigs <- getS $ set narSignature
metadataContentAddress <- getS maybeContentAddress
pure $ Metadata{..}
, putS = \Metadata{..} -> do
putS (maybePath storeDir) metadataDeriverPath
let putNarHash
:: DSum HashAlgo Digest
-> PutM ()
putNarHash = \case
System.Nix.Hash.HashAlgo_SHA256 :=> d
-> putS (digest @SHA256 Base16) d
_ -> error "nar hash must be SHA 256"
-- throwError SError_NarHashMustBeSHA256
putNarHash metadataNarHash
putS (hashSet $ storePath storeDir) metadataReferences
putS time metadataRegistrationTime
putS int $ Data.Maybe.fromMaybe 0 metadataNarBytes
putS storePathTrust metadataTrust
putS (set narSignature) metadataSigs
putS maybeContentAddress metadataContentAddress
}
where
maybeContentAddress
:: NixSerializer SError (Maybe ContentAddress)
maybeContentAddress =
mapPrismSerializer
(maybeAlmostPrism $ AlmostPrism
(ExceptT
. Identity
. Data.Bifunctor.first SError_ContentAddress
. System.Nix.ContentAddress.parseContentAddress
)
System.Nix.ContentAddress.buildContentAddress
)
maybeText
storePathTrust
:: NixSerializer SError StorePathTrust
storePathTrust =
mapIsoSerializer
(\case False -> BuiltElsewhere; True -> BuiltLocally)
(\case BuiltElsewhere -> False; BuiltLocally -> True)
bool
-- | Read/write a full ValidPathInfo (StorePath + Metadata) as one unit.
validPathInfo
:: StoreDir
-> NixSerializer SError (StorePath, Metadata StorePath)
validPathInfo storeDir = Serializer
{ getS = do
path <- getS (storePath storeDir)
metadata <- getS (pathMetadata storeDir)
pure (path, metadata)
, putS = \(path, metadata) -> do
putS (storePath storeDir) path
putS (pathMetadata storeDir) metadata
}
-- * OutputName
outputName :: NixSerializer SError OutputName
outputName =
mapIsoSerializer
System.Nix.OutputName.OutputName
System.Nix.OutputName.unOutputName
storePathName
-- * Signatures
signature
:: NixSerializer SError Signature
signature =
mapPrismSerializer
(AlmostPrism
(ExceptT
. Identity
. Data.Bifunctor.first SError_Signature
. Data.Attoparsec.Text.parseOnly
System.Nix.Signature.signatureParser)
(System.Nix.Signature.signatureToText)
)
text
narSignature
:: NixSerializer SError NamedSignature
narSignature =
mapPrismSerializer
(AlmostPrism
(ExceptT
. Identity
. Data.Bifunctor.first SError_Signature
. Data.Attoparsec.Text.parseOnly
System.Nix.Signature.narSignatureParser)
(System.Nix.Signature.narSignatureToText)
)
text
-- * Some HashAlgo
someHashAlgo :: NixSerializer SError (Some HashAlgo)
someHashAlgo =
mapPrismSerializer
(AlmostPrism
(ExceptT
. Identity
. Data.Bifunctor.first SError_HashAlgo
. System.Nix.Hash.textToAlgo)
(Data.Some.foldSome System.Nix.Hash.algoToText)
)
text
-- * Digest
digest
:: forall a
. HashAlgorithm a
=> BaseEncoding
-> NixSerializer SError (Digest a)
digest base = mapPrismSerializer (digestP base) $ text
digestP
:: forall a
. HashAlgorithm a
=> BaseEncoding
-> AlmostPrism (ExceptT SError) Text (Digest a)
digestP base = AlmostPrism
(ExceptT
. Identity
. Data.Bifunctor.first SError_Digest
. System.Nix.Hash.decodeDigestWith @a base)
(System.Nix.Hash.encodeDigestWith base)
-- * DSum HashAlgo Digest
namedDigest :: NixSerializer SError (DSum HashAlgo Digest)
namedDigest = Serializer
{ getS = do
sriHash <- getS text
let (sriName, _h) = Data.Text.breakOn (Data.Text.singleton '-') sriHash
-- bit hacky since mkNamedDigest does the check
-- that the expected matches but we don't know
-- what we expect here (i.e. handle each HashAlgo)
case System.Nix.Hash.mkNamedDigest sriName sriHash of
Left e -> throwError $ SError_Digest e
Right x -> pure x
-- TODO: we also lack a builder for SRI hashes
-- , putS = putS textBuilder . System.Nix.Hash.algoDigestBuilder
, putS = \(algo :=> d) -> do
putS text
$ System.Nix.Hash.algoToText algo
<> (Data.Text.singleton '-')
<> System.Nix.Hash.encodeDigestWith NixBase32 d
}
derivationOutput
:: StoreDir
-> NixSerializer SError FreeformDerivationOutput
derivationOutput storeDir = Serializer
{ getS = do
rawPath <- getS text
rawMethodHashAlgo <- getS text
rawHash <- getS text
parseRawDerivationOutput storeDir $ RawDerivationOutput {..}
, putS = \output -> do
let RawDerivationOutput {..} = renderRawDerivationOutput storeDir output
putS text rawPath
putS text rawMethodHashAlgo
putS text rawHash
}
-- * Derivation
basicDerivation
:: StoreDir
-> NixSerializer SError (TraditionalDerivation' (Set StorePath) FreeformDerivationOutputs)
basicDerivation storeDir = Serializer
{ getS = do
anonOutputs <- getS $ mapS' $ tup outputName $ derivationOutput storeDir
anonInputs <- getS $ set $ storePath storeDir
anonPlatform <- getS text
anonBuilder <- getS text
anonArgs <- getS $ vector text
anonEnv <- getS $ mapS text text
pure $ TraditionalDerivation{..}
, putS = \TraditionalDerivation{..} -> do
putS (mapS' $ tup outputName $ derivationOutput storeDir) anonOutputs
putS (set $ storePath storeDir) anonInputs
putS text anonPlatform
putS text anonBuilder
putS (vector text) anonArgs
putS (mapS text text) anonEnv
}
-- * DerivedPath
derivedPathNew
:: StoreDir
-> NixSerializer SError DerivedPath
derivedPathNew storeDir = Serializer
{ getS = do
p <- getS text
case System.Nix.DerivedPath.parseDerivedPath storeDir p of
Left err -> throwError $ SError_DerivedPath err
Right x -> pure x
, putS = \d -> do
putS text (System.Nix.DerivedPath.derivedPathToText storeDir d)
}
derivedPath
:: StoreDir
-> NixSerializer SError DerivedPath
derivedPath storeDir = Serializer
{ getS = getS $ derivedPathNew storeDir
, putS = putS (derivedPathNew storeDir)
}
-- | Tagged, recursive format used by the builder-rpc-v0 operations,
-- unlike 'derivedPath' which round-trips through the textual
-- rendering.
singleDerivedPath
:: StoreDir
-> NixSerializer SError SingleDerivedPath
singleDerivedPath storeDir = Serializer
{ getS = getS (int @Word64) >>= \case
0 -> SingleDerivedPath_Opaque <$> getS (storePath storeDir)
1 -> do
drvPath <- getS (singleDerivedPath storeDir)
output <- getS outputName
pure $ SingleDerivedPath_Built drvPath output
x -> throwError $ SError_SingleDerivedPathInvalidTag x
, putS = \case
SingleDerivedPath_Opaque p -> do
putS (int @Word64) 0
putS (storePath storeDir) p
SingleDerivedPath_Built drvPath output -> do
putS (int @Word64) 1
putS (singleDerivedPath storeDir) drvPath
putS outputName output
}
-- * Build
buildMode :: NixSerializer SError BuildMode
buildMode = enum
-- * Logger
data LoggerSError
= LoggerSError_Prim SError
| LoggerSError_InvalidOpCode Word64
| LoggerSError_UnknownLogFieldType Word8
deriving (Eq, Generic, Show)
mapPrimE
:: Functor m
=> ExceptT SError m a
-> ExceptT LoggerSError m a
mapPrimE = withExceptT LoggerSError_Prim
maybeActivity :: NixSerializer LoggerSError (Maybe Activity)
maybeActivity = Serializer
{ getS = getS (int @Int) >>= \case
0 -> pure Nothing
x -> mapPrimE $ toEnumCheckBoundsM (x - 100) >>= pure . Just
, putS = \case
Nothing -> putS (int @Int) 0
Just act -> putS activity act
}
activity :: NixSerializer LoggerSError Activity
activity = Serializer
{ getS = mapPrimE $ getS int >>= toEnumCheckBoundsM . (+(-100))
, putS = putS int . (+100) . fromEnum
}
activityID :: NixSerializer LoggerSError ActivityID
activityID = mapIsoSerializer ActivityID unActivityID int
activityResult :: NixSerializer LoggerSError ActivityResult
activityResult = Serializer
{ getS = mapPrimE $ getS int >>= toEnumCheckBoundsM . (+(-100))
, putS = putS int . (+100) . fromEnum
}
field :: NixSerializer LoggerSError Field
field = Serializer
{ getS = getS (int @Word8) >>= \case
0 -> Field_LogInt <$> getS int
1 -> Field_LogStr <$> mapPrimE (getS text)
x -> throwError $ LoggerSError_UnknownLogFieldType x
, putS = \case
Field_LogInt x -> putS int (0 :: Word8) >> putS int x
Field_LogStr x -> putS int (1 :: Word8) >> putS text x
}
trace :: NixSerializer LoggerSError Trace
trace = Serializer
{ getS = do
tracePosition <- (\case 0 -> Nothing; x -> Just x) <$> getS (int @Int)
traceHint <- mapPrimE $ getS text
pure Trace{..}
, putS = \Trace{..} -> do
putS int $ Data.Maybe.fromMaybe 0 tracePosition
putS text traceHint
}
errorInfo :: NixSerializer LoggerSError ErrorInfo
errorInfo = Serializer
{ getS = do
etyp <- mapPrimE $ getS text
Control.Monad.unless (etyp == Data.Text.pack "Error")
$ fail
$ "get ErrorInfo: received unknown error type" ++ show etyp
errorInfoLevel <- getS verbosity
_name <- mapPrimE $ getS text -- removed error name
errorInfoMessage <- mapPrimE $ getS text
errorInfoPosition <- (\case 0 -> Nothing; x -> Just x) <$> getS int
errorInfoTraces <- getS (list trace)
pure ErrorInfo{..}
, putS = \ErrorInfo{..} -> do
do
putS text $ Data.Text.pack "Error"
putS verbosity errorInfoLevel
do
putS text $ Data.Text.pack "Error" -- removed error name
putS text errorInfoMessage
putS int $ Data.Maybe.fromMaybe 0 errorInfoPosition
putS (list trace) errorInfoTraces
}
loggerOpCode :: NixSerializer LoggerSError LoggerOpCode
loggerOpCode = Serializer
{ getS = do
c <- getS int
either
(pure $ throwError (LoggerSError_InvalidOpCode c))
pure
$ word64ToLoggerOpCode c
, putS = putS int . loggerOpCodeToWord64
}
logger
:: ProtoVersion
-> NixSerializer LoggerSError Logger
logger _pv = Serializer
{ getS = getS loggerOpCode >>= \case
LoggerOpCode_Next ->
mapPrimE $
Logger_Next <$> getS text
LoggerOpCode_Read ->
Logger_Read <$> getS int
LoggerOpCode_Write ->
mapPrimE $
Logger_Write <$> getS byteString
LoggerOpCode_Last ->
pure Logger_Last
LoggerOpCode_Error -> do
Logger_Error <$> getS errorInfo
LoggerOpCode_StartActivity -> do
startActivityID <- getS activityID
startActivityVerbosity <- getS verbosity
startActivityType <- getS maybeActivity
startActivityString <- mapPrimE $ getS byteString
startActivityFields <- getS (list field)
startActivityParentID <- getS activityID
pure Logger_StartActivity{..}
LoggerOpCode_StopActivity -> do
stopActivityID <- getS activityID
pure Logger_StopActivity{..}
LoggerOpCode_Result -> do
resultActivityID <- getS activityID
resultType <- getS activityResult
resultFields <- getS (list field)
pure Logger_Result {..}
, putS = \case
Logger_Next s -> do
putS loggerOpCode LoggerOpCode_Next
putS text s
Logger_Read i -> do
putS loggerOpCode LoggerOpCode_Read
putS int i
Logger_Write s -> do
putS loggerOpCode LoggerOpCode_Write
putS byteString s
Logger_Last ->
putS loggerOpCode LoggerOpCode_Last
Logger_Error e -> do
putS loggerOpCode LoggerOpCode_Error
putS errorInfo e
Logger_StartActivity{..} -> do
putS loggerOpCode LoggerOpCode_StartActivity
putS activityID startActivityID
putS verbosity startActivityVerbosity
putS maybeActivity startActivityType
putS byteString startActivityString
putS (list field) startActivityFields
putS activityID startActivityParentID
Logger_StopActivity{..} -> do
putS loggerOpCode LoggerOpCode_StopActivity
putS activityID stopActivityID
Logger_Result{..} -> do
putS loggerOpCode LoggerOpCode_Result
putS activityID resultActivityID
putS activityResult resultType
putS (list field) resultFields
}
verbosity :: NixSerializer LoggerSError Verbosity
verbosity = Serializer
{ getS = mapPrimE $ getS enum
, putS = putS enum
}
-- * Handshake
data HandshakeSError
= HandshakeSError_InvalidWorkerMagic Word64
| HandshakeSError_InvalidTrustedFlag Word8
deriving (Eq, Ord, Generic, Show)
workerMagic :: NixSerializer HandshakeSError WorkerMagic
workerMagic = Serializer
{ getS = do
c <- getS int
either
(pure $ throwError (HandshakeSError_InvalidWorkerMagic c))
pure
$ word64ToWorkerMagic c
, putS = putS int . workerMagicToWord64
}
trustedFlag :: NixSerializer HandshakeSError (Maybe TrustedFlag)
trustedFlag = Serializer
{ getS = do
n :: Word8 <- getS int
case n of
0 -> return $ Nothing
1 -> return $ Just TrustedFlag_Trusted
2 -> return $ Just TrustedFlag_NotTrusted
_ -> throwError (HandshakeSError_InvalidTrustedFlag n)
, putS = \n -> putS int $ case n of
Nothing -> 0 :: Word8
Just TrustedFlag_Trusted -> 1
Just TrustedFlag_NotTrusted -> 2
}
-- * Worker protocol
storeText :: NixSerializer SError StoreText
storeText = Serializer
{ getS = do
storeTextName <- getS storePathName
storeTextText <- getS text
pure StoreText{..}
, putS = \StoreText{..} -> do
putS storePathName storeTextName
putS text storeTextText
}
workerOp :: NixSerializer SError WorkerOp
workerOp = Serializer
{ getS = getS int >>=
either
(throwError . SError_InvalidWorkerOp)
pure
. word64ToWorkerOp
, putS = putS int . workerOpToWord64
}
-- * Request
data RequestSError
= RequestSError_NotYetImplemented WorkerOp
| RequestSError_ReservedOp WorkerOp
| RequestSError_PrimGet SError
| RequestSError_PrimWorkerOp SError
deriving (Eq, Generic, Show)
storeRequest
:: StoreDir
-> ProtoVersion
-> NixSerializer RequestSError (Some StoreRequest)
storeRequest storeDir _pv = Serializer
{ getS = withExceptT RequestSError_PrimWorkerOp (getS workerOp) >>= \case
WorkerOp_AddToStore -> mapGetE $ do
pathName <- getS storePathName
camStr <- getS text
(method, hashAlgo) <- case Data.Attoparsec.Text.parseOnly methodAlgoParser camStr of
Left e -> fail e
Right x -> pure x
refs <- getS (set (storePath storeDir))
repairBool <- getS bool
let repair = if repairBool then RepairMode_DoRepair else RepairMode_DontRepair
pure $ Some (AddToStore pathName method hashAlgo refs repair)
WorkerOp_AddToStoreScanning -> mapGetE $ do
pathName <- getS storePathName
camStr <- getS text
(method, hashAlgo) <- case Data.Attoparsec.Text.parseOnly methodAlgoParser camStr of
Left e -> fail e
Right x -> pure x
pure $ Some (AddToStoreScanning pathName method hashAlgo)
WorkerOp_AddToStoreNar -> mapGetE $ do
storePath' <- getS $ storePath storeDir
metadata <- getS $ pathMetadata storeDir
repair <- getS bool
let repairMode = if repair then RepairMode_DoRepair else RepairMode_DontRepair
dontCheckSigs <- getS bool
let checkSigs = if dontCheckSigs then CheckMode_DontCheck else CheckMode_DoCheck
pure $ Some (AddToStoreNar storePath' metadata repairMode checkSigs)
WorkerOp_AddTextToStore -> mapGetE $ do
txt <- getS storeText
paths <- getS $ hashSet $ storePath storeDir
let repair = RepairMode_DontRepair
pure $ Some (AddTextToStore txt paths repair)
WorkerOp_AddSignatures -> mapGetE $ do
path <- getS $ storePath storeDir
signatures <- getS (set signature)
pure $ Some (AddSignatures path signatures)
WorkerOp_AddIndirectRoot -> mapGetE $ do
Some . AddIndirectRoot <$> getS (storePath storeDir)
WorkerOp_AddTempRoot -> mapGetE $ do
Some . AddTempRoot <$> getS (storePath storeDir)
WorkerOp_BuildPaths -> mapGetE $ do
derived <- getS (set $ derivedPath storeDir)
buildMode' <- getS buildMode
pure $ Some (BuildPaths derived buildMode')
WorkerOp_BuildDerivation -> mapGetE $ do
path <- getS $ storePath storeDir
let name = System.Nix.StorePath.storePathName path
drv0 <- getS $ basicDerivation storeDir
let drv1 = withName name drv0
outputs <- toSpecificOutputs storeDir name $ outputs drv1
let drv2 = drv1 { outputs = outputs }
buildMode' <- getS buildMode
pure $ Some (BuildDerivation path drv2 buildMode')
WorkerOp_CollectGarbage -> mapGetE $ do
gcOptionsOperation <- getS enum
gcOptionsPathsToDelete <- getS (hashSet $ storePath storeDir)
gcOptionsIgnoreLiveness <- getS bool
gcOptionsMaxFreed <- getS int
-- obsolete fields
Control.Monad.forM_ [0..(2 :: Word8)]
$ pure $ getS (int @Word8)
pure $ Some (CollectGarbage GCOptions{..})
WorkerOp_EnsurePath -> mapGetE $ do
Some . EnsurePath <$> getS (storePath storeDir)
WorkerOp_FindRoots -> mapGetE $ do
pure $ Some FindRoots
WorkerOp_IsValidPath -> mapGetE $ do
Some . IsValidPath <$> getS (storePath storeDir)
WorkerOp_NarFromPath -> mapGetE $ do
Some . NarFromPath <$> getS (storePath storeDir)
WorkerOp_QueryValidPaths -> mapGetE $ do
paths <- getS (hashSet $ storePath storeDir)
substituteMode <- getS enum
pure $ Some (QueryValidPaths paths substituteMode)
WorkerOp_QueryAllValidPaths -> mapGetE $ do
pure $ Some QueryAllValidPaths
WorkerOp_QuerySubstitutablePaths -> mapGetE $ do
Some . QuerySubstitutablePaths <$> getS (hashSet $ storePath storeDir)
WorkerOp_QueryPathInfo -> mapGetE $ do
Some . QueryPathInfo <$> getS (storePath storeDir)
WorkerOp_QueryReferrers -> mapGetE $ do
Some . QueryReferrers <$> getS (storePath storeDir)
WorkerOp_QueryValidDerivers -> mapGetE $ do
Some . QueryValidDerivers <$> getS (storePath storeDir)
WorkerOp_QueryDerivationOutputs -> mapGetE $ do
Some . QueryDerivationOutputs <$> getS (storePath storeDir)
WorkerOp_QueryDerivationOutputNames -> mapGetE $ do
Some . QueryDerivationOutputNames <$> getS (storePath storeDir)
WorkerOp_QueryPathFromHashPart -> mapGetE $ do
Some . QueryPathFromHashPart <$> getS storePathHashPart
WorkerOp_QueryMissing -> mapGetE $ do
Some . QueryMissing <$> getS (set $ derivedPath storeDir)
WorkerOp_OptimiseStore -> mapGetE $ do
pure $ Some OptimiseStore
WorkerOp_SubmitOutput -> mapGetE $ do
path <- getS $ singleDerivedPath storeDir
output <- getS outputName
pure $ Some (SubmitOutput path output)
WorkerOp_SyncWithGC -> mapGetE $ do
pure $ Some SyncWithGC
WorkerOp_VerifyStore -> mapGetE $ do
checkMode <- getS enum
repairMode <- getS enum
pure $ Some (VerifyStore checkMode repairMode)
w@WorkerOp_Reserved_0__ -> reserved w
w@WorkerOp_Reserved_2__ -> reserved w
w@WorkerOp_Reserved_15__ -> reserved w
w@WorkerOp_Reserved_17__ -> reserved w
w@WorkerOp_AddBuildLog -> notYet w
w@WorkerOp_AddMultipleToStore -> notYet w
w@WorkerOp_BuildPathsWithResults -> notYet w
w@WorkerOp_ClearFailedPaths -> notYet w
w@WorkerOp_ExportPath -> notYet w
w@WorkerOp_HasSubstitutes -> notYet w
w@WorkerOp_ImportPaths -> notYet w
w@WorkerOp_QueryDerivationOutputMap -> notYet w
w@WorkerOp_QueryDeriver -> notYet w
w@WorkerOp_QueryFailedPaths -> notYet w
w@WorkerOp_QueryPathHash -> notYet w
w@WorkerOp_QueryRealisation -> notYet w
w@WorkerOp_QuerySubstitutablePathInfo -> notYet w
w@WorkerOp_QuerySubstitutablePathInfos -> notYet w
w@WorkerOp_QueryReferences -> notYet w
w@WorkerOp_RegisterDrvOutput -> notYet w
w@WorkerOp_SetOptions -> notYet w
, putS = \case
Some (AddToStore pathName method hashAlgo refs repair) -> do
putS workerOp WorkerOp_AddToStore
putS storePathName pathName
putS text
$ Data.Text.Lazy.toStrict
$ Data.Text.Lazy.Builder.toLazyText
$ methodAlgoBuilder method hashAlgo
putS (set (storePath storeDir)) refs
putS bool (repair == RepairMode_DoRepair)
Some (AddToStoreScanning pathName method hashAlgo) -> do
putS workerOp WorkerOp_AddToStoreScanning
putS storePathName pathName
putS text
$ Data.Text.Lazy.toStrict
$ Data.Text.Lazy.Builder.toLazyText
$ methodAlgoBuilder method hashAlgo
Some (AddToStoreNar storePath' metadata repair checkSigs) -> do
putS workerOp WorkerOp_AddToStoreNar
putS (storePath storeDir) storePath'
putS (pathMetadata storeDir) metadata
putS bool $ repair == RepairMode_DoRepair
putS bool $ checkSigs == CheckMode_DontCheck
Some (AddTextToStore txt paths _repair) -> do
putS workerOp WorkerOp_AddTextToStore
putS storeText txt
putS (hashSet $ storePath storeDir) paths
Some (AddSignatures path signatures) -> do
putS workerOp WorkerOp_AddSignatures
putS (storePath storeDir) path
putS (set signature) signatures
Some (AddIndirectRoot path) -> do
putS workerOp WorkerOp_AddIndirectRoot
putS (storePath storeDir) path
Some (AddTempRoot path) -> do
putS workerOp WorkerOp_AddTempRoot
putS (storePath storeDir) path
Some (BuildPaths derived buildMode') -> do
putS workerOp WorkerOp_BuildPaths
putS (set $ derivedPath storeDir) derived
putS buildMode buildMode'
Some (BuildDerivation path drv0 buildMode') -> do
putS workerOp WorkerOp_BuildDerivation
putS (storePath storeDir) path
let drv1 = drv0 { outputs = fromSpecificOutputs storeDir (name drv0) $ outputs drv0 }
let drv2 = withoutName drv1
putS (basicDerivation storeDir) drv2
putS buildMode buildMode'
Some (CollectGarbage GCOptions{..}) -> do
putS workerOp WorkerOp_CollectGarbage
putS enum gcOptionsOperation
putS (hashSet $ storePath storeDir) gcOptionsPathsToDelete
putS bool gcOptionsIgnoreLiveness
putS int gcOptionsMaxFreed
-- obsolete fields
Control.Monad.forM_ [0..(2 :: Word8)]
$ pure $ putS int (0 :: Word8)
Some (EnsurePath path) -> do
putS workerOp WorkerOp_EnsurePath
putS (storePath storeDir) path
Some FindRoots -> do
putS workerOp WorkerOp_FindRoots
Some (IsValidPath path) -> do
putS workerOp WorkerOp_IsValidPath
putS (storePath storeDir) path
Some (NarFromPath path) -> do
putS workerOp WorkerOp_NarFromPath
putS (storePath storeDir) path
Some (QueryValidPaths paths substituteMode) -> do
putS workerOp WorkerOp_QueryValidPaths
putS (hashSet $ storePath storeDir) paths
putS enum substituteMode
Some QueryAllValidPaths -> do
putS workerOp WorkerOp_QueryAllValidPaths
Some (QuerySubstitutablePaths paths) -> do
putS workerOp WorkerOp_QuerySubstitutablePaths
putS (hashSet $ storePath storeDir) paths
Some (QueryPathInfo path) -> do
putS workerOp WorkerOp_QueryPathInfo
putS (storePath storeDir) path
Some (QueryReferrers path) -> do
putS workerOp WorkerOp_QueryReferrers
putS (storePath storeDir) path
Some (QueryValidDerivers path) -> do
putS workerOp WorkerOp_QueryValidDerivers
putS (storePath storeDir) path
Some (QueryDerivationOutputs path) -> do
putS workerOp WorkerOp_QueryDerivationOutputs
putS (storePath storeDir) path
Some (QueryDerivationOutputNames path) -> do
putS workerOp WorkerOp_QueryDerivationOutputNames
putS (storePath storeDir) path
Some (QueryPathFromHashPart pathHashPart) -> do
putS workerOp WorkerOp_QueryPathFromHashPart
putS storePathHashPart pathHashPart
Some (QueryMissing derived) -> do
putS workerOp WorkerOp_QueryMissing
putS (set $ derivedPath storeDir) derived
Some OptimiseStore -> do
putS workerOp WorkerOp_OptimiseStore
Some (SubmitOutput path output) -> do
putS workerOp WorkerOp_SubmitOutput
putS (singleDerivedPath storeDir) path
putS outputName output
Some SyncWithGC -> do
putS workerOp WorkerOp_SyncWithGC
Some (VerifyStore checkMode repairMode) -> do
putS workerOp WorkerOp_VerifyStore
putS enum checkMode
putS enum repairMode
}
where
mapGetE
:: Functor m
=> ExceptT SError m a
-> ExceptT RequestSError m a
mapGetE = withExceptT RequestSError_PrimGet
notYet
:: MonadError RequestSError m
=> WorkerOp
-> m a
notYet = throwError . RequestSError_NotYetImplemented
reserved
:: MonadError RequestSError m
=> WorkerOp
-> m a
reserved = throwError . RequestSError_ReservedOp
-- ** Reply
data ReplySError
= ReplySError_PrimGet SError
| ReplySError_GCResult SError
| ReplySError_Metadata SError
| ReplySError_Missing SError
| ReplySError_Realisation SError
| ReplySError_RealisationWithId SError
| ReplySError_UnexpectedFalseOpSuccess
deriving (Eq, Generic, Show)
mapGetER
:: Functor m
=> ExceptT SError m a
-> ExceptT ReplySError m a
mapGetER = withExceptT ReplySError_PrimGet
-- | Parse a bool returned at the end of simple operations.
-- This is always 1 (@True@) so we assert that it really is so.
-- Errors for these operations are indicated via @Logger_Error@.
opSuccess :: NixSerializer ReplySError SuccessCodeReply
opSuccess = Serializer
{ getS = do
retCode <- mapGetER $ getS bool
Control.Monad.unless
(retCode == True)
$ throwError ReplySError_UnexpectedFalseOpSuccess
pure SuccessCodeReply
, putS = \_ -> putS bool True
}
noop :: a -> NixSerializer ReplySError a
noop ret = Serializer
{ getS = pure ret
, putS = \_ -> pure ()
}
-- *** Realisation
realisation :: NixSerializer ReplySError Realisation
realisation = mapErrorS ReplySError_Realisation json
-- *** BuildResult
buildResult
:: StoreDir
-> ProtoVersion
-> NixSerializer ReplySError BuildResult
buildResult _storeDir pv = Serializer
{ getS = do
statusWord <- mapGetER $ getS enum
errorMessage <- mapGetER $ getS maybeText
( buildResultTimesBuilt
, isNonDeterministic
, buildResultStartTime
, buildResultStopTime
) <-
mapGetER $ do
tb <- getS int
nondet <- getS bool
start <- (\case x | x == t0 -> Nothing; x -> Just x) <$> getS time
end <- (\case x | x == t0 -> Nothing; x -> Just x) <$> getS time
pure $ (tb, nondet, start, end)
Control.Monad.unless (hasFeature ProtoFeature_RealisationWithPathNotHash pv)
$ fail "missing required feature: realisation-with-path-not-hash"
parsedBuiltOutputs <-
getS (mapS (mapErrorS ReplySError_PrimGet outputName) realisation)
let buildResultStatus = case (wireToStatus statusWord, errorMessage) of
(Right successStatus, _) -> Right $ BuildSuccess successStatus parsedBuiltOutputs
(Left failureStatus, Just errorMsg) -> Left $ BuildFailure failureStatus errorMsg isNonDeterministic
(Left failureStatus, Nothing) -> Left $ BuildFailure failureStatus Data.Text.empty isNonDeterministic
let buildResultStartTime' = Data.Maybe.fromMaybe t0 buildResultStartTime
buildResultStopTime' = Data.Maybe.fromMaybe t0 buildResultStopTime
buildResultCpuUser = Nothing
buildResultCpuSystem = Nothing
pure BuildResult
{ buildResultStatus = buildResultStatus
, buildResultTimesBuilt = buildResultTimesBuilt
, buildResultStartTime = buildResultStartTime'
, buildResultStopTime = buildResultStopTime'
, buildResultCpuUser = buildResultCpuUser
, buildResultCpuSystem = buildResultCpuSystem
}
, putS = \BuildResult{..} -> do
let (statusWord, errorMessage, isNonDeterministic) = case buildResultStatus of
Right (BuildSuccess st _bo) -> (successStatusToWire st, Nothing, False)
Left (BuildFailure st em nd) -> (failureStatusToWire st, Just em, nd)
putS enum statusWord
putS maybeText errorMessage
putS int buildResultTimesBuilt
putS bool isNonDeterministic
putS time buildResultStartTime
putS time buildResultStopTime
let builtOutputs = case buildResultStatus of
Right (BuildSuccess _ bo) -> bo
Left _ -> mempty
putS (mapS (mapErrorS ReplySError_PrimGet outputName) realisation) builtOutputs
}
where
t0 :: UTCTime
t0 = Data.Time.Clock.POSIX.posixSecondsToUTCTime 0
-- Convert wire format status code to either success or failure status
wireToStatus :: Word8 -> Either BuildFailureStatus BuildSuccessStatus
wireToStatus 0 = Right BuildSuccessStatus_Built
wireToStatus 1 = Right BuildSuccessStatus_Substituted
wireToStatus 2 = Right BuildSuccessStatus_AlreadyValid
wireToStatus 3 = Left BuildFailureStatus_PermanentFailure
wireToStatus 4 = Left BuildFailureStatus_InputRejected
wireToStatus 5 = Left BuildFailureStatus_OutputRejected
wireToStatus 6 = Left BuildFailureStatus_TransientFailure
wireToStatus 7 = Left BuildFailureStatus_CachedFailure
wireToStatus 8 = Left BuildFailureStatus_TimedOut
wireToStatus 9 = Left BuildFailureStatus_MiscFailure
wireToStatus 10 = Left BuildFailureStatus_DependencyFailed
wireToStatus 11 = Left BuildFailureStatus_LogLimitExceeded
wireToStatus 12 = Left BuildFailureStatus_NotDeterministic
wireToStatus 13 = Right BuildSuccessStatus_ResolvesToAlreadyValid
wireToStatus 14 = Left BuildFailureStatus_NoSubstituters
wireToStatus 15 = Left BuildFailureStatus_HashMismatch
wireToStatus _ = Left BuildFailureStatus_MiscFailure
-- Convert success status to wire format
successStatusToWire :: BuildSuccessStatus -> Word8
successStatusToWire BuildSuccessStatus_Built = 0
successStatusToWire BuildSuccessStatus_Substituted = 1
successStatusToWire BuildSuccessStatus_AlreadyValid = 2
successStatusToWire BuildSuccessStatus_ResolvesToAlreadyValid = 13
-- Convert failure status to wire format
failureStatusToWire :: BuildFailureStatus -> Word8
failureStatusToWire BuildFailureStatus_PermanentFailure = 3
failureStatusToWire BuildFailureStatus_InputRejected = 4
failureStatusToWire BuildFailureStatus_OutputRejected = 5
failureStatusToWire BuildFailureStatus_TransientFailure = 6
failureStatusToWire BuildFailureStatus_CachedFailure = 7
failureStatusToWire BuildFailureStatus_TimedOut = 8
failureStatusToWire BuildFailureStatus_MiscFailure = 9
failureStatusToWire BuildFailureStatus_DependencyFailed = 10
failureStatusToWire BuildFailureStatus_LogLimitExceeded = 11
failureStatusToWire BuildFailureStatus_NotDeterministic = 12
failureStatusToWire BuildFailureStatus_NoSubstituters = 14
failureStatusToWire BuildFailureStatus_HashMismatch = 15
-- *** GCResult
gcResult
:: StoreDir
-> NixSerializer ReplySError GCResult
gcResult storeDir = mapErrorS ReplySError_GCResult $ Serializer
{ getS = do
gcResultDeletedPaths <- getS (hashSet $ storePath storeDir)
gcResultBytesFreed <- getS int
Control.Monad.void $ getS (int @Word64) -- obsolete
pure GCResult{..}
, putS = \GCResult{..} -> do
putS (hashSet $ storePath storeDir) gcResultDeletedPaths
putS int gcResultBytesFreed
putS (int @Word64) 0 -- obsolete
}
-- *** GCRoot
gcRoot :: NixSerializer ReplySError GCRoot
gcRoot = Serializer
{ getS = mapGetER $ do
getS byteString >>= \case
p | p == censored -> pure GCRoot_Censored
p -> pure (GCRoot_Path p)
, putS = putS byteString . \case
GCRoot_Censored -> censored
GCRoot_Path p -> p
}
where censored = Data.ByteString.Char8.pack "{censored}"
-- *** Missing
missing
:: StoreDir
-> NixSerializer ReplySError Missing
missing storeDir = mapErrorS ReplySError_Missing $ Serializer
{ getS = do
missingWillBuild <- getS (hashSet $ storePath storeDir)
missingWillSubstitute <- getS (hashSet $ storePath storeDir)
missingUnknownPaths <- getS (hashSet $ storePath storeDir)
missingDownloadSize <- getS int
missingNarSize <- getS int
pure Missing{..}
, putS = \Missing{..} -> do
putS (hashSet $ storePath storeDir) missingWillBuild
putS (hashSet $ storePath storeDir) missingWillSubstitute
putS (hashSet $ storePath storeDir) missingUnknownPaths
putS int missingDownloadSize
putS int missingNarSize
}
-- *** Maybe (Metadata StorePath)
maybePathMetadata
:: StoreDir
-> NixSerializer ReplySError (Maybe (Metadata StorePath))
maybePathMetadata storeDir = mapErrorS ReplySError_Metadata $ Serializer
{ getS = do
valid <- getS bool
if valid
then pure <$> getS (pathMetadata storeDir)
else pure Nothing
, putS = \case
Nothing -> putS bool False
Just pm -> putS bool True >> putS (pathMetadata storeDir) pm
}