packages feed

strong-path-1.0.0.0: src/StrongPath/TH.hs

{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_HADDOCK hide #-}

module StrongPath.TH
  ( -- ** QuasiQuoters
    -- $quasiQuoters
    absdir,
    absdirP,
    absdirW,
    absfile,
    absfileP,
    absfileW,
    reldir,
    reldirP,
    reldirW,
    relfile,
    relfileP,
    relfileW,
  )
where

import Control.Monad ((>=>))
import qualified Language.Haskell.TH.Lib as TH
import Language.Haskell.TH.Quote (QuasiQuoter (..))
import Language.Haskell.TH.Syntax (Lift (..))
import qualified Language.Haskell.TH.Syntax as TH
import StrongPath.FilePath
import StrongPath.Internal

-- $quasiQuoters
-- StrongPath provides quasi quoters that enable you to construct 'Path' in compile time.
-- You will need to enable 'QuasiQuotes' language extension in order to use them.
-- With quasi quoters, you can define paths like this:
--
-- > dirFooAbsPath :: Path System Abs (Dir FooDir)
-- > dirFooAbsPath = [absdir|/foo/bar|]
--
-- > someFile :: Path Posix (Rel FooDir) File ()
-- > someFile = [relfileP|some/file.txt|]
--
-- These will run at compile-time and underneath use the appropriate parser, ensuring that paths are valid and throwing compile-time error if not.

-- TODO: Split these into a separate module, StrongPath.QuasiQuoters, that will be reexported from this module.
--   This will also need extraction of some other parts of this module, in order to avoid cyclic imports.

qq ::
  (Lift p, Show err) =>
  (String -> Either err p) ->
  (p -> TH.ExpQ) ->
  QuasiQuoter
qq parse liftP =
  QuasiQuoter
    { quoteExp = either (fail . show) liftP . parse,
      quotePat = err "pattern",
      quoteType = err "type",
      quoteDec = err "declaration"
    }
  where
    err what x = fail ("unexpected " ++ what ++ ", must be expression: " ++ x)

liftPath :: TH.TypeQ -> TH.TypeQ -> TH.TypeQ -> Path s b t -> TH.ExpQ
liftPath s b t p = [|$(lift p) :: Path $s $b $t|]

typeVar :: String -> TH.TypeQ
typeVar = TH.newName >=> TH.varT

absdir, absdirP, absdirW :: QuasiQuoter
absdir = qq parseAbsDir (liftPath [t|System|] [t|Abs|] [t|Dir $(typeVar "d")|])
absdirP = qq parseAbsDirP (liftPath [t|Posix|] [t|Abs|] [t|Dir $(typeVar "d")|])
absdirW = qq parseAbsDirW (liftPath [t|Windows|] [t|Abs|] [t|Dir $(typeVar "d")|])

absfile, absfileP, absfileW :: QuasiQuoter
absfile = qq parseAbsFile (liftPath [t|System|] [t|Abs|] [t|File $(typeVar "f")|])
absfileP = qq parseAbsFileP (liftPath [t|Posix|] [t|Abs|] [t|File $(typeVar "f")|])
absfileW = qq parseAbsFileW (liftPath [t|Windows|] [t|Abs|] [t|File $(typeVar "f")|])

reldir, reldirP, reldirW :: QuasiQuoter
reldir = qq parseRelDir (liftPath [t|System|] [t|Rel $(typeVar "d1")|] [t|Dir $(typeVar "d2")|])
reldirP = qq parseRelDirP (liftPath [t|Posix|] [t|Rel $(typeVar "d1")|] [t|Dir $(typeVar "d2")|])
reldirW = qq parseRelDirW (liftPath [t|Windows|] [t|Rel $(typeVar "d1")|] [t|Dir $(typeVar "d2")|])

relfile, relfileP, relfileW :: QuasiQuoter
relfile = qq parseRelFile (liftPath [t|System|] [t|Rel $(typeVar "d")|] [t|File $(typeVar "f")|])
relfileP = qq parseRelFileP (liftPath [t|Posix|] [t|Rel $(typeVar "d")|] [t|File $(typeVar "f")|])
relfileW = qq parseRelFileW (liftPath [t|Windows|] [t|Rel $(typeVar "d")|] [t|File $(typeVar "f")|])