packages feed

hnix-store-remote-0.8.0.0: src/System/Nix/Store/Remote/Client.hs

module System.Nix.Store.Remote.Client
  ( addToStore
  , addToStoreNar
  , addToStoreScanning
  , addTextToStore
  , addSignatures
  , addTempRoot
  , addIndirectRoot
  , buildPaths
  , buildDerivation
  , collectGarbage
  , ensurePath
  , findRoots
  , isValidPath
  , narFromPath
  , queryValidPaths
  , queryAllValidPaths
  , querySubstitutablePaths
  , queryPathInfo
  , queryReferrers
  , queryValidDerivers
  , queryDerivationOutputs
  , queryDerivationOutputNames
  , queryPathFromHashPart
  , queryMissing
  , optimiseStore
  , submitOutput
  , syncWithGC
  , verifyStore
  , module System.Nix.Store.Remote.Client.Core
  ) where

import Control.Monad (void, when)
import Control.Monad.Except (throwError)
import Data.ByteString (ByteString)
import Data.HashSet (HashSet)
import Data.Map (Map)
import Data.Set (Set)
import Data.Some (Some)
import Data.Word (Word64)

import System.Nix.Build (BuildMode, BuildResult)
import System.Nix.Derivation.Traditional qualified
import System.Nix.Derivation.ATerm qualified
import System.Nix.DerivedPath (DerivedPath, SingleDerivedPath)
import System.Nix.Hash (HashAlgo(..))
import System.Nix.Nar (NarSource)
import System.Nix.OutputName (OutputName)
import System.Nix.Signature (Signature)
import System.Nix.StorePath (StorePath, StorePathHashPart, StorePathName)
import System.Nix.StorePath.Metadata (Metadata)
import System.Nix.Store.Remote.MonadStore
import System.Nix.Store.Remote.Types.GC (GCOptions, GCResult, GCRoot)
import System.Nix.Store.Remote.Types.CheckMode (CheckMode)
import System.Nix.Store.Remote.Types.ProtoVersion (ProtoFeature(..), hasFeature)
import System.Nix.Store.Remote.Types.Query.Missing (Missing)
import System.Nix.Store.Remote.Types.StoreRequest (StoreRequest(..))
import System.Nix.Store.Remote.Types.StoreText (StoreText)
import System.Nix.Store.Remote.Types.SubstituteMode (SubstituteMode)
import System.Nix.Store.Remote.Client.Core
import System.Nix.ContentAddress (ContentAddressMethod)
import System.Nix.Store.Types (RepairMode(..))

import Control.Monad.IO.Class qualified
import Data.Attoparsec.Text qualified
import Data.Text.IO qualified
import System.Nix.Derivation qualified
import System.Nix.StorePath qualified

-- | Add `NarSource` to the store
addToStore
  :: MonadRemoteStore m
  => StorePathName        -- ^ Name part of the newly created `StorePath`
  -> NarSource IO         -- ^ Provide nar stream
  -> ContentAddressMethod -- ^ Content addressing method
  -> Some HashAlgo        -- ^ Hashing algorithm
  -> Set StorePath        -- ^ References
  -> RepairMode           -- ^ Whether to overwrite an existing valid path, repairing corruption.
                          --   Sent to the daemon as part of the request since the 1.25 wire format.
  -> m StorePath
addToStore name source method hashAlgo refs repair = do
  setNarSource source
  doReq (AddToStore name method hashAlgo refs repair)

-- | Add `NarSource` to the store, letting the daemon scan the
-- contents for references instead of declaring them.
--
-- Only valid on connections where @add-to-store-scanning@ was
-- negotiated, i.e. inside a derivation build with the
-- @recursive-nix@ or @builder-rpc-v0@ system feature.
addToStoreScanning
  :: MonadRemoteStore m
  => StorePathName        -- ^ Name part of the newly created `StorePath`
  -> NarSource IO         -- ^ Provide nar stream
  -> ContentAddressMethod -- ^ Content addressing method
  -> Some HashAlgo        -- ^ Hashing algorithm
  -> m StorePath
addToStoreScanning name source method hashAlgo = do
  requireProtoFeature ProtoFeature_AddToStoreScanning
  setNarSource source
  doReq (AddToStoreScanning name method hashAlgo)

addToStoreNar
  :: MonadRemoteStore m
  => StorePath
  -> Metadata StorePath
  -> RepairMode
  -> CheckMode
  -> (Word64 -> IO(Maybe ByteString))
  -> m ()
addToStoreNar path metadata repair checkSigs source = do
  setDataSource source
  void $ doReq (AddToStoreNar path metadata repair checkSigs)
  pure ()

-- | Add @StoreText@ to the store
-- Reference accepts repair but only uses it
-- to throw error in case of remote talking to nix-daemon.
addTextToStore
  :: MonadRemoteStore m
  => StoreText
  -> HashSet StorePath -- ^ Set of `StorePath`s that the added text references
  -> RepairMode        -- ^ Repair mode, must be `RepairMode_DontRepair` for remote backend
                       --   (only valid for local store)
  -> m StorePath
addTextToStore stext references repair = do
  Control.Monad.when
    (repair == RepairMode_DoRepair)
    $ throwError RemoteStoreError_RapairNotSupportedByRemoteStore

  doReq (AddTextToStore stext references repair)

-- | Add @Signature@s to a store path
addSignatures
  :: MonadRemoteStore m
  => StorePath
  -> Set Signature
  -> m ()
addSignatures p signatures = (void . doReq) (AddSignatures p signatures)

-- | Add temporary garbage collector root.
--
-- This root is removed as soon as the client exits.
addTempRoot
  :: MonadRemoteStore m
  => StorePath
  -> m ()
addTempRoot = void . doReq . AddTempRoot

-- | Add indirect garbage collector root.
addIndirectRoot
  :: MonadRemoteStore m
  => StorePath
  -> m ()
addIndirectRoot = void . doReq . AddIndirectRoot

-- | Build a derivation available at @StorePath@
buildDerivation
  :: MonadRemoteStore m
  => StorePath
  -> BuildMode
  -> m BuildResult
buildDerivation sp mode = do
  sd <- getStoreDir
  drvContents <-
    Control.Monad.IO.Class.liftIO
    $ Data.Text.IO.readFile
    $ System.Nix.StorePath.storePathToFilePath sd sp
  case Data.Attoparsec.Text.parseOnly
    (System.Nix.Derivation.ATerm.parseTraditionalDerivation sd) drvContents of
      Left e -> throwError $ RemoteStoreError_DerivationParse e
      Right drv -> do
        let name = System.Nix.StorePath.storePathName sp
        outputs <- case
          System.Nix.Derivation.toSpecificOutputs sd name $
            System.Nix.Derivation.Traditional.anonOutputs drv
          of
           Nothing -> throwError $ RemoteStoreError_DerivationParse "TODO get error"
           Just os -> pure os
        let drv' = System.Nix.Derivation.Traditional.withName name $
              drv
                { System.Nix.Derivation.Traditional.anonOutputs = outputs
                , System.Nix.Derivation.Traditional.anonInputs =
                    System.Nix.Derivation.Traditional.traditionalSrcs
                      (System.Nix.Derivation.Traditional.anonInputs drv)
                }
        doReq (BuildDerivation sp drv' mode)

-- | Build paths if they are an actual derivations.
--
-- If derivation output paths are already valid, do nothing.
buildPaths
  :: MonadRemoteStore m
  => Set DerivedPath
  -> BuildMode
  -> m ()
buildPaths a b = (void . doReq) (BuildPaths a b)

collectGarbage
  :: MonadRemoteStore m
  => GCOptions
  -> m GCResult
collectGarbage = doReq . CollectGarbage

ensurePath
  :: MonadRemoteStore m
  => StorePath
  -> m ()
ensurePath = void . doReq . EnsurePath

-- | Find garbage collector roots.
findRoots
  :: MonadRemoteStore m
  => m (Map GCRoot StorePath)
findRoots = doReq FindRoots

isValidPath
  :: MonadRemoteStore m
  => StorePath
  -> m Bool
isValidPath = doReq . IsValidPath

-- | Download a NAR file.
narFromPath
  :: MonadRemoteStore m
  => StorePath -- ^ Path to generate a NAR for
  -> Word64 -- ^ Byte length of NAR
  -> (ByteString -> IO()) -- ^ Data sink where NAR bytes will be written
  -> m ()
narFromPath path narSize sink = do
  setDataSink sink
  setDataSinkSize narSize
  void $ doReq (NarFromPath path)

-- | Query valid paths from a set,
-- optionally try to use substitutes
queryValidPaths
  :: MonadRemoteStore m
  => HashSet StorePath
  -- ^ Set of @StorePath@s to query
  -> SubstituteMode
  -- ^ Try substituting missing paths when @SubstituteMode_DoSubstitute@
  -> m (HashSet StorePath)
queryValidPaths a b = doReq (QueryValidPaths a b)

-- | Query all valid paths
queryAllValidPaths
  :: MonadRemoteStore m
  => m (HashSet StorePath)
queryAllValidPaths = doReq QueryAllValidPaths

-- | Query a set of paths substituable from caches
querySubstitutablePaths
  :: MonadRemoteStore m
  => HashSet StorePath
  -> m (HashSet StorePath)
querySubstitutablePaths = doReq . QuerySubstitutablePaths

-- | Query path metadata
queryPathInfo
  :: MonadRemoteStore m
  => StorePath
  -> m (Maybe (Metadata StorePath))
queryPathInfo = doReq . QueryPathInfo

queryReferrers
  :: MonadRemoteStore m
  => StorePath
  -> m (HashSet StorePath)
queryReferrers = doReq . QueryReferrers

queryValidDerivers
  :: MonadRemoteStore m
  => StorePath
  -> m (HashSet StorePath)
queryValidDerivers = doReq . QueryValidDerivers

queryDerivationOutputs
  :: MonadRemoteStore m
  => StorePath
  -> m (HashSet StorePath)
queryDerivationOutputs = doReq . QueryDerivationOutputs

queryDerivationOutputNames
  :: MonadRemoteStore m
  => StorePath
  -> m (HashSet StorePathName)
queryDerivationOutputNames = doReq . QueryDerivationOutputNames

queryPathFromHashPart
  :: MonadRemoteStore m
  => StorePathHashPart
  -> m StorePath
queryPathFromHashPart = doReq . QueryPathFromHashPart

queryMissing
  :: MonadRemoteStore m
  => Set DerivedPath
  -> m Missing
queryMissing = doReq . QueryMissing

optimiseStore
  :: MonadRemoteStore m
  => m ()
optimiseStore = (void . doReq) OptimiseStore

-- | Register a store object as an output of the currently running
-- derivation.
--
-- Only valid on connections where @submit-output@ was negotiated,
-- i.e. inside a derivation build with the @builder-rpc-v0@ system
-- feature.
submitOutput
  :: MonadRemoteStore m
  => SingleDerivedPath
  -> OutputName
  -> m ()
submitOutput path output = do
  requireProtoFeature ProtoFeature_SubmitOutput
  void $ doReq (SubmitOutput path output)

syncWithGC
  :: MonadRemoteStore m
  => m ()
syncWithGC = (void . doReq) SyncWithGC

-- | Fail early with a clear error instead of letting the daemon
-- reject the opcode.
requireProtoFeature
  :: MonadRemoteStore m
  => ProtoFeature
  -> m ()
requireProtoFeature f = do
  pv <- getProtoVersion
  Control.Monad.when
    (not $ hasFeature f pv)
    $ throwError $ RemoteStoreError_ProtocolFeatureNotNegotiated f

verifyStore
  :: MonadRemoteStore m
  => CheckMode
  -> RepairMode
  -> m Bool
verifyStore check repair = doReq (VerifyStore check repair)