content: {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE LambdaCase #-}
  
  module Data.Apiary.Method
      ( Method(..)
      , renderMethod
      , dispatchMethod
      , parseMethod
      ) where
  
  import Data.Hashable(Hashable(..))
  import Data.String(IsString(..))
  import qualified Data.ByteString.Char8 as S
  import qualified Data.ByteString.Unsafe as U
  
  data Method
      = GET
      | POST
      | HEAD
      | PUT
      | DELETE
      | TRACE
      | CONNECT
      | OPTIONS
      | PATCH
      | NonStandard S.ByteString
      deriving (Eq, Ord, Read, Show)
  
  instance Hashable Method where
      hash GET             = 0
      hash POST            = 1
      hash HEAD            = 2
      hash PUT             = 3
      hash DELETE          = 4
      hash TRACE           = 5
      hash CONNECT         = 6
      hash OPTIONS         = 7
      hash PATCH           = 8
      hash (NonStandard s) = hash s
      hashWithSalt salt x = salt `hashWithSalt` hash x
  
  renderMethod :: Method -> S.ByteString
  renderMethod = \case
      GET           -> "GET"
      POST          -> "POST"
      HEAD          -> "HEAD"
      PUT           -> "PUT"
      DELETE        -> "DELETE"
      TRACE         -> "TRACE"
      CONNECT       -> "CONNECT"
      OPTIONS       -> "OPTIONS"
      PATCH         -> "PATCH"
      NonStandard a -> a
  {-# INLINE renderMethod #-}
  
  dispatchMethod :: a -> a -> a -> a -> a -> a -> a -> a -> a -> (S.ByteString -> a) -> S.ByteString -> a
  dispatchMethod get post head_ put delete trace connect options patch ns s
      | S.length s < 2 = ns s
      | otherwise = case U.unsafeHead s of
          71 -> if S.length s == 3 && cc 1 69 && cc 2 84 then get else ns s
          80 -> case U.unsafeIndex s 1 of
              79 -> if S.length s == 4 && cc 2 83 && cc 3 84 then post  else ns s
              85 -> if S.length s == 3 && cc 2 84            then put   else ns s
              65 -> if S.length s == 5 && cc 2 84 && cc 3 67 && cc 4 72 then patch else ns s
              _  -> ns s
          72 -> if S.length s == 4 && cc 1 69 && cc 2 65 && cc 3 68 then head_ else ns s
          68 -> if S.length s == 6 && cc 1 69 && cc 2 76 && cc 3 69 && cc 4 84 && cc 5 69 then delete else ns s
          84 -> if S.length s == 5 && cc 1 82 && cc 2 65 && cc 3 67 && cc 4 69 then trace else ns s
          67 -> if S.length s == 7 && cc 1 79 && cc 2 78 && cc 3 78 && cc 4 69 && cc 5 67 && cc 6 84 then connect else ns s
          79 -> if S.length s == 7 && cc 1 80 && cc 2 84 && cc 3 73 && cc 4 79 && cc 5 78 && cc 6 83 then options else ns s
          _  -> ns s
    where
      cc i c = U.unsafeIndex s i == c
  
  parseMethod :: S.ByteString -> Method
  parseMethod = dispatchMethod GET POST HEAD PUT DELETE TRACE CONNECT OPTIONS PATCH NonStandard
  
  instance IsString Method where
      fromString = parseMethod . S.pack
  