{-|
Module: IHP.LibDir
Description: Functions to access the IHP lib/ directory at runtime
Copyright: (c) digitally induced GmbH, 2020
-}
module IHP.LibDir (findLibDirectory, ensureSymlink) where

import IHP.Prelude
import qualified System.Directory as Directory
import qualified Data.Text as Text
import qualified System.Process as Process
import qualified System.Posix.Files as Files

-- | Finds the lib
--
-- The location depends on whether the framework is installed through nix
-- or checked out from git inside the current project directory.
--
-- When it's installed with nix, the lib dir is located at @lib/ihp@
-- while the dev server binary is located at @bin/RunDevServer@.
findLibDirectory :: IO Text
findLibDirectory :: IO Text
findLibDirectory = do
    Bool
frameworkMountedLocally <- FilePath -> IO Bool
Directory.doesDirectoryExist FilePath
"IHP"
    Bool
ihpLibSymlinkAvailable <- FilePath -> IO Bool
Directory.doesDirectoryExist FilePath
"build/ihp-lib"
    if Bool
frameworkMountedLocally
        then Text -> IO Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
"IHP/lib/IHP/"
        else if Bool
ihpLibSymlinkAvailable
            then do
                Text -> IO Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
"build/ihp-lib/"
            else do
                Text
binDir <- FilePath -> Text
forall a b. ConvertibleStrings a b => a -> b
cs (FilePath -> Text) -> IO FilePath -> IO Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CreateProcess -> FilePath -> IO FilePath
Process.readCreateProcess (FilePath -> CreateProcess
Process.shell FilePath
"dirname $(which RunDevServer)") FilePath
""
                Text -> IO Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> Text
Text.strip Text
binDir Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"/../lib/IHP/")

-- | Creates the build/ihp-lib symlink if missing
--
-- This is called in dev mode to make sure that build/ihp-lib points to the right IHP version.
-- Otherwise the CLI tools might not work as expected, e.g. @make db@ might fail.
--
-- If the symlink is missing, it tries to fix this by starting a new nix-shell and pinpointing the framework lib dir in there
ensureSymlink :: IO ()
ensureSymlink :: IO ()
ensureSymlink = do
    Bool
frameworkMountedLocally <- FilePath -> IO Bool
Directory.doesDirectoryExist FilePath
"IHP"
    Bool
ihpLibSymlinkAvailable <- FilePath -> IO Bool
Directory.doesDirectoryExist FilePath
"build/ihp-lib"

    Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
ihpLibSymlinkAvailable do
        -- Make sure the build directory exists, otherwise we cannot add the symlink
        Bool -> FilePath -> IO ()
Directory.createDirectoryIfMissing Bool
False FilePath
"build"

        Text
libDir <- if Bool
frameworkMountedLocally
            then Text -> IO Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
"../IHP/lib/IHP/"
            else do
                Text -> IO ()
putStrLn Text
"Building build/ihp-lib. This might take a few seconds"
                Text
binDir <- FilePath -> Text
forall a b. ConvertibleStrings a b => a -> b
cs (FilePath -> Text) -> IO FilePath -> IO Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CreateProcess -> FilePath -> IO FilePath
Process.readCreateProcess (FilePath -> CreateProcess
Process.shell FilePath
"nix-shell --run 'dirname $(which RunDevServer)'") FilePath
""
                Text -> IO Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> Text
Text.strip Text
binDir Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"/../lib/IHP/")
        FilePath -> FilePath -> IO ()
Files.createSymbolicLink (Text -> FilePath
forall a b. ConvertibleStrings a b => a -> b
cs Text
libDir) FilePath
"build/ihp-lib"