2 ExistentialQuantification
4 , MultiParamTypeClasses
12 {-# OPTIONS_GHC -fno-warn-orphans #-}
13 module Network.HTTP.Lucu.StatusCode.Internal
19 import Control.Applicative
20 import Data.Ascii (Ascii, AsciiBuilder)
21 import qualified Data.Ascii as A
22 import Data.Attoparsec.Char8
23 import qualified Data.Attoparsec.Lazy as LP
24 import qualified Data.ByteString.Lazy.Char8 as Lazy
25 import Data.Convertible.Base
26 import Data.Convertible.Instances.Ascii ()
27 import Data.Convertible.Utils
29 import Language.Haskell.TH.Lib
30 import Language.Haskell.TH.Syntax
31 import Language.Haskell.TH.Quote
32 import Network.HTTP.Lucu.Parser
33 import Prelude.Unicode
35 -- |Type class for HTTP status codes.
37 -- Declaring types for each statuses is surely a pain. See:
38 -- 'statusCodes' quasi-quoter.
40 -- Minimal complete definition: 'numericCode' and 'textualStatus'.
41 class (Eq sc, Show sc) ⇒ StatusCode sc where
42 -- |Return the 3-digit integer for this status e.g. @200@
43 numericCode ∷ sc → Int
44 -- |Return the combination of 3-digit integer and reason phrase
45 -- for this status e.g. @200 OK@
46 textualStatus ∷ sc → AsciiBuilder
47 -- |Wrap the status code into 'SomeStatusCode'.
48 fromStatusCode ∷ sc → SomeStatusCode
49 fromStatusCode = SomeStatusCode
51 instance StatusCode sc ⇒ ConvertSuccess sc SomeStatusCode where
52 {-# INLINE convertSuccess #-}
53 convertSuccess = fromStatusCode
55 instance StatusCode sc ⇒ ConvertSuccess sc Ascii where
56 {-# INLINE convertSuccess #-}
57 convertSuccess = convertSuccessVia ((⊥) ∷ AsciiBuilder)
59 instance StatusCode sc ⇒ ConvertSuccess sc AsciiBuilder where
60 {-# INLINE convertSuccess #-}
61 convertSuccess = textualStatus
63 instance StatusCode sc ⇒ ConvertAttempt sc SomeStatusCode where
64 {-# INLINE convertAttempt #-}
65 convertAttempt = return ∘ cs
67 instance StatusCode sc ⇒ ConvertAttempt sc Ascii where
68 {-# INLINE convertAttempt #-}
69 convertAttempt = return ∘ cs
71 instance StatusCode sc ⇒ ConvertAttempt sc AsciiBuilder where
72 {-# INLINE convertAttempt #-}
73 convertAttempt = return ∘ cs
75 -- |Container type for the 'StatusCode' type class.
77 = ∀sc. StatusCode sc ⇒ SomeStatusCode sc
79 -- |Equivalence of 'StatusCode's. Two 'StatusCode's @α@ and
80 -- @β@ are said to be equivalent iff @'numericCode' α '=='
81 -- 'numericCode' β@.
82 instance Eq SomeStatusCode where
84 (==) = (∘ numericCode) ∘ (==) ∘ numericCode
86 instance Show SomeStatusCode where
87 show (SomeStatusCode sc) = show sc
89 instance StatusCode SomeStatusCode where
90 numericCode (SomeStatusCode sc) = numericCode sc
91 textualStatus (SomeStatusCode sc) = textualStatus sc
94 -- |'QuasiQuoter' for 'StatusCode' declarations.
102 -- 405 Method Not Allowed
109 -- data OK = OK deriving ('Eq', 'Show')
111 -- 'numericCode' _ = 200
112 -- 'textualStatus' _ = 'cs' (\"200 OK\" ∷ Ascii)
114 -- data BadRequest = BadRequest deriving ('Eq', 'Show')
115 -- instance BadRequest where
116 -- 'numericCode' _ = 400
117 -- 'textualStatus' _ = 'cs' (\"400 Bad Request\" ∷ Ascii)
119 -- data MethodNotAllowed = MethodNotAllowed deriving ('Eq', 'Show')
120 -- instance MethodNotAllowed where
121 -- 'numericCode' _ = 405
122 -- 'textualStatus' _ = 'cs' (\"405 Method Not Allowed\" ∷ Ascii)
124 statusCodes ∷ QuasiQuoter
125 statusCodes = QuasiQuoter {
126 quoteExp = const unsupported
127 , quotePat = const unsupported
128 , quoteType = const unsupported
129 , quoteDec = (concat <$>) ∘ mapM statusDecl ∘ parseStatusCodes ∘ Lazy.pack
132 unsupported ∷ Monad m ⇒ m α
133 unsupported = fail "Unsupported usage of statusCodes quasi-quoter."
135 parseStatusCodes ∷ Lazy.ByteString → [(Int, [Ascii])]
137 = case LP.parse pairs src of
139 → error $ "Unparsable status codes: "
140 ⧺ intercalate ", " eCtx
146 pairs ∷ Parser [(Int, [Ascii])]
147 pairs = do skipMany endOfLine
148 xs ← sepBy pair (skipMany1 endOfLine)
155 pair ∷ Parser (Int, [Ascii])
159 phrase ← sepBy1 word $ skipWhile1 (≡ '\x20')
165 word = A.unsafeFromByteString <$> takeWhile1 isAlpha_ascii
167 statusDecl ∷ (Int, [Ascii]) → Q [Dec]
168 statusDecl (num, phrase)
174 name = mkName $ concatMap cs phrase
177 dataDecl = dataD (cxt []) name [] [con] [''Eq, ''Show]
179 instanceDecl ∷ Q [Dec]
181 = [d| instance StatusCode $typ where
182 {-# INLINE CONLIKE numericCode #-}
183 numericCode _ = $(lift num)
184 {-# INLINE CONLIKE textualStatus #-}
185 textualStatus _ = $txt
192 con = return $ NormalC name []
195 txt = [| cs ($(lift txt') ∷ Ascii) |]
198 txt' = concat $ intersperse "\x20"
199 $ show num : map cs phrase