css-selectors 0.3.0.0 → 0.4.0.0
raw patch · 6 files changed
+83/−25 lines, 6 filesdep +hashable
Dependencies added: hashable
Files
- CHANGELOG.md +4/−0
- README.md +10/−6
- css-selectors.cabal +3/−1
- src/Css3/Selector/Core.hs +47/−17
- src/Css3/Selector/Utils.hs +2/−0
- test/Spec.hs +17/−1
CHANGELOG.md view
@@ -2,6 +2,10 @@ For a full list of changes, see the history on [GitHub](https://github.com/hapytex/css-selectors). +## Version 0.4.0.0++All css items are now a member of the `Generic` and the `Hashable` typeclass.+ ## Version 0.3.0.0 The css items are now an instance of `Binary` and can be serialized and deserialized; and compressed. One
README.md view
@@ -129,17 +129,21 @@ is often useful, since javascript itself has no syntax for css selectors, and often strings are used to represent these. -## `Binary` instances--The css-elements are all a member of the `Binary` typeclass, that converts the-css selector to a compact binary format. This is *not* standard format. This is-more to write a css-selector to a binary format and back.- ## `Arbitrary` css selectors One can generate arbitrary CSS selectors (and their subcomponents). It is however not advisable to use this for anything other than for validation purposes (like with `QuickCheck`).++## `Binary` and `Hashable` instances++The css-elements are all members of the `Binary` and `Hashable` typeclasses,+The `Binary` typeclass converts the css selector to a compact binary format.+This is *not* standard format. This is more to write a css-selector to a+binary format and back.++css-elements are an instance of `Hashable` as well, for example to use as+keys in a `HashMap`. ## `css-selectors` is not *safe* Haskell
css-selectors.cabal view
@@ -1,5 +1,5 @@ name: css-selectors-version: 0.3.0.0+version: 0.4.0.0 synopsis: Parsing, rendering and manipulating css selectors in Haskell. description: A library for parsing, manipulating, and rendering css selectors (not css files,@@ -48,6 +48,7 @@ , bytestring >=0.9 , data-default >=0.7 && <0.8 , Decimal >=0.5.1 && <0.5.2+ , hashable >=1.2.7.0 , QuickCheck >=2.13 && <2.14 , shakespeare >=2.0 && <3.0 , template-haskell >=2.12.0 && <2.16.0@@ -66,6 +67,7 @@ base , binary >=0.2 && <0.8.8.0 , css-selectors+ , hashable >=1.2.7.0 , text , test-framework , test-framework-quickcheck2
src/Css3/Selector/Core.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE DeriveDataTypeable, OverloadedStrings, PatternSynonyms, TemplateHaskellQuotes, TypeFamilies #-}+{-# LANGUAGE DeriveDataTypeable, DeriveGeneric, OverloadedStrings, PatternSynonyms, TemplateHaskellQuotes, TypeFamilies #-} {-| Module : Css3.Selector.Core@@ -52,6 +52,7 @@ import Data.Data(Data) import Data.Default(Default(def)) import Data.Function(on)+import Data.Hashable(Hashable) import Data.List(sort, unfoldr) import Data.List.NonEmpty(NonEmpty((:|))) import qualified Data.List.NonEmpty@@ -61,6 +62,7 @@ import Data.Text(Text, cons, inits, intercalate, pack, tails, unpack) import GHC.Exts(IsList(Item, fromList, toList))+import GHC.Generics(Generic) import Language.Haskell.TH.Lib(appE, conE) import Language.Haskell.TH.Syntax(Lift(lift), Exp(AppE, ConE, LitE), Lit(StringL), Name, Pat(ConP, ListP, ViewP), Q)@@ -77,10 +79,12 @@ -- -- The specificity is calculated with @100*a+10*b+c@ where @a@, @b@ and @c@ -- count certain elements of the css selector.-data SelectorSpecificity =- SelectorSpecificity Int Int Int -- ^ Create a 'SelectorSpecificity' object with a given value for @a@, @b@, and @c@.- deriving (Data, Show)+data SelectorSpecificity+ = SelectorSpecificity Int Int Int -- ^ Create a 'SelectorSpecificity' object with a given value for @a@, @b@, and @c@.+ deriving (Data, Generic, Show) +instance Hashable SelectorSpecificity+ -- | Calculate the specificity value of the 'SelectorSpecificity' specificityValue :: SelectorSpecificity -- ^ The 'SelectorSpecificity' to calculate the specificity value from. -> Int -- ^ The specificity level of the 'SelectorSpecificity'. If the value is higher, the rules in the css selector take precedence.@@ -152,24 +156,30 @@ -- selectors. newtype SelectorGroup = SelectorGroup { unSelectorGroup :: NonEmpty Selector -- ^ Unwrap the given 'NonEmpty' list of 'Selector's from the 'SelectorGroup' object.- } deriving (Data, Eq, Ord, Show)+ } deriving (Data, Eq, Generic, Ord, Show) +instance Hashable SelectorGroup+ -- | The type of a single selector. This is a sequence of 'SelectorSequence's that -- are combined with a 'SelectorCombinator'. data Selector = Selector SelectorSequence -- ^ Convert a given 'SelectorSequence' to a 'Selector'. | Combined SelectorSequence SelectorCombinator Selector -- ^ Create a combined selector where we have a 'SelectorSequence' that is combined with a given 'SelectorCombinator' to a 'Selector'.- deriving (Data, Eq, Ord, Show)+ deriving (Data, Eq, Generic, Ord, Show) +instance Hashable Selector + -- | A type that contains the possible ways to combine 'SelectorSequence's. data SelectorCombinator = Descendant -- ^ The second tag is a descendant of the first one, denoted in css with a space. | Child -- ^ The second tag is the (direct) child of the first one, denoted with a @>@ in css. | DirectlyPreceded -- ^ The second tag is directly preceded by the first one, denoted with a @+@ in css. | Preceded -- ^ The second tag is preceded by the first one, denoted with a @~@ in css.- deriving (Bounded, Data, Enum, Eq, Ord, Read, Show)+ deriving (Bounded, Data, Enum, Eq, Generic, Ord, Read, Show) +instance Hashable SelectorCombinator+ -- | Convert the 'SelectorCombinator' to the equivalent css selector text. A -- space for 'Descendant', a @>@ for 'Child', a @+@ for 'DirectlyPreceded', and -- a @~@ for 'Preceded'@@ -213,8 +223,10 @@ data SelectorSequence = SimpleSelector TypeSelector -- ^ Convert a 'TypeSelector' into a 'SimpleSelector'. | Filter SelectorSequence SelectorFilter -- ^ Apply an additional 'SelectorFilter' to the 'SelectorSequence'.- deriving (Data, Eq, Ord, Show)+ deriving (Data, Eq, Generic, Ord, Show) +instance Hashable SelectorSequence+ -- | Add a given list of 'SelectorFilter's to the given 'SelectorSequence'. The -- filters are applied left-to-right. addFilters :: SelectorSequence -- ^ The 'SelectorSequence' to apply the filter on.@@ -248,16 +260,20 @@ SHash Hash -- ^ A 'Hash' object as filter. | SClass Class -- ^ A 'Class' object as filter. | SAttrib Attrib -- ^ An 'Attrib' object as filter.- deriving (Data, Eq, Ord, Show)+ deriving (Data, Eq, Generic, Ord, Show) +instance Hashable SelectorFilter+ -- | A css attribute can come in two flavors: either a constraint that the -- attribute should exists, or a constraint that a certain attribute should have -- a certain value (prefix, suffix, etc.). data Attrib = Exist AttributeName -- ^ A constraint that the given 'AttributeName' should exist. | Attrib AttributeName AttributeCombinator AttributeValue -- ^ A constraint about the value associated with the given 'AttributeName'.- deriving (Data, Eq, Ord, Show)+ deriving (Data, Eq, Generic, Ord, Show) +instance Hashable Attrib+ -- | A flipped version of the 'Attrib' data constructor, where one first -- specifies the conbinator, then the 'AttributeName' and finally the value. attrib :: AttributeCombinator -- ^ The 'AttributeCombinator' that specifies the required relation between the attribute and a value.@@ -334,8 +350,10 @@ data Namespace = NAny -- ^ A typeselector part that specifies that we accept all namespaces, in css denoted with @*@. | Namespace Text -- ^ A typselector part that specifies that we accept a certain namespace name.- deriving (Data, Eq, Ord, Show)+ deriving (Data, Eq, Generic, Ord, Show) +instance Hashable Namespace+ -- | The empty namespace. This is /not/ the wildcard namespace (@*@). This is a -- bidirectional namespace and can thus be used in expressions as well. pattern NEmpty :: Namespace@@ -346,22 +364,28 @@ data ElementName = EAny -- ^ A typeselector part that specifies that we accept all element names, in css denoted with @*@. | ElementName Text -- ^ A typeselector part that specifies that we accept a certain element name.- deriving (Data, Eq, Ord, Show)+ deriving (Data, Eq, Generic, Ord, Show) +instance Hashable ElementName+ -- | A typeselector is a combination of a selector for a namespace, and a -- selector for an element name. One, or both can be a wildcard. data TypeSelector = TypeSelector { selectorNamespace :: Namespace, -- ^ The selector for the namespace. elementName :: ElementName -- ^ The selector for the element name.- } deriving (Data, Eq, Ord, Show)+ } deriving (Data, Eq, Generic, Ord, Show) +instance Hashable TypeSelector+ -- | An attribute name is a name that optionally has a namespace, and the name -- of the attribute. data AttributeName = AttributeName { attributeNamespace :: Namespace, -- ^ The namespace to which the attribute name belongs. This can be 'NAny' as well. attributeName :: Text -- ^ The name of the attribute over which we make a claim.- } deriving (Data, Eq, Ord, Show)+ } deriving (Data, Eq, Generic, Ord, Show) +instance Hashable AttributeName+ -- | We use 'Text' as the type to store an attribute value. type AttributeValue = Text @@ -374,19 +398,25 @@ | PrefixMatch -- ^ The value is a prefix of the value in the attribute, denoted with @^=@ in css. | SuffixMatch -- ^ The value is a suffix of the value in the attribute, denoted with @$=@ in css. | SubstringMatch -- ^The value is a substring of the value in the attribute, denoted with @*=@ in css.- deriving (Bounded, Data, Enum, Eq, Ord, Read, Show)+ deriving (Bounded, Data, Enum, Eq, Generic, Ord, Read, Show) +instance Hashable AttributeCombinator+ -- | A css class, this is wrapped in a data type. The type only wraps the class -- name, not the dot prefix. newtype Class = Class { unClass :: Text -- ^ Obtain the name from the class.- } deriving (Data, Eq, Ord, Show)+ } deriving (Data, Eq, Generic, Ord, Show) +instance Hashable Class+ -- | A css hash (used to match an element with a given id). The type only wraps -- the hash name, not the hash (@#@) prefix. newtype Hash = Hash { unHash :: Text -- ^ Obtain the name from the hash.- } deriving (Data, Eq, Ord, Show)+ } deriving (Data, Eq, Generic, Ord, Show)++instance Hashable Hash -- | Convert the given 'AttributeCombinator' to its css selector counterpart. attributeCombinatorText :: AttributeCombinator -- ^ The 'AttributeCombinator' for which we obtain the corresponding css selector text.
src/Css3/Selector/Utils.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE Safe #-}+ {-| Module : Css3.Selector.Utils Description : A set of utility methods to encode and decode strings.
test/Spec.hs view
@@ -5,6 +5,7 @@ import Data.Binary(Binary, encode, decode) import Data.Function(on)+import Data.Hashable(Hashable(hashWithSalt)) import Data.Text(pack, unpack) import Test.Framework (defaultMain, testGroup)@@ -70,8 +71,20 @@ testProperty "Binary uniqness: attribute name" (uniqnessEncoding @AttributeName), testProperty "Binary uniqness: class" (uniqnessEncoding @Class), testProperty "Binary uniqness: hash" (uniqnessEncoding @Hash)+ ],+ testGroup "Check hash constraint for the Hashable instances" [+ testProperty "Different hash implies different items: selector group" (hashingDifferent @SelectorGroup),+ testProperty "Different hash implies different items: selector" (hashingDifferent @Selector),+ testProperty "Different hash implies different items: selector sequence" (hashingDifferent @SelectorSequence),+ testProperty "Different hash implies different items: selector filter" (hashingDifferent @SelectorFilter),+ testProperty "Different hash implies different items: namespace" (hashingDifferent @Namespace),+ testProperty "Different hash implies different items: element name" (hashingDifferent @ElementName),+ testProperty "Different hash implies different items: type selector" (hashingDifferent @TypeSelector),+ testProperty "Different hash implies different items: attribute" (hashingDifferent @Attrib),+ testProperty "Different hash implies different items: attribute name" (hashingDifferent @AttributeName),+ testProperty "Different hash implies different items: class" (hashingDifferent @Class),+ testProperty "Different hash implies different items: hash" (hashingDifferent @Hash) ]- ] encodeDecode :: Char -> String -> Bool@@ -85,6 +98,9 @@ binaryEquivalent :: (Binary a, Eq a, ToCssSelector a) => a -> Bool binaryEquivalent x = decode (encode x) == x++hashingDifferent :: (Hashable a, Eq a) => Int -> a -> a -> Bool+hashingDifferent slt xa xb = (hashWithSalt slt xa == hashWithSalt slt xb) || (xa /= xb) uniqnessEncoding :: (Binary a, Eq a, ToCssSelector a) => a -> a -> Bool uniqnessEncoding ca cb = (encode ca == encode cb) == (ca == cb)