Michael Snoyman <michael@snoyman.com> writes:
* "Request and response headers should have case-insensitive match for the Eq instance." I'm thinking that this is the correct approach to take, and would like input on it. (Thanks Gregory.
Here's our (dead simple) code for this: ------------------------------------------------------------------------ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE OverloadedStrings #-} module Data.CIByteString ( CIByteString , toCI , unCI ) where -- for IsString instance import Data.ByteString.Char8 () import Data.ByteString (ByteString) import Data.ByteString.Internal (c2w, w2c) import qualified Data.ByteString as S import Data.Char import Data.String -- | A case-insensitive newtype wrapper for ByteString data CIByteString = CIByteString { unCI :: !ByteString , _lowercased :: !ByteString } toCI :: ByteString -> CIByteString toCI s = CIByteString s t where t = lowercase s instance Show CIByteString where show (CIByteString s _) = show s lowercase :: ByteString -> ByteString lowercase = S.map (c2w . toLower . w2c) instance Eq CIByteString where (CIByteString _ a) == (CIByteString _ b) = a == b (CIByteString _ a) /= (CIByteString _ b) = a /= b instance Ord CIByteString where (CIByteString _ a) <= (CIByteString _ b) = a <= b instance IsString CIByteString where fromString = toCI . fromString ------------------------------------------------------------------------ Note that we store the downcased version in the datatype, otherwise Eq/Ord has to perform the downcase a zillion times, only to throw it away afterwards. Note also that we ignore any encoding issues -- we can get away w/ this because HTTP header names are constrained by the protocol to be ASCII-only. This datatype also has an "IsString" instance so you can use string literals with "{-# LANGUAGE OverloadedStrings #-}" turned on. G. -- Gregory Collins <greg@gregorycollins.net>