module Network.HTTP.QueryString.Internal where
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BC
import Data.List (sort)
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Monoid
import qualified Network.HTTP.Types as HTTP
data QueryString =
QueryString { rawData :: [(ByteString, ByteString)] }
deriving (Eq)
instance Monoid QueryString where
mempty = QueryString []
mappend a b = QueryString $ rawData a `merge` rawData b
merge :: Ord a => [a] -> [a] -> [a]
merge [] bs = bs
merge as [] = as
merge (a:as) (b:bs)
| a <= b = a:merge as (b:bs)
| otherwise = b:merge (a:as) bs
instance Read QueryString where
readsPrec _
= maybe [] (\a -> [(a, "")])
. parseQuery
. BC.pack
parseQuery :: ByteString -> Maybe QueryString
parseQuery
= fmap queryString
. sequence
. map (t . map (HTTP.urlDecode True) . BC.split '=')
. BC.split '&'
where
t [a, b] = Just (a, b)
t _ = Nothing
toString :: QueryString -> ByteString
toString = BS.intercalate "&" . map concatWithEqual . rawData
where
concatWithEqual ("", _) = error "name is null."
concatWithEqual (key, val) = mconcat
[ HTTP.urlEncode True key
, "="
, HTTP.urlEncode True val
]
queryString :: [(ByteString, ByteString)] -> QueryString
queryString = QueryString . sort
queryStringFromMap :: Map ByteString ByteString -> QueryString
queryStringFromMap = queryString . Map.toList