module Control.Monad.Apiary.Filter.Internal.Capture.TH(capture) where
import Control.Arrow(first)
import qualified Language.Haskell.TH as TH
import Language.Haskell.TH.Quote(QuasiQuoter(..))
import qualified Control.Monad.Apiary.Filter.Capture as Capture
import qualified Data.Text as T
import Data.String(IsString(..))
import Data.List(isInfixOf)
import Data.Apiary.SProxy(SProxy(..))
import Data.Proxy(Proxy(..))
preCap :: String -> [String]
preCap "" = []
preCap "/" = []
preCap ('/':p) = splitPath p
preCap p = splitPath p
splitPath :: String -> [String]
splitPath = map T.unpack . T.splitOn "/" . T.pack
description :: String -> TH.Q (String, TH.ExpQ)
description s = case break (`elem` ("([" :: String)) s of
(t, []) -> return (t, [|Nothing|])
(t, st) -> case break (`elem` (")]" :: String)) st of
(_:'$':b, ")") -> do
TH.reportWarning "DEPRECATED () description. use []."
v <- TH.lookupValueName b
maybe (fail $ b ++ " not found.") (\n -> return (t, [|Just $(TH.varE n)|])) v
(_:b, ")") -> do
TH.reportWarning "DEPRECATED () description. use []."
return (t, [|Just $(TH.stringE b)|])
(_:'$':b, "]") -> TH.lookupValueName b >>=
maybe (fail $ b ++ " not found.") (\n -> return (t, [|Just $(TH.varE n)|]))
(_:b, "]") -> return (t, [|Just $(TH.stringE b)|])
(_, _) -> fail "capture: syntax error."
mkCap :: [String] -> TH.ExpQ
mkCap [] = [|id|]
mkCap (('*':'*':[]):as) = [|Capture.anyPath . $(mkCap as) |]
mkCap (('*':'*':tS):as) = do
(k, d) <- description tS
[|Capture.restPath (SProxy :: SProxy $(TH.litT $ TH.strTyLit k)) $d . $(mkCap as) |]
mkCap (str:as)
| "::" `isInfixOf` fst (break (`elem` ("([" :: String)) str) = do
(key, d) <- first T.pack `fmap` description str
let v = T.unpack . T.strip . fst $ T.breakOn "::" key
t = T.unpack . T.strip . snd $ T.breakOnEnd "::" key
ty <- TH.lookupTypeName t >>= maybe (fail $ t ++ " not found.") return
[|(Capture.fetch' (SProxy :: SProxy $(TH.litT $ TH.strTyLit v)) (Proxy :: Proxy $(TH.conT ty)) $d) . $(mkCap as)|]
| otherwise = [|(Capture.path (fromString $(TH.stringE str))) . $(mkCap as) |]
capture :: QuasiQuoter
capture = QuasiQuoter
{ quoteExp = mkCap . preCap
, quotePat = \_ -> error "No quotePat."
, quoteType = \_ -> error "No quoteType."
, quoteDec = \_ -> error "No quoteDec."
}