hnix-store-core-0.9.0.0: src/System/Nix/DerivedPath.hs
{-# LANGUAGE OverloadedStrings #-}
module System.Nix.DerivedPath (
OutputsSpec(..)
, SingleDerivedPath(..)
, parseSingleDerivedPath
, singleDerivedPathToText
, DerivedPath(..)
, ParseOutputsError(..)
, parseOutputsSpec
, outputsSpecToText
, parseDerivedPath
, derivedPathToText
) where
import GHC.Generics (Generic)
import Data.Set (Set)
import Data.Text (Text)
import System.Nix.OutputName (OutputName, InvalidNameError)
import System.Nix.StorePath (StoreDir(..), StorePath, InvalidPathError)
import Data.Bifunctor qualified
import Data.ByteString.Char8 qualified
import Data.Set qualified
import Data.List qualified
import Data.Text qualified
import System.Nix.OutputName qualified
import System.Nix.StorePath qualified
data OutputsSpec =
OutputsSpec_All
-- ^ Wildcard spec (^*) meaning all outputs
| OutputsSpec_Names (Set OutputName)
-- ^ Set of specific outputs
deriving (Eq, Generic, Ord, Show)
data SingleDerivedPath =
SingleDerivedPath_Opaque StorePath
-- ^ Fully evaluated store path that can't be built
-- but can be fetched
| SingleDerivedPath_Built SingleDerivedPath OutputName
-- ^ Derivation path and the output built from it
deriving (Eq, Generic, Ord, Show)
data DerivedPath =
DerivedPath_Opaque StorePath
-- ^ Fully evaluated store path that can't be built
-- but can be fetched
| DerivedPath_Built SingleDerivedPath OutputsSpec
-- ^ Derivation path and the outputs built from it
deriving (Eq, Generic, Ord, Show)
data ParseOutputsError =
ParseOutputsError_InvalidPath InvalidPathError
| ParseOutputsError_InvalidName InvalidNameError
| ParseOutputsError_NoNames
| ParseOutputsError_NoPrefix StoreDir Text
deriving (Eq, Ord, Show)
parseOutputName :: Text -> Either ParseOutputsError OutputName
parseOutputName =
( Data.Bifunctor.first
ParseOutputsError_InvalidName
. System.Nix.OutputName.mkOutputName
)
parseOutputsSpec :: Text -> Either ParseOutputsError OutputsSpec
parseOutputsSpec t
| t == "*" = Right OutputsSpec_All
| otherwise = do
names <- mapM
parseOutputName
(Data.Text.splitOn "," t)
if null names
then Left ParseOutputsError_NoNames
else Right $ OutputsSpec_Names (Data.Set.fromList names)
outputsSpecToText :: OutputsSpec -> Text
outputsSpecToText = \case
OutputsSpec_All -> "*"
OutputsSpec_Names ns ->
Data.Text.intercalate
","
(fmap System.Nix.OutputName.outputNameToText
(Data.Set.toList ns)
)
parseSingleDerivedPath
:: StoreDir
-> Text
-> Either ParseOutputsError SingleDerivedPath
parseSingleDerivedPath root@(StoreDir sd) path =
let -- We need to do a bit more legwork for case
-- when StoreDir contains '!', which is the delimiter
-- between store path and output name(s).
-- (The Arbitrary instance can generate such paths.)
textRoot = Data.Text.pack
$ Data.ByteString.Char8.unpack sd
in case Data.Text.stripPrefix textRoot path of
Nothing -> Left $ ParseOutputsError_NoPrefix root path
Just woRoot ->
case Data.Text.splitOn "!" woRoot of
[] -> error "internal error, this function should return NonEmpty"
(pathNoPrefix : outputs) -> Data.List.foldl'
(liftA2 SingleDerivedPath_Built)
(SingleDerivedPath_Opaque
<$> (convertError
$ System.Nix.StorePath.parsePathFromText
root
(textRoot <> pathNoPrefix)
))
(parseOutputName <$> outputs)
parseDerivedPath
:: StoreDir
-> Text
-> Either ParseOutputsError DerivedPath
parseDerivedPath root@(StoreDir sd) path =
let -- We need to do a bit more legwork for case
-- when StoreDir contains '!', which is the delimiter
-- between store path and output name(s).
-- (The Arbitrary instance can generate such paths.)
textRoot = Data.Text.pack
$ Data.ByteString.Char8.unpack sd
in case Data.Text.stripPrefix textRoot path of
Nothing -> Left $ ParseOutputsError_NoPrefix root path
Just woRoot ->
case Data.Text.breakOnEnd "!" woRoot of
(r, suffix) ->
if Data.Text.null r
then DerivedPath_Opaque
<$> (convertError
$ System.Nix.StorePath.parsePathFromText
root
path
)
else DerivedPath_Built
<$> parseSingleDerivedPath
root
(textRoot <> Data.Text.dropEnd (Data.Text.length "!") r)
<*> parseOutputsSpec suffix
convertError
:: Either InvalidPathError a
-> Either ParseOutputsError a
convertError = Data.Bifunctor.first ParseOutputsError_InvalidPath
singleDerivedPathToText :: StoreDir -> SingleDerivedPath -> Text
singleDerivedPathToText root = \case
SingleDerivedPath_Opaque p ->
System.Nix.StorePath.storePathToText root p
SingleDerivedPath_Built p o ->
singleDerivedPathToText root p
<> "!"
<> System.Nix.OutputName.outputNameToText o
derivedPathToText :: StoreDir -> DerivedPath -> Text
derivedPathToText root = \case
DerivedPath_Opaque p ->
System.Nix.StorePath.storePathToText root p
DerivedPath_Built p os ->
singleDerivedPathToText root p
<> "!"
<> outputsSpecToText os