@@ -0,0 +1,63 @@
1+{-# LANGUAGE OverloadedStrings #-}
3+module Links (links) where
5+import Control.Applicative
6+import Control.DeepSeq (NFData(..))
7+import Criterion.Main (Benchmark, bench, nf)
8+import Data.Attoparsec as A
9+import Data.Attoparsec.Char8 as A8
10+import Data.ByteString.Char8 as B8
13+ linkURL :: ByteString
14+ , linkParams :: [(ByteString, ByteString)]
15+ } deriving (Eq, Show)
17+instance NFData Link where
18+ rnf l = rnf (linkURL l) `seq` rnf (linkParams l)
21+link = Link <$> url <*> many (char8 ';' *> skipSpace *> param)
22+ where url = char8 '<' *> A8.takeTill (=='>') <* char8 '>' <* skipSpace
24+param :: Parser (ByteString, ByteString)
27+ skipSpace *> "=" *> skipSpace
29+ let isTokenChar = A.inClass "!#$%&'()*+./0-9:<=>?@a-zA-Z[]^_`{|}~-"
32+ _ -> A.takeWhile isTokenChar
36+data Quot = Literal | Backslash
38+quotedString :: Parser ByteString
39+quotedString = char '"' *> (fixup <$> body) <* char '"'
40+ where body = A8.scan Literal $ \s c ->
42+ (Literal, '\\') -> backslash
43+ (Literal, '"') -> Nothing
45+ literal = Just Literal
46+ backslash = Just Backslash
47+ fixup = B8.pack . go . B8.unpack
48+ where go ('\\' : x@'\\' : xs) = x : go xs
49+ go ('\\' : x@'"' : xs) = x : go xs
50+ go (x : xs) = x : go xs
53+paramName :: Parser ByteString
55+ name <- A.takeWhile1 $ A.inClass "a-zA-Z0-9!#$&+-.^_`|~"
58+ Just '*' -> B8.snoc name '*'
62+links = bench "links" $ nf (A.parseOnly link) lnk
63+ where lnk = "<https://api.github.com/search/code?q=addClass+user%3Amozilla&page=2>; rel=\"next\", <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=34>; rel=\"last\""