module Foreign.Nix.Shellout
(
parseNixExpr, ParseError(..)
, instantiate, InstantiateError(..)
, eval
, realize, RealizeError(..)
, addToStore
, parseInstRealize
, NixError(..)
, StorePath(fromStorePath), Derivation, Realized
, NixExpr
, runNixAction, NixAction(..)
) where
import Protolude hiding (show, isPrefixOf)
import Control.Error hiding (bool, err)
import Data.String (String)
import Data.Text (stripPrefix, lines, isPrefixOf)
import System.FilePath (isValid)
import Text.Show (Show(..))
import qualified Foreign.Nix.Shellout.Helpers as Helpers
import Foreign.Nix.Shellout.Types
newtype NixExpr = NixExpr Text deriving (Show, Eq)
data ParseError
= SyntaxError Text
| UnknownParseError
deriving (Show, Eq)
parseNixExpr :: Text -> NixAction ParseError NixExpr
parseNixExpr e =
bimap parseParseError NixExpr
$ evalNixOutput "nix-instantiate" [ "--parse", "-E", e ]
parseParseError :: Text -> ParseError
parseParseError
(stripPrefix "error: syntax error, "
-> Just mes) = SyntaxError $ mes
parseParseError _ = UnknownParseError
data InstantiateError
= NotADerivation
| UnknownInstantiateError
deriving (Show, Eq)
instantiate :: NixExpr -> NixAction InstantiateError (StorePath Derivation)
instantiate (NixExpr e) =
first parseInstantiateError
$ evalNixOutput "nix-instantiate" [ "-E", e ]
>>= toNixFilePath StorePath
eval :: NixExpr -> NixAction InstantiateError ()
eval (NixExpr e) =
void $ first parseInstantiateError
$ evalNixOutput "nix-instantiate" [ "--eval", "-E", e ]
parseInstantiateError :: Text -> InstantiateError
parseInstantiateError
(stripPrefix "error: expression does not evaluate to a derivation"
-> Just _) = NotADerivation
parseInstantiateError _ = UnknownInstantiateError
data RealizeError = UnknownRealizeError deriving (Show, Eq)
realize :: StorePath Derivation -> NixAction RealizeError (StorePath Realized)
realize (StorePath d) =
storeOp [ "-r", toS d ]
addToStore :: FilePath -> NixAction RealizeError (StorePath Realized)
addToStore fp = storeOp [ "--add", toS fp ]
storeOp :: [Text] -> NixAction RealizeError (StorePath Realized)
storeOp op =
first (const UnknownRealizeError)
$ evalNixOutput "nix-store" op
>>= toNixFilePath StorePath
data NixError
= ParseError ParseError
| InstantiateError InstantiateError
| RealizeError RealizeError deriving (Show, Eq)
parseInstRealize :: Text -> NixAction NixError (StorePath Realized)
parseInstRealize = first ParseError . parseNixExpr
>=> first InstantiateError . instantiate
>=> first RealizeError . realize
evalNixOutput :: Text
-> [Text]
-> NixAction Text Text
evalNixOutput = Helpers.readProcess (\(out, err) -> \case
ExitFailure _ -> throwE $
case mconcat . intersperse "\n"
. dropWhile (not . isPrefixOf "error: ")
. lines $ toS err of
"" -> "nix didn’t output any error message"
s -> s
ExitSuccess -> tryLast
"nix didn’t output a store path" (lines $ toS out))
toNixFilePath :: (String -> a) -> Text -> NixAction Text a
toNixFilePath a p@(toS -> ps) = NixAction $
if isValid ps then (pure $ a ps)
else (throwE $ (nostderr, p <> " is not a filepath!"))
where nostderr = mempty