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 -- | A query string for HTTP. -- -- > "param1=value1¶m2=value2" 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 ] -- | Convert a parameter list to 'QueryString'. -- -- >>> toString $ queryString [("param1", "value1"), ("param2", "value2")] -- "param1=value1¶m2=value2" queryString :: [(ByteString, ByteString)] -> QueryString queryString = QueryString . sort -- | Convert a parameter map to 'QueryString'. -- -- >>> import qualified Data.Map as Map -- >>> toString $ queryStringFromMap $ Map.fromList [("param1", "value1"), ("param2", "value2")] -- "param1=value1¶m2=value2" queryStringFromMap :: Map ByteString ByteString -> QueryString queryStringFromMap = queryString . Map.toList