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
28 import Data.Eq.Indirect
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 (Eq sc, 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 -- |Equivalence of 'StatusCode's. Two 'StatusCode's @α@ and
53 -- @β@ are said to be equivalent iff @'numericCode' α '=='
54 -- 'numericCode' β@.
55 instance StatusCode sc ⇒ Eq' sc where
57 {-# INLINE CONLIKE unify #-}
60 -- |Container type for the 'StatusCode' type class.
62 = ∀sc. StatusCode sc ⇒ SomeStatusCode sc
64 instance Eq SomeStatusCode where
65 {-# INLINE CONLIKE (==) #-}
68 instance Show SomeStatusCode where
69 show (SomeStatusCode sc) = show sc
71 instance StatusCode SomeStatusCode where
72 numericCode (SomeStatusCode sc) = numericCode sc
73 textualStatus (SomeStatusCode sc) = textualStatus sc
76 instance StatusCode sc ⇒ ConvertSuccess sc Ascii where
77 {-# INLINE convertSuccess #-}
78 convertSuccess = convertSuccessVia ((⊥) ∷ AsciiBuilder)
80 instance StatusCode sc ⇒ ConvertSuccess sc AsciiBuilder where
81 {-# INLINE convertSuccess #-}
82 convertSuccess = textualStatus
84 instance StatusCode sc ⇒ ConvertAttempt sc Ascii where
85 {-# INLINE convertAttempt #-}
86 convertAttempt = return ∘ cs
88 instance StatusCode sc ⇒ ConvertAttempt sc AsciiBuilder where
89 {-# INLINE convertAttempt #-}
90 convertAttempt = return ∘ cs
92 -- |'QuasiQuoter' for 'StatusCode' declarations.
100 -- 405 Method Not Allowed
107 -- data OK = OK deriving ('Eq', 'Show')
109 -- 'numericCode' _ = 200
110 -- 'textualStatus' _ = 'cs' (\"200 OK\" ∷ Ascii)
112 -- data BadRequest = BadRequest deriving ('Eq', 'Show')
113 -- instance BadRequest where
114 -- 'numericCode' _ = 400
115 -- 'textualStatus' _ = 'cs' (\"400 Bad Request\" ∷ Ascii)
117 -- data MethodNotAllowed = MethodNotAllowed deriving ('Eq', 'Show')
118 -- instance MethodNotAllowed where
119 -- 'numericCode' _ = 405
120 -- 'textualStatus' _ = 'cs' (\"405 Method Not Allowed\" ∷ Ascii)
122 statusCodes ∷ QuasiQuoter
123 statusCodes = QuasiQuoter {
124 quoteExp = const unsupported
125 , quotePat = const unsupported
126 , quoteType = const unsupported
127 , quoteDec = (concat <$>) ∘ mapM statusDecl ∘ parseStatusCodes ∘ Lazy.pack
130 unsupported ∷ Monad m ⇒ m α
131 unsupported = fail "Unsupported usage of statusCodes quasi-quoter."
133 parseStatusCodes ∷ Lazy.ByteString → [(Int, [Ascii])]
135 = case LP.parse pairs src of
137 → error $ "Unparsable status codes: "
138 ⧺ intercalate ", " eCtx
144 pairs ∷ Parser [(Int, [Ascii])]
145 pairs = do skipMany endOfLine
146 xs ← sepBy pair (skipMany1 endOfLine)
153 pair ∷ Parser (Int, [Ascii])
157 phrase ← sepBy1 word $ skipWhile1 (≡ '\x20')
163 word = A.unsafeFromByteString <$> takeWhile1 isAlpha_ascii
165 statusDecl ∷ (Int, [Ascii]) → Q [Dec]
166 statusDecl (num, phrase)
172 name = mkName $ concatMap cs phrase
175 dataDecl = dataD (cxt []) name [] [con] [''Eq, ''Show]
177 instanceDecl ∷ Q [Dec]
179 = [d| instance StatusCode $typ where
180 {-# INLINE CONLIKE numericCode #-}
181 numericCode _ = $(lift num)
182 {-# INLINE CONLIKE textualStatus #-}
183 textualStatus _ = $txt
190 con = return $ NormalC name []
193 txt = [| cs ($(lift txt') ∷ Ascii) |]
196 txt' = concat $ intersperse "\x20"
197 $ show num : map cs phrase