packages feed

hurl-workbench-core-0.1.0.0: src/HurlWorkbench/Workspace/Discover.hs

-- | Locate the workspace manifest.
--
--   An explicit @--workspace FILE@ always wins. Otherwise the start directory
--   and each of its parents are searched for 'manifestFileName', stopping at
--   the filesystem root. Neither @$HOME@, a global configuration directory,
--   nor the network is ever consulted.
module HurlWorkbench.Workspace.Discover
  ( WorkspaceSource (..),
    manifestFileName,
    discoverWorkspace,
    workspaceSourcePath,
  )
where

import HurlWorkbench.Prelude
import HurlWorkbench.Workspace.Error (WorkspaceError (..))
import System.Directory (doesFileExist, makeAbsolute)
import System.FilePath (normalise, takeDirectory, (</>))

-- | Where the manifest came from.
data WorkspaceSource
  = -- | Named with @--workspace FILE@.
    ExplicitWorkspace !FilePath
  | -- | Found by walking up from the start directory.
    DiscoveredWorkspace !FilePath
  deriving stock (Generic, Eq, Show)

-- | The manifest file name searched for during discovery.
manifestFileName :: FilePath
manifestFileName = "hurl-workbench.dhall"

-- | The absolute manifest path of a source.
workspaceSourcePath :: WorkspaceSource -> FilePath
workspaceSourcePath = \case
  ExplicitWorkspace path -> path
  DiscoveredWorkspace path -> path

-- | @discoverWorkspace explicit startDirectory@. A relative explicit path is
--   resolved against @startDirectory@.
discoverWorkspace :: Maybe FilePath -> FilePath -> IO (Either WorkspaceError WorkspaceSource)
discoverWorkspace explicit startDirectory = do
  start <- normalise <$> makeAbsolute startDirectory
  case explicit of
    Just file -> do
      let candidate = normalise (start </> file)
      exists <- doesFileExist candidate
      pure $
        if exists
          then Right (ExplicitWorkspace candidate)
          else Left (ManifestNotFound candidate)
    Nothing -> walk start start
  where
    walk start directory = do
      let candidate = directory </> manifestFileName
      exists <- doesFileExist candidate
      let parent = takeDirectory directory
      if exists
        then pure (Right (DiscoveredWorkspace candidate))
        else
          if parent == directory
            then pure (Left (NoWorkspaceFound start))
            else walk start parent