2 ExistentialQuantification
4 , MultiParamTypeClasses
11 {-# OPTIONS_GHC -fno-warn-orphans #-}
12 module Network.HTTP.Lucu.StatusCode.Internal
20 import Control.Applicative
21 import Data.Ascii (Ascii, AsciiBuilder)
22 import qualified Data.Ascii as A
23 import Data.Attoparsec.Char8
24 import qualified Data.Attoparsec.Lazy as LP
25 import qualified Data.ByteString.Lazy.Char8 as Lazy
26 import Data.Convertible.Base
27 import Data.Convertible.Instances.Ascii ()
28 import Data.Convertible.Utils
30 import Language.Haskell.TH.Lib
31 import Language.Haskell.TH.Syntax
32 import Language.Haskell.TH.Quote
33 import Network.HTTP.Lucu.Parser
34 import Prelude.Unicode
36 -- |Type class for HTTP status codes.
38 -- Declaring types for each statuses is surely a pain. See:
39 -- 'statusCodes' quasi-quoter.
41 -- Minimal complete definition: 'numericCode' and 'textualStatus'.
42 class Show sc ⇒ StatusCode sc where
43 -- |Return the 3-digit integer for this status e.g. @200@
44 numericCode ∷ sc → Int
45 -- |Return the combination of 3-digit integer and reason phrase
46 -- for this status e.g. @200 OK@
47 textualStatus ∷ sc → AsciiBuilder
48 -- |Wrap the status code into 'SomeStatusCode'.
49 fromStatusCode ∷ sc → SomeStatusCode
50 fromStatusCode = SomeStatusCode
52 instance StatusCode sc ⇒ Eq sc where
55 -- |Container type for the 'StatusCode' type class.
57 = ∀sc. StatusCode sc ⇒ SomeStatusCode sc
59 instance Show SomeStatusCode where
60 show (SomeStatusCode sc) = show sc
63 -- |Equivalence of 'StatusCode's. Two 'StatusCode's @a@ and @b@ are
64 -- said to be equivalent iff @'numericCode' a '==' 'numericCode' b@.
66 -- U+2248, ALMOST EQUAL TO
67 (≈) ∷ (StatusCode α, StatusCode β) ⇒ α → β → Bool
69 α ≈ β = numericCode α ≡ numericCode β
71 -- |@(a ≉ b) '==' 'not' (a ≈ b)@
73 -- U+2249, NOT ALMOST EQUAL TO
74 (≉) ∷ (StatusCode α, StatusCode β) ⇒ α → β → Bool
78 instance StatusCode SomeStatusCode where
79 numericCode (SomeStatusCode sc) = numericCode sc
80 textualStatus (SomeStatusCode sc) = textualStatus sc
83 instance StatusCode sc ⇒ ConvertSuccess sc Ascii where
84 {-# INLINE convertSuccess #-}
85 convertSuccess = convertSuccessVia ((⊥) ∷ AsciiBuilder)
87 instance StatusCode sc ⇒ ConvertSuccess sc AsciiBuilder where
88 {-# INLINE convertSuccess #-}
89 convertSuccess = textualStatus
91 instance StatusCode sc ⇒ ConvertAttempt sc Ascii where
92 {-# INLINE convertAttempt #-}
93 convertAttempt = return ∘ cs
95 instance StatusCode sc ⇒ ConvertAttempt sc AsciiBuilder where
96 {-# INLINE convertAttempt #-}
97 convertAttempt = return ∘ cs
99 -- |'QuasiQuoter' for 'StatusCode' declarations.
101 -- Top-level splicing
107 -- 405 Method Not Allowed
114 -- data OK = OK deriving ('Show')
116 -- 'numericCode' _ = 200
117 -- 'textualStatus' _ = 'cs' (\"200 OK\" ∷ Ascii)
119 -- data BadRequest = BadRequest deriving ('Show')
120 -- instance BadRequest where
121 -- 'numericCode' _ = 400
122 -- 'textualStatus' _ = 'cs' (\"400 Bad Request\" ∷ Ascii)
124 -- data MethodNotAllowed = MethodNotAllowed deriving ('Show')
125 -- instance MethodNotAllowed where
126 -- 'numericCode' _ = 405
127 -- 'textualStatus' _ = 'cs' (\"405 Method Not Allowed\" ∷ Ascii)
129 statusCodes ∷ QuasiQuoter
130 statusCodes = QuasiQuoter {
131 quoteExp = const unsupported
132 , quotePat = const unsupported
133 , quoteType = const unsupported
134 , quoteDec = (concat <$>) ∘ mapM statusDecl ∘ parseStatusCodes ∘ Lazy.pack
137 unsupported ∷ Monad m ⇒ m α
138 unsupported = fail "Unsupported usage of statusCodes quasi-quoter."
140 parseStatusCodes ∷ Lazy.ByteString → [(Int, [Ascii])]
142 = case LP.parse pairs src of
144 → error $ "Unparsable status codes: "
145 ⧺ intercalate ", " eCtx
151 pairs ∷ Parser [(Int, [Ascii])]
152 pairs = do skipMany endOfLine
153 xs ← sepBy pair (skipMany1 endOfLine)
160 pair ∷ Parser (Int, [Ascii])
164 phrase ← sepBy1 word $ skipWhile1 (≡ '\x20')
170 word = A.unsafeFromByteString <$> takeWhile1 isAlpha_ascii
172 statusDecl ∷ (Int, [Ascii]) → Q [Dec]
173 statusDecl (num, phrase)
179 name = mkName $ concatMap cs phrase
182 dataDecl = dataD (cxt []) name [] [con] [''Show]
184 instanceDecl ∷ Q [Dec]
186 = [d| instance StatusCode $typ where
187 {-# INLINE CONLIKE numericCode #-}
188 numericCode _ = $(lift num)
189 {-# INLINE CONLIKE textualStatus #-}
190 textualStatus _ = $txt
197 con = return $ NormalC name []
200 txt = [| cs ($(lift txt') ∷ Ascii) |]
203 txt' = concat $ intersperse "\x20"
204 $ show num : map cs phrase