packages feed

hurl-workbench-core-0.1.0.0: src/HurlWorkbench/Hurl/Capabilities.hs

-- | Detection of the external Hurl toolchain used by execution commands.
module HurlWorkbench.Hurl.Capabilities
  ( HurlCapabilities (..),
    detectHurlCapabilities,
    parseHurlVersion,
    hurlCapabilityWarnings,
  )
where

import Control.Exception (IOException, displayException, try)
import Data.ByteString.Lazy qualified as LazyByteString
import Data.Generics.Labels ()
import Data.Text qualified as Text
import Data.Text.Encoding qualified as Text.Encoding
import Data.Text.Encoding.Error (lenientDecode)
import Data.Version (Version, makeVersion, versionBranch)
import HurlWorkbench.Hurl.Format
import HurlWorkbench.Prelude
import System.Directory (findExecutable)
import System.Exit (ExitCode (..))
import System.Process.Typed (proc, readProcess)

data HurlCapabilities = HurlCapabilities
  { hurlExecutable :: !FilePath,
    hurlVersion :: !Version,
    hurlfmt :: !HurlfmtCapabilities
  }
  deriving stock (Generic, Eq, Show)

detectHurlCapabilities :: IO (Either DependencyError HurlCapabilities)
detectHurlCapabilities = do
  located <- findExecutable "hurl"
  case located of
    Nothing -> pure (Left (DependencyNotFound "hurl" "hurl"))
    Just executable -> do
      probed <- try @IOException (readProcess (proc executable ["--version"]))
      case probed of
        Left err -> pure (Left (DependencyNotFound "hurl" (executable <> ": " <> displayException err)))
        Right (exitCode, stdout, stderr)
          | exitCode /= ExitSuccess -> pure (Left (DependencyProbeFailed "hurl" exitCode (decodeOutput stderr)))
          | otherwise -> case parseHurlVersion (decodeOutput stdout) of
              Nothing -> pure (Left (DependencyVersionUnrecognized "hurl" (decodeOutput stdout)))
              Just version
                | version < makeVersion [8, 0, 0] -> pure (Left (DependencyVersionUnsupported "hurl" version (makeVersion [8, 0, 0])))
                | otherwise ->
                    detectHurlfmtCapabilities <&> fmap (HurlCapabilities executable version)

parseHurlVersion :: Text -> Maybe Version
parseHurlVersion = parseHurlfmtVersion

hurlCapabilityWarnings :: HurlCapabilities -> [Text]
hurlCapabilityWarnings capabilities =
  [ "warning: Hurl major version "
      <> Text.pack (show major)
      <> " is newer than the tested Hurl 8.x adapter"
  | let major = case versionBranch (capabilities ^. #hurlVersion) of
          value : _ -> value
          [] -> 0,
    major > 8
  ]

decodeOutput :: LazyByteString.ByteString -> Text
decodeOutput = Text.Encoding.decodeUtf8With lenientDecode . LazyByteString.toStrict