<commit_before>{-# LANGUAGE LambdaCase #-}

module Main (main) where

import Control.Applicative ((<$>), (<*>), pure)
import Control.Exception (bracket)
import System.Directory (canonicalizePath, getCurrentDirectory, getDirectoryContents, setCurrentDirectory)
import System.Environment (getEnv)
import System.Exit (exitWith)
import System.FilePath.Posix ((</>), takeBaseName)
import System.IO (hClose)
import System.IO.Temp (withSystemTempFile)
import System.Posix.Signals (Handler(Catch), installHandler, sigHUP, sigTERM, signalProcess, Signal)
import System.Posix.Types (ProcessID)
import System.Process (createProcess, proc, terminateProcess, waitForProcess, ProcessHandle())
import System.Process.Internals (ProcessHandle__(ClosedHandle, OpenHandle), withProcessHandle)


data App = App
    { appExe :: FilePath
    , appName :: String
    }

data Config = Config
    { configAngelFile :: FilePath
    , configAppsRoot :: FilePath
    , configAppRunRelPath :: FilePath
    }

main :: IO ()
main =
    withConfig $ \c -> do
        angelPH <- createAngle c
        _ <- installHandler sigHUP (Catch (reloadAngel c angelPH)) Nothing 
        _ <- installHandler sigTERM (Catch (terminateProcess angelPH)) Nothing
        waitForProcess angelPH >>= exitWith

createAngle :: Config -> IO ProcessHandle
createAngle c = do
    (_, _, _, ph) <- createProcess (proc "angel" [configAngelFile c])
    return ph

reloadAngel :: Config -> ProcessHandle -> IO ()
reloadAngel c ph =
    rebuildConfig c >> signalProcessHandle sigHUP ph

withConfig :: (Config -> IO ()) -> IO ()
withConfig f = do
    appsRoot <- getEnv "APPSROOT"
    appRunRelPath <- getEnv "APPRUNRELPATH"
    withSystemTempFile "applicator.conf" $ \angelConfigFile _handle -> do
        hClose _handle
        let c = Config angelConfigFile appsRoot appRunRelPath
        rebuildConfig c
        f c

rebuildConfig :: Config -> IO ()
rebuildConfig c =
    getApps c >>= writeFile (configAngelFile c) . concatMap formatAngelEntry

formatAngelEntry :: App -> String
formatAngelEntry a =
    concat
        [ appName a ++ " {\n"
        , "  exec = " ++ show (appExe a) ++ "\n"
        , "}\n"
        ]

getApps :: Config -> IO [App]
getApps c =
    withCurrentDirectory (configAppsRoot c) $
        getDirectoryContents "." >>=
        return . filter (\p -> p /= "." && p /= "..") >>=
        mapM (\p ->
            App <$> canonicalizePath (getAppExe c p)
                <*> pure (getAppName p))

getAppExe :: Config -> FilePath -> FilePath
getAppExe c p =
    p </> configAppRunRelPath c

getAppName :: FilePath -> String
getAppName =
    takeBaseName

getProcessID :: ProcessHandle -> IO (Maybe ProcessID)
getProcessID ph =
    withProcessHandle ph $ \case
        OpenHandle x   -> return $ Just x
        ClosedHandle _ -> return Nothing

signalProcessHandle :: Signal -> ProcessHandle -> IO ()
signalProcessHandle sig ph =
    getProcessID ph >>= \case
        Just pid -> signalProcess sig pid
        Nothing -> putStrLn "signalProcessHandle: process is already dead."

-- Since: directory-1.2.3.0
withCurrentDirectory :: FilePath -> IO a -> IO a
withCurrentDirectory dir action =
    bracket getCurrentDirectory setCurrentDirectory $ \ _ -> do
        setCurrentDirectory dir
        action<commit_msg>archangel: check if executable exists<commit_after>{-# LANGUAGE LambdaCase #-}

module Main (main) where

import Control.Applicative ((<$>), (<*>), pure)
import Control.Exception (bracket)
import Control.Monad (filterM)
import System.Directory (canonicalizePath, doesFileExist, executable, getCurrentDirectory, getDirectoryContents, getPermissions, setCurrentDirectory)
import System.Environment (getEnv)
import System.Exit (exitWith)
import System.FilePath.Posix ((</>), takeBaseName)
import System.IO (hClose)
import System.IO.Temp (withSystemTempFile)
import System.Posix.Signals (Handler(Catch), installHandler, sigHUP, sigTERM, signalProcess, Signal)
import System.Posix.Types (ProcessID)
import System.Process (createProcess, proc, terminateProcess, waitForProcess, ProcessHandle())
import System.Process.Internals (ProcessHandle__(ClosedHandle, OpenHandle), withProcessHandle)


data App = App
    { appExe :: FilePath
    , appName :: String
    }

data Config = Config
    { configAngelFile :: FilePath
    , configAppsRoot :: FilePath
    , configAppRunRelPath :: FilePath
    }

main :: IO ()
main =
    withConfig $ \c -> do
        angelPH <- createAngle c
        _ <- installHandler sigHUP (Catch (reloadAngel c angelPH)) Nothing 
        _ <- installHandler sigTERM (Catch (terminateProcess angelPH)) Nothing
        waitForProcess angelPH >>= exitWith

createAngle :: Config -> IO ProcessHandle
createAngle c = do
    (_, _, _, ph) <- createProcess (proc "angel" [configAngelFile c])
    return ph

reloadAngel :: Config -> ProcessHandle -> IO ()
reloadAngel c ph =
    rebuildConfig c >> signalProcessHandle sigHUP ph

withConfig :: (Config -> IO ()) -> IO ()
withConfig f = do
    appsRoot <- getEnv "APPSROOT"
    appRunRelPath <- getEnv "APPRUNRELPATH"
    withSystemTempFile "applicator.conf" $ \angelConfigFile _handle -> do
        hClose _handle
        let c = Config angelConfigFile appsRoot appRunRelPath
        rebuildConfig c
        f c

rebuildConfig :: Config -> IO ()
rebuildConfig c =
    getApps c >>= writeFile (configAngelFile c) . concatMap formatAngelEntry

formatAngelEntry :: App -> String
formatAngelEntry a =
    concat
        [ appName a ++ " {\n"
        , "  exec = " ++ show (appExe a) ++ "\n"
        , "}\n"
        ]

getApps :: Config -> IO [App]
getApps c =
    withCurrentDirectory (configAppsRoot c) $
        getDirectoryContents "." >>=
        return . filter (\p -> p /= "." && p /= "..") >>=
        filterM (executableExists . getAppExe c) >>=
        mapM (\p ->
            App <$> canonicalizePath (getAppExe c p)
                <*> pure (getAppName p))

getAppExe :: Config -> FilePath -> FilePath
getAppExe c p =
    p </> configAppRunRelPath c

getAppName :: FilePath -> String
getAppName =
    takeBaseName

executableExists :: FilePath -> IO Bool
executableExists p = do
    exists <- doesFileExist p
    if exists
        then executable <$> getPermissions p
        else return False

getProcessID :: ProcessHandle -> IO (Maybe ProcessID)
getProcessID ph =
    withProcessHandle ph $ \case
        OpenHandle x   -> return $ Just x
        ClosedHandle _ -> return Nothing

signalProcessHandle :: Signal -> ProcessHandle -> IO ()
signalProcessHandle sig ph =
    getProcessID ph >>= \case
        Just pid -> signalProcess sig pid
        Nothing -> putStrLn "signalProcessHandle: process is already dead."

-- Since: directory-1.2.3.0
withCurrentDirectory :: FilePath -> IO a -> IO a
withCurrentDirectory dir action =
    bracket getCurrentDirectory setCurrentDirectory $ \ _ -> do
        setCurrentDirectory dir
        action
{'fraction_non_alphanumeric': 0.1074909090909091, 'fraction_numerical': 0.0011636363636363637, 'mean_word_length': 3.1723300970873787, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 93, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1422313', 'n_tokens_mistral': 2041, 'n_tokens_neox': 1975, 'n_words': 592}