{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}

{-# HLINT ignore "Use maybe" #-}
module AirGQL.ExternalAppContext (
  SandboxingConfig (..),
  ExternalAppContext (..),
  getExternalAppContext,
) where

import Protolude (
  FilePath,
  IO,
  Maybe (Just, Nothing),
  Show,
  Text,
  not,
  pure,
  ($),
  (&&),
  (/=),
  (<|>),
  (==),
 )
import Protolude qualified as P

import Data.ByteString qualified as BS
import Data.Text qualified as T
import System.Environment (lookupEnv)
import System.Info (os)
import System.Process.Typed (ExitCode (ExitSuccess), proc, readProcessStdout)


lookupBinaryPath :: Text -> IO (Maybe FilePath)
lookupBinaryPath :: Text -> IO (Maybe String)
lookupBinaryPath Text
name = do
  (ExitCode
code, ByteString
resultBS) <- ProcessConfig () () () -> IO (ExitCode, ByteString)
forall (m :: * -> *) stdin stdoutIgnored stderr.
MonadIO m =>
ProcessConfig stdin stdoutIgnored stderr
-> m (ExitCode, ByteString)
readProcessStdout (ProcessConfig () () () -> IO (ExitCode, ByteString))
-> ProcessConfig () () () -> IO (ExitCode, ByteString)
forall a b. (a -> b) -> a -> b
$ String -> [String] -> ProcessConfig () () ()
proc String
"which" [Text -> String
T.unpack Text
name]
  let result :: Text
result = Text -> Text
T.strip (Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ ByteString -> Text
P.decodeUtf8 (ByteString -> Text) -> ByteString -> Text
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
BS.toStrict ByteString
resultBS
  Maybe String -> IO (Maybe String)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe String -> IO (Maybe String))
-> Maybe String -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$
    if ExitCode
code ExitCode -> ExitCode -> Bool
forall a. Eq a => a -> a -> Bool
== ExitCode
ExitSuccess
      Bool -> Bool -> Bool
&& Text
result Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Text
""
      Bool -> Bool -> Bool
&& Bool -> Bool
not (Text
"which: no" Text -> Text -> Bool
`T.isInfixOf` Text
result)
      then String -> Maybe String
forall a. a -> Maybe a
Just (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack Text
result
      else Maybe String
forall a. Maybe a
Nothing


data SandboxingConfig = SandboxingConfig
  { SandboxingConfig -> String
firejail :: FilePath
  , SandboxingConfig -> [String]
extraBinds :: [FilePath]
  }
  deriving (Int -> SandboxingConfig -> ShowS
[SandboxingConfig] -> ShowS
SandboxingConfig -> String
(Int -> SandboxingConfig -> ShowS)
-> (SandboxingConfig -> String)
-> ([SandboxingConfig] -> ShowS)
-> Show SandboxingConfig
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SandboxingConfig -> ShowS
showsPrec :: Int -> SandboxingConfig -> ShowS
$cshow :: SandboxingConfig -> String
show :: SandboxingConfig -> String
$cshowList :: [SandboxingConfig] -> ShowS
showList :: [SandboxingConfig] -> ShowS
Show)


data ExternalAppContext = ExternalAppContext
  { ExternalAppContext -> String
sqlite :: FilePath
  , ExternalAppContext -> Maybe String
sqliteLib :: Maybe FilePath
  , ExternalAppContext -> Text
baseUrl :: Text
  }
  deriving (Int -> ExternalAppContext -> ShowS
[ExternalAppContext] -> ShowS
ExternalAppContext -> String
(Int -> ExternalAppContext -> ShowS)
-> (ExternalAppContext -> String)
-> ([ExternalAppContext] -> ShowS)
-> Show ExternalAppContext
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ExternalAppContext -> ShowS
showsPrec :: Int -> ExternalAppContext -> ShowS
$cshow :: ExternalAppContext -> String
show :: ExternalAppContext -> String
$cshowList :: [ExternalAppContext] -> ShowS
showList :: [ExternalAppContext] -> ShowS
Show)


getExternalAppContext :: Text -> IO ExternalAppContext
getExternalAppContext :: Text -> IO ExternalAppContext
getExternalAppContext Text
baseUrl = do
  Maybe String
sqlite <- Text -> IO (Maybe String)
lookupBinaryPath Text
"sqlite3"
  Maybe String
sqliteEnv <- String -> IO (Maybe String)
lookupEnv String
"AIRGQL_SQLITE_BIN"
  Maybe String
sqliteLib <- String -> IO (Maybe String)
lookupEnv String
"AIRGQL_SQLITE_LIB"

  ExternalAppContext -> IO ExternalAppContext
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ExternalAppContext -> IO ExternalAppContext)
-> ExternalAppContext -> IO ExternalAppContext
forall a b. (a -> b) -> a -> b
$
    ExternalAppContext
      { $sel:baseUrl:ExternalAppContext :: Text
baseUrl = Text
baseUrl
      , $sel:sqlite:ExternalAppContext :: String
sqlite = String -> Maybe String -> String
forall a. a -> Maybe a -> a
P.fromMaybe String
"/usr/bin/sqlite3" (Maybe String -> String) -> Maybe String -> String
forall a b. (a -> b) -> a -> b
$ Maybe String
sqliteEnv Maybe String -> Maybe String -> Maybe String
forall a. Maybe a -> Maybe a -> Maybe a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Maybe String
sqlite
      , $sel:sqliteLib:ExternalAppContext :: Maybe String
sqliteLib =
          Maybe String
sqliteLib
            Maybe String -> Maybe String -> Maybe String
forall a. Maybe a -> Maybe a -> Maybe a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> if String
os String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"darwin"
              then String -> Maybe String
forall a. a -> Maybe a
Just String
"/usr/local/opt/sqlite/lib/libsqlite3.dylib"
              else Maybe String
forall a. Maybe a
Nothing
      }