module Network.HTTP.Lucu.Response.StatusCode.Internal
( StatusCode(..)
, SomeStatusCode
, statusCodes
)
where
import Control.Applicative
import Control.Applicative.Unicode
import Control.Monad.Unicode
import Data.Ascii (Ascii, AsciiBuilder)
import qualified Data.Ascii as A
import Data.Attoparsec.Char8
import qualified Data.Attoparsec.Lazy as LP
import qualified Data.ByteString.Lazy.Char8 as Lazy
import Data.Convertible.Base
import Data.Convertible.Instances.Ascii ()
import Data.Convertible.Utils
import Data.List
import Data.Monoid
import Language.Haskell.TH.Lib
import Language.Haskell.TH.Syntax
import Language.Haskell.TH.Quote
import Network.HTTP.Lucu.OrphanInstances ()
import Network.HTTP.Lucu.Parser
import Prelude.Unicode
class (Eq sc, Show sc) ⇒ StatusCode sc where
numericCode ∷ sc → Int
textualStatus ∷ sc → AsciiBuilder
fromStatusCode ∷ sc → SomeStatusCode
fromStatusCode = SomeStatusCode
instance StatusCode sc ⇒ ConvertSuccess sc SomeStatusCode where
convertSuccess = fromStatusCode
instance StatusCode sc ⇒ ConvertSuccess sc Ascii where
convertSuccess = convertSuccessVia ((⊥) ∷ AsciiBuilder)
instance StatusCode sc ⇒ ConvertSuccess sc AsciiBuilder where
convertSuccess = textualStatus
instance StatusCode sc ⇒ ConvertAttempt sc SomeStatusCode where
convertAttempt = return ∘ cs
instance StatusCode sc ⇒ ConvertAttempt sc Ascii where
convertAttempt = return ∘ cs
instance StatusCode sc ⇒ ConvertAttempt sc AsciiBuilder where
convertAttempt = return ∘ cs
data SomeStatusCode
= ∀sc. StatusCode sc ⇒ SomeStatusCode !sc
instance Eq SomeStatusCode where
(==) = (∘ numericCode) ∘ (==) ∘ numericCode
instance Show SomeStatusCode where
show (SomeStatusCode sc) = show sc
instance StatusCode SomeStatusCode where
numericCode (SomeStatusCode sc) = numericCode sc
textualStatus (SomeStatusCode sc) = textualStatus sc
fromStatusCode = id
statusCodes ∷ QuasiQuoter
statusCodes = QuasiQuoter {
quoteExp = const unsupported
, quotePat = const unsupported
, quoteType = const unsupported
, quoteDec = (concat <$>)
∘ (mapM statusDecl =≪)
∘ parseStatusCodes
∘ Lazy.pack
}
where
unsupported ∷ Monad m ⇒ m α
unsupported = fail "Unsupported usage of statusCodes quasi-quoter."
parseStatusCodes ∷ Monad m ⇒ Lazy.ByteString → m [(Int, [Ascii])]
parseStatusCodes src
= case LP.parse pairs src of
LP.Fail _ eCtx e
→ fail $ "Unparsable status codes: "
⧺ intercalate ", " eCtx
⧺ ": "
⧺ e
LP.Done _ xs
→ return xs
where
pairs ∷ Parser [(Int, [Ascii])]
pairs = do skipMany endOfLine
xs ← sepBy pair (skipMany1 endOfLine)
skipMany endOfLine
endOfInput
return xs
<?>
"pairs"
pair ∷ Parser (Int, [Ascii])
pair = do skipSpace
num ← decimal
skipSpace1
phrase ← sepBy1 word $ skipWhile1 (≡ '\x20')
return (num, phrase)
<?>
"pair"
word ∷ Parser Ascii
word = A.unsafeFromByteString <$> takeWhile1 isAlpha_ascii
statusDecl ∷ (Int, [Ascii]) → Q [Dec]
statusDecl (num, phrase) = (:) <$> dataDecl ⊛ instanceDecl
where
dataDecl ∷ Q Dec
dataDecl = dataD (cxt []) name [] [con] [''Eq, ''Show]
name ∷ Name
name = mkName $ concatMap cs phrase
con ∷ Q Con
con = normalC name []
instanceDecl ∷ Q [Dec]
instanceDecl
= [d| instance StatusCode $typ where
numericCode _ = $(lift num)
textualStatus _ = cs $(lift txt)
|]
typ ∷ Q Type
typ = conT name
txt ∷ Ascii
txt = mconcat $ intersperse "\x20"
$ A.unsafeFromString (show num) : phrase