{-# LANGUAGE
OverloadedStrings
, FlexibleInstances
, GeneralizedNewtypeDeriving
, StandaloneDeriving
, UndecidableInstances
, ViewPatterns
, PatternGuards
, CPP
#-}
module Clay.Selector where
import Control.Applicative
import Data.Semigroup
import Data.String
import Data.Text (Text)
import qualified Data.Text as Text
star :: Selector
star = In (SelectorF (Refinement []) Star)
element :: Text -> Selector
element e = In (SelectorF (Refinement []) (Elem e))
deep :: Selector -> Selector -> Selector
deep a b = In (SelectorF (Refinement []) (Deep a b))
(**) :: Selector -> Selector -> Selector
(**) = deep
child :: Selector -> Selector -> Selector
child a b = In (SelectorF (Refinement []) (Child a b))
(|>) :: Selector -> Selector -> Selector
(|>) = child
(|+) :: Selector -> Selector -> Selector
(|+) a b = In (SelectorF (Refinement []) (Adjacent a b))
with :: Selector -> Refinement -> Selector
with (In (SelectorF (Refinement fs) e)) (Refinement ps) = In (SelectorF (Refinement (fs ++ ps)) e)
(#) :: Selector -> Refinement -> Selector
(#) = with
byId :: Text -> Refinement
byId = Refinement . pure . Id
byClass :: Text -> Refinement
byClass = Refinement . pure . Class
pseudo :: Text -> Refinement
pseudo = Refinement . pure . Pseudo
func :: Text -> [Text] -> Refinement
func f = Refinement . pure . PseudoFunc f
attr :: Text -> Refinement
attr = Refinement . pure . Attr
(@=) :: Text -> Text -> Refinement
(@=) a = Refinement . pure . AttrVal a
(^=) :: Text -> Text -> Refinement
(^=) a = Refinement . pure . AttrBegins a
($=) :: Text -> Text -> Refinement
($=) a = Refinement . pure . AttrEnds a
(*=) :: Text -> Text -> Refinement
(*=) a = Refinement . pure . AttrContains a
(~=) :: Text -> Text -> Refinement
(~=) a = Refinement . pure . AttrSpace a
(|=) :: Text -> Text -> Refinement
(|=) a = Refinement . pure . AttrHyph a
data Predicate
= Id Text
| Class Text
| Attr Text
| AttrVal Text Text
| AttrBegins Text Text
| AttrEnds Text Text
| AttrContains Text Text
| AttrSpace Text Text
| AttrHyph Text Text
| Pseudo Text
| PseudoFunc Text [Text]
| PseudoElem Text
deriving (Eq, Ord, Show)
newtype Refinement = Refinement { unFilter :: [Predicate] }
deriving (Show, Semigroup, Monoid)
instance IsString Refinement where
fromString = refinementFromText . fromString
refinementFromText :: Text -> Refinement
refinementFromText t = Refinement $
case Text.uncons t of
Just ('#', s) -> [Id s]
Just ('.', s) -> [Class s]
Just (':', s)
| Just (':',s') <- Text.uncons s
-> [PseudoElem s']
| otherwise -> [Pseudo s]
Just ('@', s) -> [Attr s]
_ -> [Attr t]
data Path f
= Star
| Elem Text
| Child f f
| Deep f f
| Adjacent f f
| Combined f f
deriving Show
newtype Fix f = In { out :: f (Fix f) }
deriving instance Show (f (Fix f)) => Show (Fix f)
data SelectorF a = SelectorF Refinement (Path a)
deriving Show
type Selector = Fix SelectorF
instance IsString (Fix SelectorF) where
fromString = selectorFromText . fromString
selectorFromText :: Text -> Selector
selectorFromText t =
case Text.uncons t of
Just (c, _) | elem c ("#.:@" :: [Char])
-> with star (refinementFromText t)
_ -> In $ SelectorF (Refinement []) (Elem t)
instance Semigroup (Fix SelectorF) where
a <> b = In (SelectorF (Refinement []) (Combined a b))
instance Monoid (Fix SelectorF) where
mempty = error "Selector is a semigroup"
mappend = (<>)