{-| Description : Types and effects for interacting with the Nix store. Maintainer : Shea Levy -} {-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} module System.Nix.Path ( FilePathPart(..) , PathHashAlgo , Path(..) , pathToText , PathSet , SubstitutablePathInfo(..) , ValidPathInfo(..) , PathName(..) , filePathPart , pathName , Roots ) where import System.Nix.Hash (Digest(..), HashAlgorithm(Truncated, SHA256)) import System.Nix.Internal.Hash import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as BSC import Data.Hashable (Hashable (..), hashPtrWithSalt) import Data.HashMap.Strict (HashMap) import Data.HashSet (HashSet) import Data.Map.Strict (Map) import Data.Monoid import Data.Text (Text) import qualified Data.Text as T import System.IO.Unsafe (unsafeDupablePerformIO) import Text.Regex.Base.RegexLike (makeRegex, matchTest) import Text.Regex.TDFA.Text (Regex) -- | The hash algorithm used for store path hashes. type PathHashAlgo = Truncated 20 SHA256 -- | The name portion of a Nix path. -- -- Must be composed of a-z, A-Z, 0-9, +, -, ., _, ?, and =, can't -- start with a ., and must have at least one character. newtype PathName = PathName { pathNameContents :: Text -- ^ The contents of the path name } deriving (Eq, Ord, Show, Hashable) -- | A regular expression for matching a valid 'PathName' nameRegex :: Regex nameRegex = makeRegex ("[a-zA-Z0-9\\+\\-\\_\\?\\=][a-zA-Z0-9\\+\\-\\.\\_\\?\\=]*" :: String) -- | Construct a 'PathName', assuming the provided contents are valid. pathName :: Text -> Maybe PathName pathName n = case matchTest nameRegex n of True -> Just $ PathName n False -> Nothing -- | A path in a store. data Path = Path !(Digest PathHashAlgo) !PathName deriving (Eq, Ord, Show) pathToText :: Text -> Path -> Text pathToText storeDir (Path h nm) = storeDir <> "/" <> encodeBase32 h <> "-" <> pathNameContents nm type PathSet = HashSet Path -- | Information about substitutes for a 'Path'. data SubstitutablePathInfo = SubstitutablePathInfo { -- | The .drv which led to this 'Path'. deriver :: !(Maybe Path) , -- | The references of the 'Path' references :: !PathSet , -- | The (likely compressed) size of the download of this 'Path'. downloadSize :: !Integer , -- | The size of the uncompressed NAR serialization of this -- 'Path'. narSize :: !Integer } deriving (Eq, Ord, Show) -- | Information about @Path@ data ValidPathInfo = ValidPathInfo { -- | Path itself path :: !Path , -- | The .drv which led to this 'Path'. deriverVP :: !(Maybe Path) , -- | NAR hash narHash :: !Text , -- | The references of the 'Path' referencesVP :: !PathSet , -- | Registration time should be time_t registrationTime :: !Integer , -- | The size of the uncompressed NAR serialization of this -- 'Path'. narSizeVP :: !Integer , -- | Whether the path is ultimately trusted, that is, it's a -- derivation output that was built locally. ultimate :: !Bool , -- | Signatures sigs :: ![Text] , -- | Content-addressed -- Store path is computed from a cryptographic hash -- of the contents of the path, plus some other bits of data like -- the "name" part of the path. -- -- ‘ca’ has one of the following forms: -- * ‘text:sha256:’ (paths by makeTextPath() / addTextToStore()) -- * ‘fixed:::’ (paths by makeFixedOutputPath() / addToStore()) ca :: !Text } deriving (Eq, Ord, Show) -- | A valid filename or directory name newtype FilePathPart = FilePathPart { unFilePathPart :: BSC.ByteString } deriving (Eq, Ord, Show) -- | Construct FilePathPart from Text by checking that there -- are no '/' or '\\NUL' characters filePathPart :: BSC.ByteString -> Maybe FilePathPart filePathPart p = case BSC.any (`elem` ['/', '\NUL']) p of False -> Just $ FilePathPart p True -> Nothing type Roots = Map Path Path instance Hashable Path where hashWithSalt s (Path hash name) = s `hashWithSalt` hash `hashWithSalt` name