cachix-1.7: src/Cachix/Client/CNix.hs
module Cachix.Client.CNix where
import Hercules.CNix.Store (Store, StorePath)
import qualified Hercules.CNix.Store as Store
import Language.C.Inline.Cpp.Exception
import Protolude
import System.Console.Pretty (Color (..), Style (..), color, style)
-- | Checks whether a store path is valid.
validateStorePath :: Store -> StorePath -> IO (Maybe StorePath)
validateStorePath store storePath = do
isValid <- Store.isValidPath store storePath `catchNixError` const (return False)
if isValid
then return (Just storePath)
else return Nothing
-- | Like 'validateStorePath', but logs a warning when the path is invalid.
filterInvalidStorePath :: Store -> StorePath -> IO (Maybe StorePath)
filterInvalidStorePath store storePath = do
mstorePath <- validateStorePath store storePath
when (isNothing mstorePath) (logBadStorePath store storePath)
return mstorePath
filterInvalidStorePaths :: Store -> [StorePath] -> IO [Maybe StorePath]
filterInvalidStorePaths store =
traverse (filterInvalidStorePath store)
-- | Follows all symlinks to a store path, returning the final store path.
--
-- Returns Nothing if the path is invalid.
followLinksToStorePath :: Store -> ByteString -> IO (Maybe StorePath)
followLinksToStorePath store storePath =
(Just <$> Store.followLinksToStorePath store storePath)
`catchNixError` \e -> do
logNixError storePath e
return Nothing
data NixError = NixError
{ typ :: Maybe Text,
msg :: Text
}
deriving (Show)
-- | Capture and handle Nix errors
catchNixError :: IO a -> (NixError -> IO a) -> IO a
catchNixError f onError =
handleJust selectNixError onError f
where
selectNixError (CppStdException _eptr msg typ) =
Just $
NixError
{ typ = decodeUtf8With lenientDecode <$> typ,
msg = decodeUtf8With lenientDecode msg
}
selectNixError _ = Nothing
-- | Pretty-print a Nix error.
--
-- There might not be a valid store path at this point.
logNixError :: ByteString -> NixError -> IO ()
logNixError storePath (NixError {..}) =
case typ of
Just "nix::BadStorePath" -> logBadPath storePath
_ -> putErrText $ color Red $ style Bold $ "Nix " <> msg
-- | Print a warning when a store path is invalid.
logBadStorePath :: Store -> StorePath -> IO ()
logBadStorePath store storePath = do
path <- Store.storePathToPath store storePath
logBadPath path
-- | Print a warning when the path is invalid.
--
-- There are two use-cases when this should be used:
-- 1. Converting a file path to a store path.
-- 2. Filtering out a store path that is not valid.
logBadPath :: ByteString -> IO ()
logBadPath path =
putErrText $ color Yellow $ "Warning: " <> decodeUtf8With lenientDecode path <> " is not valid, skipping"
-- | Pretty-print unhandled C++ exceptions from Nix.
handleCppExceptions :: CppException -> IO ()
handleCppExceptions e = do
putErrText ""
case e of
CppStdException _eptr msg _t ->
putErrText $ color Red $ style Bold $ "Nix " <> decodeUtf8With lenientDecode msg
CppNonStdException _eptr mmsg -> do
let msg = fromMaybe "unknown exception" mmsg
putErrText $ color Red $ style Bold $ "C++ exception: " <> decodeUtf8With lenientDecode msg
CppHaskellException he ->
putErrText $ color Red $ style Bold $ "Haskell exception: " <> toS (displayException he)
exitFailure