-- | This module exports functions for parsing and executing CSS selector
-- expressions in pure Haskell. TH QuasiQuoters are provided in
-- "XML.Selectors.CSS.TH" for validation and static-checking of CSS selectors.
{-# LANGUAGE OverloadedStrings #-}
module XML.Selectors.CSS (
toAxis,
parsePath
) where
import XML.Selectors.CSS.Parse
import XML.Selectors.CSS.Types
import Text.XML
import Text.XML.Cursor
import Data.List
import Data.String
import qualified Data.Map as M
import qualified Data.Text as T
-- Axes that match nodes and their ancestors could result in duplicate nodes
-- in following descendant axes
-- | Convert CSS 'Selector' to an 'Axis'.
toAxis :: Selector -> Axis
toAxis selector = descendant >=> toAxis' selector
mhead :: Monad m => [a] -> m a
mhead [] = fail "empty"
mhead (a:_) = return a
toAxis' (Selector selector) = simpleAxis selector
toAxis' (Combinator simple comb selector) = axis where
axis = simpleAxis simple >=> combaxis >=> toAxis' selector
combaxis = case comb of
Descendant -> descendant
Child -> child
AnySibling -> followingSibling
FollowingSibling -> mhead . followingSibling
simpleAxis (SimpleSelector mbelem specs mbpseudo) = axis where
axis = elemaxis >=> specaxis >=> pseuaxis
elemaxis = case mbelem of
Nothing -> anyElement
Just nm -> element (fromString nm)
pseuaxis = case mbpseudo of
Nothing -> return
Just FirstChild -> mhead . child
Just LastChild -> return . last . child
specaxis = loop specs
loop [] = return
loop (spec:ss) = toaxis spec >=> loop ss
toaxis (ID id) = attributeIs "id" (fromString id)
toaxis (Class cls) = toaxis (Attrib "class" $ Pred Includes cls)
toaxis (Attrib attr pred) = \c -> case node c of
NodeElement (Element _ as _) | Just v <- M.lookup (fromString attr) as -> case pred of
None -> [c]
Pred op val | Equals <- op, val' == v -> [c]
| Includes <- op, val' `elem` T.words v -> [c]
| BeginsWith <- op, val' == T.take vallen v -> [c]
| EndsWith <- op, val' == T.drop (T.length v - vallen) v -> [c]
| otherwise -> [] where
val' = fromString val
vallen = T.length val'
_ -> []