{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module System.Which (which, staticWhich, staticWhichNix) where

import qualified Shelly as Sh
import qualified Data.Text as T
import Language.Haskell.TH (Exp, Q, reportError, runIO)
import Data.Monoid ((<>))
import Data.List (isPrefixOf)

-- | Determine which executable would run if the given path were
--   executed, or return Nothing if a suitable executable cannot be
--   found
which :: FilePath -> IO (Maybe FilePath)
which f = fmap (fmap (T.unpack . Sh.toTextIgnore)) $ Sh.shelly $ Sh.which $ Sh.fromText $ T.pack f

staticWhichImpl :: (FilePath -> Maybe String) -> FilePath -> Q Exp
staticWhichImpl test f = do
  mf' <- runIO $ which f
  case mf' of
    Nothing -> compileError $ "Could not find executable for " <> show f
    Just f' -> case test f' of
      Just err -> compileError err
      Nothing -> [| f' |]
  where
    compileError msg' = do
      let msg = "staticWhich: " <> msg'
      reportError msg
      [| error msg |]

-- | Run `which` at compile time, and substitute the full path to the executable.
staticWhich :: FilePath -> Q Exp
staticWhich = staticWhichImpl (const Nothing)

-- A variant of 'staticWhich' that ensures the executable is in the nix store.
-- This is useful in NixOS to ensure that the resulting executable
-- contains the dependency in its closure and that it refers to the
-- same version at run time as at compile time
staticWhichNix :: FilePath -> Q Exp
staticWhichNix = staticWhichImpl $ \x ->
  if "/nix/store/" `isPrefixOf` x
    then Nothing
    else Just $ "Executable was found in " <> x <> " which is not in /nix/store."