content: {- |
  Module                  : Iris.Browse
  Copyright               : (c) 2022 Dmitrii Kovanikov
  SPDX-License-Identifier : MPL-2.0
  Maintainer              : Dmitrii Kovanikov <kovanikov@gmail.com>
  Stability               : Experimental
  Portability             : Portable
  
  Implements a function that opens a given file in a browser.
  
  @since 0.0.0.0
  -}
  module Iris.Browse (
      openInBrowser,
      BrowseException (..),
  ) where
  
  import Control.Exception (Exception, throwIO)
  import System.Directory (findExecutable)
  import System.Environment (lookupEnv)
  import System.Info (os)
  import System.Process (callCommand, showCommandForUser)
  
  {- | Exception thrown by 'openInBrowser'.
  
  @since 0.0.0.0
  -}
  newtype BrowseException
      = -- | Can't find a browser application. Stores the current OS inside.
        --
        -- @since 0.0.0.0
        BrowserNotFoundException String
      deriving stock
          ( Show
            -- ^ @since 0.0.0.0
          )
      deriving newtype
          ( Eq
            -- ^ @since 0.0.0.0
          )
      deriving anyclass
          ( Exception
            -- ^ @since 0.0.0.0
          )
  
  {- | 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
  
  __Throws:__ 'BrowseException' if can't find a browser.
  
  @since 0.0.0.0
  -}
  openInBrowser :: FilePath -> IO ()
  openInBrowser file =
      lookupEnv "BROWSER" >>= \case
          Just browser -> runCommand browser [file]
          Nothing -> case os of
              "darwin" -> runCommand "open" [file]
              "mingw32" -> runCommand "cmd" ["/c", "start", file]
              curOs -> do
                  browserExe <-
                      findFirstExecutable
                          [ "xdg-open"
                          , "cygstart"
                          , "x-www-browser"
                          , "firefox"
                          , "opera"
                          , "mozilla"
                          , "netscape"
                          ]
                  case browserExe of
                      Just browser -> runCommand browser [file]
                      Nothing -> throwIO $ BrowserNotFoundException curOs
  
  -- | Execute a command with arguments.
  runCommand :: FilePath -> [String] -> IO ()
  runCommand cmd args = do
      let cmdStr = showCommandForUser cmd args
      putStrLn $ "⚙  " ++ cmdStr
      callCommand cmdStr
  
  findFirstExecutable :: [FilePath] -> IO (Maybe FilePath)
  findFirstExecutable = \case
      [] -> pure Nothing
      exe : exes ->
          findExecutable exe >>= \case
              Nothing -> findFirstExecutable exes
              Just path -> pure $ Just path
  