{-# LANGUAGE CPP #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Test.Syd.Discover where

import Control.Monad.IO.Class
import Data.List
import Data.Maybe
import Options.Applicative
import Path
import Path.IO
import qualified System.FilePath as FP

sydTestDiscover :: IO ()
sydTestDiscover :: IO ()
sydTestDiscover = do
  Arguments {FilePath
Settings
argSettings :: Arguments -> Settings
argDestination :: Arguments -> FilePath
argIgnored :: Arguments -> FilePath
argSource :: Arguments -> FilePath
argSettings :: Settings
argDestination :: FilePath
argIgnored :: FilePath
argSource :: FilePath
..} <- IO Arguments
getArguments
  Path Abs File
specSourceFile <- FilePath -> IO (Path Abs File)
forall (m :: * -> *). MonadIO m => FilePath -> m (Path Abs File)
resolveFile' FilePath
argSource
  -- We asume that the spec source is a top-level module.
  let testDir :: Path Abs Dir
testDir = Path Abs File -> Path Abs Dir
forall b t. Path b t -> Path b Dir
parent Path Abs File
specSourceFile
  Path Rel File
specSourceFileRel <- Path Abs Dir -> Path Abs File -> IO (Path Rel File)
forall (m :: * -> *) b t.
MonadThrow m =>
Path b Dir -> Path b t -> m (Path Rel t)
stripProperPrefix Path Abs Dir
testDir Path Abs File
specSourceFile
  [SpecModule]
otherSpecFiles <- (Path Rel File -> Maybe SpecModule)
-> [Path Rel File] -> [SpecModule]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Path Rel File -> Maybe SpecModule
parseSpecModule ([Path Rel File] -> [SpecModule])
-> ([Path Rel File] -> [Path Rel File])
-> [Path Rel File]
-> [SpecModule]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Path Rel File] -> [Path Rel File]
forall a. Ord a => [a] -> [a]
sort ([Path Rel File] -> [Path Rel File])
-> ([Path Rel File] -> [Path Rel File])
-> [Path Rel File]
-> [Path Rel File]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Path Rel File -> Bool) -> [Path Rel File] -> [Path Rel File]
forall a. (a -> Bool) -> [a] -> [a]
filter (\Path Rel File
fp -> Path Rel File
fp Path Rel File -> Path Rel File -> Bool
forall a. Eq a => a -> a -> Bool
/= Path Rel File
specSourceFileRel Bool -> Bool -> Bool
&& Path Rel File -> Bool
isHaskellFile Path Rel File
fp) ([Path Rel File] -> [SpecModule])
-> IO [Path Rel File] -> IO [SpecModule]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Path Abs Dir -> IO [Path Rel File]
forall (m :: * -> *).
MonadIO m =>
Path Abs Dir -> m [Path Rel File]
sourceFilesInNonHiddenDirsRecursively Path Abs Dir
testDir
  let output :: FilePath
output = Settings -> Path Rel File -> [SpecModule] -> FilePath
makeSpecModule Settings
argSettings Path Rel File
specSourceFileRel [SpecModule]
otherSpecFiles
  FilePath -> FilePath -> IO ()
writeFile FilePath
argDestination FilePath
output

data Arguments = Arguments
  { Arguments -> FilePath
argSource :: FilePath,
    Arguments -> FilePath
argIgnored :: FilePath,
    Arguments -> FilePath
argDestination :: FilePath,
    Arguments -> Settings
argSettings :: Settings
  }
  deriving (Int -> Arguments -> ShowS
[Arguments] -> ShowS
Arguments -> FilePath
(Int -> Arguments -> ShowS)
-> (Arguments -> FilePath)
-> ([Arguments] -> ShowS)
-> Show Arguments
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
showList :: [Arguments] -> ShowS
$cshowList :: [Arguments] -> ShowS
show :: Arguments -> FilePath
$cshow :: Arguments -> FilePath
showsPrec :: Int -> Arguments -> ShowS
$cshowsPrec :: Int -> Arguments -> ShowS
Show, Arguments -> Arguments -> Bool
(Arguments -> Arguments -> Bool)
-> (Arguments -> Arguments -> Bool) -> Eq Arguments
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Arguments -> Arguments -> Bool
$c/= :: Arguments -> Arguments -> Bool
== :: Arguments -> Arguments -> Bool
$c== :: Arguments -> Arguments -> Bool
Eq)

data Settings = Settings
  { Settings -> Bool
settingMain :: Bool
  }
  deriving (Int -> Settings -> ShowS
[Settings] -> ShowS
Settings -> FilePath
(Int -> Settings -> ShowS)
-> (Settings -> FilePath) -> ([Settings] -> ShowS) -> Show Settings
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
showList :: [Settings] -> ShowS
$cshowList :: [Settings] -> ShowS
show :: Settings -> FilePath
$cshow :: Settings -> FilePath
showsPrec :: Int -> Settings -> ShowS
$cshowsPrec :: Int -> Settings -> ShowS
Show, Settings -> Settings -> Bool
(Settings -> Settings -> Bool)
-> (Settings -> Settings -> Bool) -> Eq Settings
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Settings -> Settings -> Bool
$c/= :: Settings -> Settings -> Bool
== :: Settings -> Settings -> Bool
$c== :: Settings -> Settings -> Bool
Eq)

getArguments :: IO Arguments
getArguments :: IO Arguments
getArguments = ParserInfo Arguments -> IO Arguments
forall a. ParserInfo a -> IO a
execParser (ParserInfo Arguments -> IO Arguments)
-> ParserInfo Arguments -> IO Arguments
forall a b. (a -> b) -> a -> b
$ Parser Arguments -> InfoMod Arguments -> ParserInfo Arguments
forall a. Parser a -> InfoMod a -> ParserInfo a
info Parser Arguments
argumentsParser InfoMod Arguments
forall a. InfoMod a
fullDesc

argumentsParser :: Parser Arguments
argumentsParser :: Parser Arguments
argumentsParser =
  FilePath -> FilePath -> FilePath -> Settings -> Arguments
Arguments
    (FilePath -> FilePath -> FilePath -> Settings -> Arguments)
-> Parser FilePath
-> Parser (FilePath -> FilePath -> Settings -> Arguments)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Mod ArgumentFields FilePath -> Parser FilePath
forall s. IsString s => Mod ArgumentFields s -> Parser s
strArgument ([Mod ArgumentFields FilePath] -> Mod ArgumentFields FilePath
forall a. Monoid a => [a] -> a
mconcat [FilePath -> Mod ArgumentFields FilePath
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Source file path"])
    Parser (FilePath -> FilePath -> Settings -> Arguments)
-> Parser FilePath -> Parser (FilePath -> Settings -> Arguments)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Mod ArgumentFields FilePath -> Parser FilePath
forall s. IsString s => Mod ArgumentFields s -> Parser s
strArgument ([Mod ArgumentFields FilePath] -> Mod ArgumentFields FilePath
forall a. Monoid a => [a] -> a
mconcat [FilePath -> Mod ArgumentFields FilePath
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Ignored argument"])
    Parser (FilePath -> Settings -> Arguments)
-> Parser FilePath -> Parser (Settings -> Arguments)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Mod ArgumentFields FilePath -> Parser FilePath
forall s. IsString s => Mod ArgumentFields s -> Parser s
strArgument ([Mod ArgumentFields FilePath] -> Mod ArgumentFields FilePath
forall a. Monoid a => [a] -> a
mconcat [FilePath -> Mod ArgumentFields FilePath
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Destiantion file path"])
    Parser (Settings -> Arguments)
-> Parser Settings -> Parser Arguments
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> ( Bool -> Settings
Settings
            (Bool -> Settings) -> Parser Bool -> Parser Settings
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ( Bool -> Mod FlagFields Bool -> Parser Bool
forall a. a -> Mod FlagFields a -> Parser a
flag' Bool
True ([Mod FlagFields Bool] -> Mod FlagFields Bool
forall a. Monoid a => [a] -> a
mconcat [FilePath -> Mod FlagFields Bool
forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"main", FilePath -> Mod FlagFields Bool
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"generate a main module and function"])
                    Parser Bool -> Parser Bool -> Parser Bool
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Bool -> Mod FlagFields Bool -> Parser Bool
forall a. a -> Mod FlagFields a -> Parser a
flag' Bool
False ([Mod FlagFields Bool] -> Mod FlagFields Bool
forall a. Monoid a => [a] -> a
mconcat [FilePath -> Mod FlagFields Bool
forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"no-main", FilePath -> Mod FlagFields Bool
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"don't generate a main module and function"])
                    Parser Bool -> Parser Bool -> Parser Bool
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Bool -> Parser Bool
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
True
                )
        )

sourceFilesInNonHiddenDirsRecursively ::
  forall m.
  MonadIO m =>
  Path Abs Dir ->
  m [Path Rel File]
sourceFilesInNonHiddenDirsRecursively :: Path Abs Dir -> m [Path Rel File]
sourceFilesInNonHiddenDirsRecursively =
  Maybe
  (Path Rel Dir
   -> [Path Rel Dir] -> [Path Rel File] -> m (WalkAction Rel))
-> (Path Rel Dir
    -> [Path Rel Dir] -> [Path Rel File] -> m [Path Rel File])
-> Path Abs Dir
-> m [Path Rel File]
forall (m :: * -> *) o b.
(MonadIO m, Monoid o) =>
Maybe
  (Path Rel Dir
   -> [Path Rel Dir] -> [Path Rel File] -> m (WalkAction Rel))
-> (Path Rel Dir -> [Path Rel Dir] -> [Path Rel File] -> m o)
-> Path b Dir
-> m o
walkDirAccumRel ((Path Rel Dir
 -> [Path Rel Dir] -> [Path Rel File] -> m (WalkAction Rel))
-> Maybe
     (Path Rel Dir
      -> [Path Rel Dir] -> [Path Rel File] -> m (WalkAction Rel))
forall a. a -> Maybe a
Just Path Rel Dir
-> [Path Rel Dir] -> [Path Rel File] -> m (WalkAction Rel)
goWalk) Path Rel Dir
-> [Path Rel Dir] -> [Path Rel File] -> m [Path Rel File]
goOutput
  where
    goWalk ::
      Path Rel Dir -> [Path Rel Dir] -> [Path Rel File] -> m (WalkAction Rel)
    goWalk :: Path Rel Dir
-> [Path Rel Dir] -> [Path Rel File] -> m (WalkAction Rel)
goWalk Path Rel Dir
curdir [Path Rel Dir]
subdirs [Path Rel File]
_ = do
      WalkAction Rel -> m (WalkAction Rel)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (WalkAction Rel -> m (WalkAction Rel))
-> WalkAction Rel -> m (WalkAction Rel)
forall a b. (a -> b) -> a -> b
$ [Path Rel Dir] -> WalkAction Rel
forall b. [Path b Dir] -> WalkAction b
WalkExclude ([Path Rel Dir] -> WalkAction Rel)
-> [Path Rel Dir] -> WalkAction Rel
forall a b. (a -> b) -> a -> b
$ (Path Rel Dir -> Bool) -> [Path Rel Dir] -> [Path Rel Dir]
forall a. (a -> Bool) -> [a] -> [a]
filter (Path Rel Dir -> Path Rel Dir -> Bool
forall b t. Path b Dir -> Path b t -> Bool
isHiddenIn Path Rel Dir
curdir) [Path Rel Dir]
subdirs
    goOutput ::
      Path Rel Dir -> [Path Rel Dir] -> [Path Rel File] -> m [Path Rel File]
    goOutput :: Path Rel Dir
-> [Path Rel Dir] -> [Path Rel File] -> m [Path Rel File]
goOutput Path Rel Dir
curdir [Path Rel Dir]
_ [Path Rel File]
files =
      [Path Rel File] -> m [Path Rel File]
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Path Rel File] -> m [Path Rel File])
-> [Path Rel File] -> m [Path Rel File]
forall a b. (a -> b) -> a -> b
$ (Path Rel File -> Path Rel File)
-> [Path Rel File] -> [Path Rel File]
forall a b. (a -> b) -> [a] -> [b]
map (Path Rel Dir
curdir Path Rel Dir -> Path Rel File -> Path Rel File
forall b t. Path b Dir -> Path Rel t -> Path b t
</>) ([Path Rel File] -> [Path Rel File])
-> [Path Rel File] -> [Path Rel File]
forall a b. (a -> b) -> a -> b
$ (Path Rel File -> Bool) -> [Path Rel File] -> [Path Rel File]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (Path Rel File -> Bool) -> Path Rel File -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Path Rel File -> Bool
hiddenFile) [Path Rel File]
files

hiddenFile :: Path Rel File -> Bool
hiddenFile :: Path Rel File -> Bool
hiddenFile = Path Rel File -> Bool
goFile
  where
    goFile :: Path Rel File -> Bool
    goFile :: Path Rel File -> Bool
goFile Path Rel File
f = Path Rel Dir -> Path Rel File -> Bool
forall b t. Path b Dir -> Path b t -> Bool
isHiddenIn (Path Rel File -> Path Rel Dir
forall b t. Path b t -> Path b Dir
parent Path Rel File
f) Path Rel File
f Bool -> Bool -> Bool
|| Path Rel Dir -> Bool
goDir (Path Rel File -> Path Rel Dir
forall b t. Path b t -> Path b Dir
parent Path Rel File
f)
    goDir :: Path Rel Dir -> Bool
    goDir :: Path Rel Dir -> Bool
goDir Path Rel Dir
f
      | Path Rel Dir -> Path Rel Dir
forall b t. Path b t -> Path b Dir
parent Path Rel Dir
f Path Rel Dir -> Path Rel Dir -> Bool
forall a. Eq a => a -> a -> Bool
== Path Rel Dir
f = Bool
False
      | Bool
otherwise = Path Rel Dir -> Path Rel Dir -> Bool
forall b t. Path b Dir -> Path b t -> Bool
isHiddenIn (Path Rel Dir -> Path Rel Dir
forall b t. Path b t -> Path b Dir
parent Path Rel Dir
f) Path Rel Dir
f Bool -> Bool -> Bool
|| Path Rel Dir -> Bool
goDir (Path Rel Dir -> Path Rel Dir
forall b t. Path b t -> Path b Dir
parent Path Rel Dir
f)

isHiddenIn :: Path b Dir -> Path b t -> Bool
isHiddenIn :: Path b Dir -> Path b t -> Bool
isHiddenIn Path b Dir
curdir Path b t
ad =
  case Path b Dir -> Path b t -> Maybe (Path Rel t)
forall (m :: * -> *) b t.
MonadThrow m =>
Path b Dir -> Path b t -> m (Path Rel t)
stripProperPrefix Path b Dir
curdir Path b t
ad of
    Maybe (Path Rel t)
Nothing -> Bool
False
    Just Path Rel t
rp -> FilePath
"." FilePath -> FilePath -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` Path Rel t -> FilePath
forall b t. Path b t -> FilePath
toFilePath Path Rel t
rp

#if MIN_VERSION_path(0,7,0)
isHaskellFile :: Path Rel File -> Bool
isHaskellFile :: Path Rel File -> Bool
isHaskellFile Path Rel File
p =
  case Path Rel File -> Maybe FilePath
forall (m :: * -> *) b. MonadThrow m => Path b File -> m FilePath
fileExtension Path Rel File
p of
    Just FilePath
".hs" -> Bool
True
    Just FilePath
".lhs" -> Bool
True
    Maybe FilePath
_ -> Bool
False
#else
isHaskellFile :: Path Rel File -> Bool
isHaskellFile p =
  case fileExtension p of
    ".hs" -> True
    ".lhs" -> True
    _ -> False
#endif

data SpecModule = SpecModule
  { SpecModule -> Path Rel File
specModulePath :: Path Rel File,
    SpecModule -> FilePath
specModuleModuleName :: String,
    SpecModule -> FilePath
specModuleDescription :: String
  }

parseSpecModule :: Path Rel File -> Maybe SpecModule
parseSpecModule :: Path Rel File -> Maybe SpecModule
parseSpecModule Path Rel File
rf = do
  let specModulePath :: Path Rel File
specModulePath = Path Rel File
rf
  let specModuleModuleName :: FilePath
specModuleModuleName = Path Rel File -> FilePath
makeModuleName Path Rel File
rf
  let withoutExtension :: FilePath
withoutExtension = ShowS
FP.dropExtension ShowS -> ShowS
forall a b. (a -> b) -> a -> b
$ Path Rel File -> FilePath
fromRelFile Path Rel File
rf
  FilePath
withoutSpecSuffix <- FilePath -> FilePath -> Maybe FilePath
forall a. Eq a => [a] -> [a] -> Maybe [a]
stripSuffix FilePath
"Spec" FilePath
withoutExtension
  Path Rel File
withoutSpecSuffixPath <- FilePath -> Maybe (Path Rel File)
forall (m :: * -> *). MonadThrow m => FilePath -> m (Path Rel File)
parseRelFile FilePath
withoutSpecSuffix
  let specModuleDescription :: FilePath
specModuleDescription = Path Rel File -> FilePath
makeModuleName Path Rel File
withoutSpecSuffixPath
  SpecModule -> Maybe SpecModule
forall (f :: * -> *) a. Applicative f => a -> f a
pure SpecModule :: Path Rel File -> FilePath -> FilePath -> SpecModule
SpecModule {FilePath
Path Rel File
specModuleDescription :: FilePath
specModuleModuleName :: FilePath
specModulePath :: Path Rel File
specModuleDescription :: FilePath
specModuleModuleName :: FilePath
specModulePath :: Path Rel File
..}
  where
    stripSuffix :: Eq a => [a] -> [a] -> Maybe [a]
    stripSuffix :: [a] -> [a] -> Maybe [a]
stripSuffix [a]
suffix [a]
s = [a] -> [a]
forall a. [a] -> [a]
reverse ([a] -> [a]) -> Maybe [a] -> Maybe [a]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [a] -> [a] -> Maybe [a]
forall a. Eq a => [a] -> [a] -> Maybe [a]
stripPrefix ([a] -> [a]
forall a. [a] -> [a]
reverse [a]
suffix) ([a] -> [a]
forall a. [a] -> [a]
reverse [a]
s)

makeModuleName :: Path Rel File -> String
makeModuleName :: Path Rel File -> FilePath
makeModuleName Path Rel File
fp =
  FilePath -> [FilePath] -> FilePath
forall a. [a] -> [[a]] -> [a]
intercalate FilePath
"." ([FilePath] -> FilePath) -> [FilePath] -> FilePath
forall a b. (a -> b) -> a -> b
$ FilePath -> [FilePath]
FP.splitDirectories (FilePath -> [FilePath]) -> FilePath -> [FilePath]
forall a b. (a -> b) -> a -> b
$ ShowS
FP.dropExtensions ShowS -> ShowS
forall a b. (a -> b) -> a -> b
$ Path Rel File -> FilePath
fromRelFile Path Rel File
fp

makeSpecModule :: Settings -> Path Rel File -> [SpecModule] -> String
makeSpecModule :: Settings -> Path Rel File -> [SpecModule] -> FilePath
makeSpecModule Settings {Bool
settingMain :: Bool
settingMain :: Settings -> Bool
..} Path Rel File
destination [SpecModule]
sources =
  [FilePath] -> FilePath
unlines
    [ FilePath
"{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-unused-imports #-}",
      if Bool
settingMain then FilePath
"" else ShowS
moduleDeclaration (Path Rel File -> FilePath
makeModuleName Path Rel File
destination),
      FilePath
"",
      FilePath
"import Test.Syd",
      FilePath
"import qualified Prelude",
      FilePath
"",
      [SpecModule] -> FilePath
importDeclarations [SpecModule]
sources,
      if Bool
settingMain then FilePath
mainDeclaration else FilePath
"",
      [SpecModule] -> FilePath
specDeclaration [SpecModule]
sources
    ]

moduleDeclaration :: String -> String
moduleDeclaration :: ShowS
moduleDeclaration FilePath
mn = [FilePath] -> FilePath
unwords [FilePath
"module", FilePath
mn, FilePath
"(spec) where"]

mainDeclaration :: String
mainDeclaration :: FilePath
mainDeclaration =
  [FilePath] -> FilePath
unlines
    [ FilePath
"main :: Prelude.IO ()",
      FilePath
"main = sydTest spec"
    ]

importDeclarations :: [SpecModule] -> String
importDeclarations :: [SpecModule] -> FilePath
importDeclarations = [FilePath] -> FilePath
unlines ([FilePath] -> FilePath)
-> ([SpecModule] -> [FilePath]) -> [SpecModule] -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SpecModule -> FilePath) -> [SpecModule] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map ((FilePath
"import qualified " FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<>) ShowS -> (SpecModule -> FilePath) -> SpecModule -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SpecModule -> FilePath
specModuleModuleName)

specDeclaration :: [SpecModule] -> String
specDeclaration :: [SpecModule] -> FilePath
specDeclaration [SpecModule]
fs =
  [FilePath] -> FilePath
unlines ([FilePath] -> FilePath) -> [FilePath] -> FilePath
forall a b. (a -> b) -> a -> b
$
    if [SpecModule] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [SpecModule]
fs
      then [FilePath
"spec = Prelude.pure ()"]
      else
        FilePath
"spec = do" FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
:
        (SpecModule -> FilePath) -> [SpecModule] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map SpecModule -> FilePath
moduleSpecLine [SpecModule]
fs

moduleSpecLine :: SpecModule -> String
moduleSpecLine :: SpecModule -> FilePath
moduleSpecLine SpecModule
rf = [FilePath] -> FilePath
unwords [FilePath
" ", FilePath
"describe", FilePath
"\"" FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> SpecModule -> FilePath
specModuleModuleName SpecModule
rf FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> FilePath
"\"", SpecModule -> FilePath
specFunctionName SpecModule
rf]

specFunctionName :: SpecModule -> String
specFunctionName :: SpecModule -> FilePath
specFunctionName SpecModule
rf = SpecModule -> FilePath
specModuleModuleName SpecModule
rf FilePath -> ShowS
forall a. [a] -> [a] -> [a]
++ FilePath
".spec"