packages feed

tasty-sugar-0.2.0.0: src/internal/Test/Tasty/Sugar/AssocCheck.hs

-- | Function and implementation to find association files for an
-- identified test root file.

{-# LANGUAGE LambdaCase #-}

module Test.Tasty.Sugar.AssocCheck
  (
    getAssoc
  )
  where

import           Control.Monad.Logic
import qualified Data.List as L
import           Data.Maybe ( catMaybes )

import           Test.Tasty.Sugar.ParamCheck
import           Test.Tasty.Sugar.Types


-- | For a specific NamedParamMatch, find all associated files having
-- the rootMatch plus the named parameter values (in the same order
-- but with any combination of separators) and the specified suffix
-- match.
getAssoc :: FilePath
         -> Separators
         -> [NamedParamMatch]
         -> [ (String, FileSuffix) ]
         -> [FilePath]
         -> Logic [(String, FilePath)]
getAssoc rootPrefix seps pmatch assocNames allNames = assocSet
  where
    assocSet = catMaybes <$> mapM fndAnAssoc assocNames

    fndAnAssoc assoc = ifte (fndAssoc assoc)
                       (return . Just)
                       (return Nothing)

    fndAssoc assoc =
      do pseq <- npseq pmatch
         (assocPfx, assocSfx) <- sepParams seps (fmap snd pseq)
         if null assocSfx
           then do let assocNm = if null (snd assoc) &&
                                    length assocPfx == 1 -- just a separator
                                 then rootPrefix
                                 else rootPrefix <> assocPfx <> (snd assoc)
                   guard (assocNm `elem` allNames)
                   return (fst assoc, assocNm)
           else let assocStart = rootPrefix <> assocPfx
                    assocEnd = assocSfx <> snd assoc
                    aSL = length assocStart
                    aEL = length assocEnd
                    possible f =
                      and [ assocStart `L.isPrefixOf` f
                          , assocEnd `L.isSuffixOf` f
                          , length f > (aSL + aEL)
                          , let mid = drop aSL (take (length f - aEL) f)
                            in and $ fmap (not . flip elem mid) seps
                          ]
                    fnd = filter possible allNames
                in do f <- eachFrom fnd
                      return (fst assoc, f)

    sepParams :: Separators -> [ParamMatch] -> Logic (String, String)
    sepParams sl = \case
      [] -> if null sl
            then return ([], [])
            else do s <- eachFrom sl
                    return ([s], [])
      (NotSpecified:ps) -> do r <- sepParams sl ps
                              return ([], fst r)
      ((Explicit v):ps) -> do (l,r) <- sepParams sl ps
                              if null sl
                                then return (v <> l, r)
                                else do s <- eachFrom sl
                                        return ([s] <> v <> l, r)
      ((Assumed  v):ps) -> do (l,r) <- sepParams sl ps
                              if null sl
                                then return (v <> l, r)
                                else do s <- eachFrom sl
                                        return ([s] <> v <> l, r)

    npseq = eachFrom
            . ([]:)                -- consider no parameters just once
            . filter (not . null)  -- excluding multiple blanks in
            . concatMap L.inits    -- any number of the
            . L.permutations       -- parameters in each possible order