{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}

module Airship.Internal.Parsers
    ( parseEtag
    , parseEtagList
    ) where

import Prelude hiding (takeWhile)

#if __GLASGOW_HASKELL__ < 710
import Control.Applicative ((<$>), (<|>), (*>), (<*))
#else
import Control.Applicative ((<|>))
#endif
import Data.Attoparsec.ByteString.Char8 (Parser, parseOnly, sepBy', char,
                                         string, takeWhile,
                                         takeWhile1, inClass, endOfInput)
import Data.ByteString (ByteString)

import Airship.Types (ETag(..))

comma :: Parser Char
comma = char ','

doubleQuote :: Char
doubleQuote = '"'

insideQuotes :: Parser a -> Parser a
insideQuotes a = char doubleQuote *> a <* char doubleQuote

optionalWhitespace :: Parser ByteString
optionalWhitespace = takeWhile (inClass " \t")

insideWhitespace :: Parser a -> Parser a
insideWhitespace a = optionalWhitespace *> a <* optionalWhitespace

weakETag :: Parser ETag
weakETag = Weak <$> (string "W/" *> insideQuotes rest)
    where rest = takeWhile1 (/= doubleQuote)

strongETag :: Parser ETag
strongETag = insideQuotes strong
    where strong = Strong <$> takeWhile1 (/= doubleQuote)

eTag :: Parser ETag
eTag = insideWhitespace (weakETag <|> strongETag)

parseEtag :: ByteString -> Maybe ETag
parseEtag input = either (const Nothing) Just (parseOnly eTagToEnd input)
    where eTagToEnd = eTag <* endOfInput

-- | Parse a list of Etags, returning an empty list if parsing fails
parseEtagList :: ByteString -> [ETag]
parseEtagList input = either (const []) id parseResult
    where parseResult = parseOnly eTagList input
          eTagList = (eTag `sepBy'` comma) <* endOfInput