2 ExistentialQuantification
8 module Network.HTTP.Lucu.StatusCode.Internal
16 import Control.Applicative
17 import Data.Ascii (Ascii)
18 import qualified Data.Ascii as A
19 import Data.Attoparsec.Char8 as P
20 import Data.Attoparsec.Lazy as LP
21 import qualified Data.ByteString.Lazy.Char8 as Lazy
23 import Language.Haskell.TH.Lib
24 import Language.Haskell.TH.Syntax
25 import Language.Haskell.TH.Quote
26 import Network.HTTP.Lucu.Parser
27 import Prelude.Unicode
29 -- |The type class for HTTP status codes.
31 -- Declaring types for each statuses is surely a pain. See:
34 -- Minimal complete definition: 'numericCode' and 'textualStatus'.
35 class Show sc ⇒ StatusCode sc where
36 -- |Return the 3-digit integer for this status e.g. @200@
37 numericCode ∷ sc → Int
38 -- |Return the combination of 3-digit integer and reason phrase
39 -- for this status e.g. @200 OK@
40 textualStatus ∷ sc → Ascii
41 -- |Wrap the status code into 'SomeStatusCode'.
42 fromStatusCode ∷ sc → SomeStatusCode
43 fromStatusCode = SomeStatusCode
45 -- |Container type for 'StatusCode' type class.
47 = ∀sc. StatusCode sc ⇒ SomeStatusCode sc
49 instance Show SomeStatusCode where
50 show (SomeStatusCode sc) = show sc
52 instance Eq SomeStatusCode where
53 (SomeStatusCode α) == (SomeStatusCode β) = α ≈ β
56 -- |Equivalence of 'StatusCode's. Two 'StatusCode's @a@ and @b@ are
57 -- said to be equivalent iff @'numericCode' a '==' 'numericCode' b@.
59 -- U+2248, ALMOST EQUAL TO
60 (≈) ∷ (StatusCode α, StatusCode β) ⇒ α → β → Bool
62 α ≈ β = numericCode α ≡ numericCode β
64 -- |@(a ≉ b) '==' 'not' (a ≈ b)@
66 -- U+2249, NOT ALMOST EQUAL TO
67 (≉) ∷ (StatusCode α, StatusCode β) ⇒ α → β → Bool
71 instance StatusCode SomeStatusCode where
72 numericCode (SomeStatusCode sc) = numericCode sc
73 textualStatus (SomeStatusCode sc) = textualStatus sc
76 -- |'QuasiQuoter' for 'StatusCode' declarations.
84 -- 405 Method Not Allowed
91 -- data OK = OK deriving ('Show')
93 -- 'numericCode' _ = 200
94 -- 'textualStatus' _ = 'A.unsafeFromString' \"200 OK\"
96 -- data BadRequest = BadRequest deriving ('Show')
97 -- instance BadRequest where
98 -- 'numericCode' _ = 400
99 -- 'textualStatus' _ = 'A.unsafeFromString' \"400 Bad Request\"
101 -- data MethodNotAllowed = MethodNotAllowed deriving ('Show')
102 -- instance MethodNotAllowed where
103 -- 'numericCode' _ = 405
104 -- 'textualStatus' _ = 'A.unsafeFromString' \"405 Method Not Allowed\"
106 statusCodes ∷ QuasiQuoter
107 statusCodes = QuasiQuoter {
108 quoteExp = const unsupported
109 , quotePat = const unsupported
110 , quoteType = const unsupported
111 , quoteDec = (concat <$>) ∘ mapM statusDecl ∘ parseStatusCodes ∘ Lazy.pack
114 unsupported ∷ Monad m ⇒ m α
115 unsupported = fail "Unsupported usage of statusCodes quasi-quoter."
117 parseStatusCodes ∷ Lazy.ByteString → [(Int, [Ascii])]
119 = case LP.parse pairs src of
121 → error $ "Unparsable status codes: "
122 ⧺ intercalate ", " eCtx
128 pairs ∷ Parser [(Int, [Ascii])]
129 pairs = do skipMany endOfLine
130 xs ← sepBy pair (skipMany1 endOfLine)
137 pair ∷ Parser (Int, [Ascii])
141 phrase ← sepBy1 word $ skipWhile1 (≡ '\x20')
147 word = A.unsafeFromByteString <$> P.takeWhile1 isAlpha_ascii
149 statusDecl ∷ (Int, [Ascii]) → Q [Dec]
150 statusDecl (num, phrase)
156 name = mkName $ concatMap A.toString phrase
159 dataDecl = dataD (cxt []) name [] [con] [''Show]
161 instanceDecl ∷ Q [Dec]
163 = [d| instance StatusCode $typ where
164 {-# INLINE numericCode #-}
165 numericCode _ = $(lift num)
166 {-# INLINE textualStatus #-}
167 textualStatus _ = $txt
174 con = return $ NormalC name []
177 txt = [| A.unsafeFromString $(lift txt') |]
180 txt' = concat $ intersperse "\x20"
181 $ show num : map A.toString phrase