{- |
Copyright: (c) 2020 Kowainik
SPDX-License-Identifier: MPL-2.0
Maintainer: Kowainik <xrom.xkov@gmail.com>

Contains implementation of a function that opens a given file in a
browser.
-}

module Stan.Browse
    ( openBrowser
    ) where

import Colourista (errorMessage, infoMessage)
import System.Directory (findExecutable)
import System.Info (os)
import System.Process (callCommand, showCommandForUser)


{- | Open a given file in a browser. The function has the following algorithm:

* Check the @BROWSER@ environment variable
* If it's not set, try to guess browser depending on OS
* If unsuccsessful, print a message
-}
openBrowser :: FilePath -> IO ()
openBrowser :: [Char] -> IO ()
openBrowser [Char]
file = [Char] -> IO (Maybe [Char])
forall (m :: * -> *). MonadIO m => [Char] -> m (Maybe [Char])
lookupEnv [Char]
"BROWSER" IO (Maybe [Char]) -> (Maybe [Char] -> IO ()) -> IO ()
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Just [Char]
browser -> [Char] -> [[Char]] -> IO ()
runCommand [Char]
browser [[Char]
file]
    Maybe [Char]
Nothing -> case [Char]
os of
        [Char]
"darwin"  -> [Char] -> [[Char]] -> IO ()
runCommand [Char]
"open" [[Char]
file]
        [Char]
"mingw32" -> [Char] -> [[Char]] -> IO ()
runCommand [Char]
"cmd"  [[Char]
"/c", [Char]
"start", [Char]
file]
        [Char]
curOs    -> do
            Maybe [Char]
browserExe <- [[Char]] -> IO (Maybe [Char])
findFirstExecutable
                [ [Char]
"xdg-open"
                , [Char]
"cygstart"
                , [Char]
"x-www-browser"
                , [Char]
"firefox"
                , [Char]
"opera"
                , [Char]
"mozilla"
                , [Char]
"netscape"
                ]
            case Maybe [Char]
browserExe of
                Just [Char]
browser -> [Char] -> [[Char]] -> IO ()
runCommand [Char]
browser [[Char]
file]
                Maybe [Char]
Nothing -> do
                    Text -> IO ()
errorMessage (Text -> IO ()) -> Text -> IO ()
forall a b. (a -> b) -> a -> b
$ Text
"Cannot guess browser for the OS: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> [Char] -> Text
forall a. ToText a => a -> Text
toText [Char]
curOs
                    Text -> IO ()
infoMessage Text
"Please set the $BROWSER environment variable to a web launcher"
                    IO ()
forall (m :: * -> *) a. MonadIO m => m a
exitFailure

-- | Execute a command with arguments.
runCommand :: FilePath -> [String] -> IO ()
runCommand :: [Char] -> [[Char]] -> IO ()
runCommand [Char]
cmd [[Char]]
args = do
    let cmdStr :: [Char]
cmdStr = [Char] -> [[Char]] -> [Char]
showCommandForUser [Char]
cmd [[Char]]
args
    [Char] -> IO ()
forall (m :: * -> *). MonadIO m => [Char] -> m ()
putStrLn ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char]
"⚙  " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
cmdStr
    [Char] -> IO ()
callCommand [Char]
cmdStr

findFirstExecutable :: [FilePath] -> IO (Maybe FilePath)
findFirstExecutable :: [[Char]] -> IO (Maybe [Char])
findFirstExecutable = \case
    [] -> Maybe [Char] -> IO (Maybe [Char])
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe [Char]
forall a. Maybe a
Nothing
    [Char]
exe:[[Char]]
exes -> [Char] -> IO (Maybe [Char])
findExecutable [Char]
exe IO (Maybe [Char])
-> (Maybe [Char] -> IO (Maybe [Char])) -> IO (Maybe [Char])
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        Maybe [Char]
Nothing   -> [[Char]] -> IO (Maybe [Char])
findFirstExecutable [[Char]]
exes
        Just [Char]
path -> Maybe [Char] -> IO (Maybe [Char])
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe [Char] -> IO (Maybe [Char]))
-> Maybe [Char] -> IO (Maybe [Char])
forall a b. (a -> b) -> a -> b
$ [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just [Char]
path