{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}

module Hie.Cabal.Parser
( Package(..)
, Component(..)
, CompType(..)
, Name
, extractPkgs
, parsePackage'
) where

import           Control.Applicative
import           Control.Monad
import           Data.Attoparsec.Text
import           Data.Char
import           Data.Foldable                                 (asum)
import           Data.Maybe                                    (catMaybes,
                                                                maybeToList)
import           Data.Text                                     (Text)
import qualified Data.Text                                     as T
import           Data.Text.Encoding                            (encodeUtf8)
import           Distribution.ModuleName                       (ModuleName,
                                                                toFilePath)
import           Distribution.Package                          (pkgName,
                                                                unPackageName)
import           Distribution.PackageDescription               (Benchmark (benchmarkBuildInfo, benchmarkInterface, benchmarkName),
                                                                BenchmarkInterface (BenchmarkExeV10),
                                                                Executable (buildInfo, exeName, modulePath),
                                                                ForeignLib (foreignLibBuildInfo, foreignLibName),
                                                                Library (libBuildInfo, libName),
                                                                LibraryName (..),
                                                                TestSuiteInterface (TestSuiteExeV10),
                                                                benchmarkModules,
                                                                exeModules,
                                                                explicitLibModules,
                                                                foreignLibModules)
import           Distribution.PackageDescription.Configuration
import           Distribution.PackageDescription.Parsec
import           Distribution.Types.BuildInfo
import           Distribution.Types.PackageDescription
import           Distribution.Types.TestSuite
import           Distribution.Types.UnqualComponentName
import           Distribution.Utils.Path                       (getSymbolicPath)
import           GHC.IO                                        (unsafePerformIO)
import           System.Directory                              (doesFileExist)
import           System.FilePath                               ((<.>), (</>))


type Name = Text

type Path = Text

type Indent = Int

data Package = Package Name [Component]
  deriving (Int -> Package -> ShowS
[Package] -> ShowS
Package -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Package] -> ShowS
$cshowList :: [Package] -> ShowS
show :: Package -> String
$cshow :: Package -> String
showsPrec :: Int -> Package -> ShowS
$cshowsPrec :: Int -> Package -> ShowS
Show, Package -> Package -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Package -> Package -> Bool
$c/= :: Package -> Package -> Bool
== :: Package -> Package -> Bool
$c== :: Package -> Package -> Bool
Eq, Eq Package
Package -> Package -> Bool
Package -> Package -> Ordering
Package -> Package -> Package
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Package -> Package -> Package
$cmin :: Package -> Package -> Package
max :: Package -> Package -> Package
$cmax :: Package -> Package -> Package
>= :: Package -> Package -> Bool
$c>= :: Package -> Package -> Bool
> :: Package -> Package -> Bool
$c> :: Package -> Package -> Bool
<= :: Package -> Package -> Bool
$c<= :: Package -> Package -> Bool
< :: Package -> Package -> Bool
$c< :: Package -> Package -> Bool
compare :: Package -> Package -> Ordering
$ccompare :: Package -> Package -> Ordering
Ord)

data CompType = Lib | Exe | Test | Bench
  deriving (Int -> CompType -> ShowS
[CompType] -> ShowS
CompType -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CompType] -> ShowS
$cshowList :: [CompType] -> ShowS
show :: CompType -> String
$cshow :: CompType -> String
showsPrec :: Int -> CompType -> ShowS
$cshowsPrec :: Int -> CompType -> ShowS
Show, CompType -> CompType -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CompType -> CompType -> Bool
$c/= :: CompType -> CompType -> Bool
== :: CompType -> CompType -> Bool
$c== :: CompType -> CompType -> Bool
Eq, Eq CompType
CompType -> CompType -> Bool
CompType -> CompType -> Ordering
CompType -> CompType -> CompType
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: CompType -> CompType -> CompType
$cmin :: CompType -> CompType -> CompType
max :: CompType -> CompType -> CompType
$cmax :: CompType -> CompType -> CompType
>= :: CompType -> CompType -> Bool
$c>= :: CompType -> CompType -> Bool
> :: CompType -> CompType -> Bool
$c> :: CompType -> CompType -> Bool
<= :: CompType -> CompType -> Bool
$c<= :: CompType -> CompType -> Bool
< :: CompType -> CompType -> Bool
$c< :: CompType -> CompType -> Bool
compare :: CompType -> CompType -> Ordering
$ccompare :: CompType -> CompType -> Ordering
Ord)

data Component
  = Comp CompType Name Path
  deriving (Int -> Component -> ShowS
[Component] -> ShowS
Component -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Component] -> ShowS
$cshowList :: [Component] -> ShowS
show :: Component -> String
$cshow :: Component -> String
showsPrec :: Int -> Component -> ShowS
$cshowsPrec :: Int -> Component -> ShowS
Show, Component -> Component -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Component -> Component -> Bool
$c/= :: Component -> Component -> Bool
== :: Component -> Component -> Bool
$c== :: Component -> Component -> Bool
Eq, Eq Component
Component -> Component -> Bool
Component -> Component -> Ordering
Component -> Component -> Component
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Component -> Component -> Component
$cmin :: Component -> Component -> Component
max :: Component -> Component -> Component
$cmax :: Component -> Component -> Component
>= :: Component -> Component -> Bool
$c>= :: Component -> Component -> Bool
> :: Component -> Component -> Bool
$c> :: Component -> Component -> Bool
<= :: Component -> Component -> Bool
$c<= :: Component -> Component -> Bool
< :: Component -> Component -> Bool
$c< :: Component -> Component -> Bool
compare :: Component -> Component -> Ordering
$ccompare :: Component -> Component -> Ordering
Ord)

parseQuoted :: Parser Text
parseQuoted :: Parser Text Text
parseQuoted = do
  Char
q <- Char -> Parser Text Char
char Char
'"' forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
char Char
'\''
  Text
s <- (Char -> Bool) -> Parser Text Text
takeTill (forall a. Eq a => a -> a -> Bool
== Char
q)
  Char
_ <- Char -> Parser Text Char
char Char
q
  forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
s

parseString :: Parser Name
parseString :: Parser Text Text
parseString = Parser Text Text
parseQuoted forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Text Text
unqualName

unqualName :: Parser Text
unqualName :: Parser Text Text
unqualName = (Char -> Bool) -> Parser Text Text
takeWhile1 (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. (\Char
c -> Char -> Bool
isSpace Char
c Bool -> Bool -> Bool
|| Char
c forall a. Eq a => a -> a -> Bool
== Char
','))

-- | Comma or space separated list, with optional new lines.
parseList :: Indent -> Parser [Text]
parseList :: Int -> Parser [Text]
parseList Int
i = forall (f :: * -> *) a. Alternative f => f a -> f [a]
many (Parser Text Text
nl forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Text Text
sl)
  where
    sep :: Parser Text ()
sep = forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany (Char -> Parser Text Char
char Char
',' forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Text Char
tabOrSpace)
    com :: Parser Text ()
com = forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser Text Char
tabOrSpace forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Parser Text Text
"--" forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> (Char -> Bool) -> Parser Text ()
skipWhile (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isEndOfLine)
    sl :: Parser Text Text
sl = do
      Parser Text ()
sep
      Text
x <- Parser Text Text
parseString
      Parser Text ()
sep
      forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser Text ()
com
      forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
x

    nl :: Parser Text Text
nl = do
      forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser Text ()
emptyOrComLine forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Text ()
endOfLine
      Int
_ <- Int -> Parser Int
indent Int
i
      Parser Text ()
sep
      forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser Text ()
com
      Text
x <- Parser Text Text
parseString
      Parser Text ()
sep
      forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser Text ()
com
      forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
x

skipToNextLine :: Parser ()
skipToNextLine :: Parser Text ()
skipToNextLine = (Char -> Bool) -> Parser Text ()
skipWhile (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isEndOfLine) forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Parser Text ()
endOfLine

comment :: Parser ()
comment :: Parser Text ()
comment = forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser Text Char
tabOrSpace forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Parser Text Text
"--" forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Parser Text ()
skipToNextLine

emptyOrComLine :: Parser ()
emptyOrComLine :: Parser Text ()
emptyOrComLine = (forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser Text Char
tabOrSpace forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Parser Text ()
endOfLine) forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Text ()
comment

tabOrSpace :: Parser Char
tabOrSpace :: Parser Text Char
tabOrSpace = Char -> Parser Text Char
char Char
' ' forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
char Char
'\t'

-- field :: Indent -> Text -> Parser Text
field ::
  Indent ->
  [Text] ->
  (Indent -> Parser a) ->
  Parser a
field :: forall a. Int -> [Text] -> (Int -> Parser a) -> Parser a
field Int
i [Text]
f Int -> Parser a
p =
  do
    Int
i' <- Int -> Parser Int
indent Int
i
    Text
_ <- forall (t :: * -> *) (f :: * -> *) a.
(Foldable t, Alternative f) =>
t (f a) -> f a
asum forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map Text -> Parser Text Text
asciiCI [Text]
f
    forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser Text Char
tabOrSpace
    Char
_ <- Char -> Parser Text Char
char Char
':'
    forall (f :: * -> *) a. Alternative f => f a -> f ()
skipMany Parser Text Char
tabOrSpace
    a
p' <- Int -> Parser a
p forall a b. (a -> b) -> a -> b
$ Int
i' forall a. Num a => a -> a -> a
+ Int
1
    Parser Text ()
skipToNextLine
    forall (f :: * -> *) a. Applicative f => a -> f a
pure a
p'

-- | Skip at least n spaces
indent :: Indent -> Parser Int
indent :: Int -> Parser Int
indent Int
i = do
  Int
c <- forall (t :: * -> *) a. Foldable t => t a -> Int
length forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
many' Parser Text Char
tabOrSpace
  if Int
c forall a. Ord a => a -> a -> Bool
>= Int
i then forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
c else forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"insufficient indent"

extractPkgs :: Parser [T.Text]
extractPkgs :: Parser [Text]
extractPkgs = forall (m :: * -> *) a. Monad m => m (m a) -> m a
join forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [Maybe a] -> [a]
catMaybes forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
many' (forall a. a -> Maybe a
Just forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Int -> [Text] -> (Int -> Parser a) -> Parser a
field Int
0 [Text
"packages"] Int -> Parser [Text]
parseList forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Parser Text ()
skipToNextLine forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing))

parsePackage' :: T.Text -> Either String Package
parsePackage' :: Text -> Either String Package
parsePackage' Text
t = do
  let bytes :: ByteString
bytes = Text -> ByteString
encodeUtf8 Text
t
  case forall a.
ParseResult a
-> ([PWarning], Either (Maybe Version, NonEmpty PError) a)
runParseResult (ByteString -> ParseResult GenericPackageDescription
parseGenericPackageDescription ByteString
bytes) of
    ([PWarning]
_warnings, Left (Maybe Version, NonEmpty PError)
err) ->
        forall a. HasCallStack => String -> a
error forall a b. (a -> b) -> a -> b
$ String
"Cannot parse Cabal file: " forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> String
show (Maybe Version, NonEmpty PError)
err
    ([PWarning]
_warnings, Right GenericPackageDescription
res) -> do
      let pkg :: PackageDescription
pkg = GenericPackageDescription -> PackageDescription
flattenPackageDescription GenericPackageDescription
res
      forall a b. b -> Either a b
Right forall a b. (a -> b) -> a -> b
$ PackageDescription -> Package
extractPackage PackageDescription
pkg

extractPackage :: PackageDescription -> Package
extractPackage :: PackageDescription -> Package
extractPackage PackageDescription{String
[String]
[(String, String)]
[(CompilerFlavor, VersionRange)]
[Benchmark]
[Executable]
[ForeignLib]
[Library]
[TestSuite]
[SourceRepo]
[SymbolicPath PackageDir LicenseFile]
Maybe Library
Maybe SetupBuildInfo
Maybe BuildType
Either License License
PackageIdentifier
CabalSpecVersion
ShortText
specVersion :: PackageDescription -> CabalSpecVersion
package :: PackageDescription -> PackageIdentifier
licenseRaw :: PackageDescription -> Either License License
licenseFiles :: PackageDescription -> [SymbolicPath PackageDir LicenseFile]
copyright :: PackageDescription -> ShortText
maintainer :: PackageDescription -> ShortText
author :: PackageDescription -> ShortText
stability :: PackageDescription -> ShortText
testedWith :: PackageDescription -> [(CompilerFlavor, VersionRange)]
homepage :: PackageDescription -> ShortText
pkgUrl :: PackageDescription -> ShortText
bugReports :: PackageDescription -> ShortText
sourceRepos :: PackageDescription -> [SourceRepo]
synopsis :: PackageDescription -> ShortText
description :: PackageDescription -> ShortText
category :: PackageDescription -> ShortText
customFieldsPD :: PackageDescription -> [(String, String)]
buildTypeRaw :: PackageDescription -> Maybe BuildType
setupBuildInfo :: PackageDescription -> Maybe SetupBuildInfo
library :: PackageDescription -> Maybe Library
subLibraries :: PackageDescription -> [Library]
executables :: PackageDescription -> [Executable]
foreignLibs :: PackageDescription -> [ForeignLib]
testSuites :: PackageDescription -> [TestSuite]
benchmarks :: PackageDescription -> [Benchmark]
dataFiles :: PackageDescription -> [String]
dataDir :: PackageDescription -> String
extraSrcFiles :: PackageDescription -> [String]
extraTmpFiles :: PackageDescription -> [String]
extraDocFiles :: PackageDescription -> [String]
extraDocFiles :: [String]
extraTmpFiles :: [String]
extraSrcFiles :: [String]
dataDir :: String
dataFiles :: [String]
benchmarks :: [Benchmark]
testSuites :: [TestSuite]
foreignLibs :: [ForeignLib]
executables :: [Executable]
subLibraries :: [Library]
library :: Maybe Library
setupBuildInfo :: Maybe SetupBuildInfo
buildTypeRaw :: Maybe BuildType
customFieldsPD :: [(String, String)]
category :: ShortText
description :: ShortText
synopsis :: ShortText
sourceRepos :: [SourceRepo]
bugReports :: ShortText
pkgUrl :: ShortText
homepage :: ShortText
testedWith :: [(CompilerFlavor, VersionRange)]
stability :: ShortText
author :: ShortText
maintainer :: ShortText
copyright :: ShortText
licenseFiles :: [SymbolicPath PackageDir LicenseFile]
licenseRaw :: Either License License
package :: PackageIdentifier
specVersion :: CabalSpecVersion
..} = Text -> [Component] -> Package
Package Text
n [Component]
cc where
  n :: Text
n = String -> Text
T.pack forall b c a. (b -> c) -> (a -> b) -> a -> c
. PackageName -> String
unPackageName forall a b. (a -> b) -> a -> b
$ PackageIdentifier -> PackageName
pkgName PackageIdentifier
package

  cc :: [Component]
cc = forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall a b. (a -> b) -> a -> b
$
    [CompType
-> Text -> BuildInfo -> [String] -> [ModuleName] -> [Component]
mkComp CompType
Test (UnqualComponentName -> Text
unqName forall a b. (a -> b) -> a -> b
$ TestSuite -> UnqualComponentName
testName TestSuite
t) (TestSuite -> BuildInfo
testBuildInfo TestSuite
t) (TestSuite -> [String]
testExePath TestSuite
t) (TestSuite -> [ModuleName]
testModules TestSuite
t) | TestSuite
t <- [TestSuite]
testSuites] forall a. [a] -> [a] -> [a]
++
    [CompType
-> Text -> BuildInfo -> [String] -> [ModuleName] -> [Component]
mkComp CompType
Bench (UnqualComponentName -> Text
unqName forall a b. (a -> b) -> a -> b
$ Benchmark -> UnqualComponentName
benchmarkName Benchmark
b) (Benchmark -> BuildInfo
benchmarkBuildInfo Benchmark
b) (Benchmark -> [String]
benchmarkExePath Benchmark
b) (Benchmark -> [ModuleName]
benchmarkModules Benchmark
b) | Benchmark
b <- [Benchmark]
benchmarks] forall a. [a] -> [a] -> [a]
++
    [CompType
-> Text -> BuildInfo -> [String] -> [ModuleName] -> [Component]
mkComp CompType
Exe (UnqualComponentName -> Text
unqName forall a b. (a -> b) -> a -> b
$ Executable -> UnqualComponentName
exeName Executable
e) (Executable -> BuildInfo
buildInfo Executable
e) [Executable -> String
modulePath Executable
e] (Executable -> [ModuleName]
exeModules Executable
e) | Executable
e <- [Executable]
executables] forall a. [a] -> [a] -> [a]
++
    [CompType
-> Text -> BuildInfo -> [String] -> [ModuleName] -> [Component]
mkComp CompType
Lib (Library -> Text
libName' Library
l) (Library -> BuildInfo
libBuildInfo Library
l) [] (Library -> [ModuleName]
explicitLibModules Library
l) | Library
l <- forall a. Maybe a -> [a]
maybeToList Maybe Library
library forall a. [a] -> [a] -> [a]
++ [Library]
subLibraries ] forall a. [a] -> [a] -> [a]
++
    [CompType
-> Text -> BuildInfo -> [String] -> [ModuleName] -> [Component]
mkComp CompType
Lib (UnqualComponentName -> Text
unqName forall a b. (a -> b) -> a -> b
$ ForeignLib -> UnqualComponentName
foreignLibName ForeignLib
f) (ForeignLib -> BuildInfo
foreignLibBuildInfo ForeignLib
f) [] (ForeignLib -> [ModuleName]
foreignLibModules ForeignLib
f) | ForeignLib
f <- [ForeignLib]
foreignLibs]

  mkComp :: CompType -> T.Text -> BuildInfo -> [FilePath] -> [ModuleName] -> [Component]
  mkComp :: CompType
-> Text -> BuildInfo -> [String] -> [ModuleName] -> [Component]
mkComp CompType
typ Text
name BuildInfo
bi [String]
fps [ModuleName]
mods =
    [CompType -> Text -> Text -> Component
Comp CompType
typ Text
name (String -> Text
T.pack String
fp)
    | String
fp0 <- [String]
fps forall a. Semigroup a => a -> a -> a
<> forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ModuleName -> [String]
toFilePath' [ModuleName]
mods
    , String
srcDir <- forall a b. (a -> b) -> [a] -> [b]
map forall from to. SymbolicPath from to -> String
getSymbolicPath forall a b. (a -> b) -> a -> b
$ BuildInfo -> [SymbolicPath PackageDir SourceDir]
hsSourceDirs BuildInfo
bi
    , let fp :: String
fp = String
srcDir String -> ShowS
</> String
fp0
    , forall a. IO a -> a
unsafePerformIO forall a b. (a -> b) -> a -> b
$ String -> IO Bool
doesFileExist String
fp
    ]

  unqName :: UnqualComponentName -> Text
unqName = String -> Text
T.pack forall b c a. (b -> c) -> (a -> b) -> a -> c
. UnqualComponentName -> String
unUnqualComponentName
  libName' :: Library -> Text
libName' Library
x = case Library -> LibraryName
libName Library
x of
    LibraryName
LMainLibName  -> Text
""
    LSubLibName UnqualComponentName
u -> UnqualComponentName -> Text
unqName UnqualComponentName
u

benchmarkExePath :: Benchmark -> [FilePath]
benchmarkExePath :: Benchmark -> [String]
benchmarkExePath Benchmark
b = case Benchmark -> BenchmarkInterface
benchmarkInterface Benchmark
b of
  BenchmarkExeV10 Version
_ String
f -> [String
f]
  BenchmarkInterface
_                   -> []

toFilePath' :: ModuleName -> [FilePath]
toFilePath' :: ModuleName -> [String]
toFilePath' ModuleName
mod = [ ModuleName -> String
toFilePath ModuleName
mod String -> ShowS
<.> String
ext | String
ext <- [String
"hs", String
"lhs"]]

testExePath :: TestSuite -> [FilePath]
testExePath :: TestSuite -> [String]
testExePath TestSuite
t = case TestSuite -> TestSuiteInterface
testInterface TestSuite
t of
    TestSuiteExeV10 Version
_ String
fp -> [String
fp]
    TestSuiteInterface
_                    -> []