{- This module is where I am working on a new API design for creating and modifying MIME messages. It is not yet in use. -} import Control.Monad import Control.Monad.Error import Data.List import Data.Time -- * Message Data Types -- |Headers data To = To [Mailbox] deriving Show data From = From Mailbox deriving Show data Date = Date ZonedTime deriving Show data Subject = Subject String deriving Show data Keywords = Keywords [String] deriving Show -- |Supporting types data Mailbox = Mailbox String deriving Show -- |Message body data Body = Body String deriving Show class Header h where toHeader :: h -> RawHeader -- perhaps toHeader should be a normal polymorphic function, and this should be an escape routine fromHeader :: (Monad m) => RawHeader -> m h headerStr :: h -> String instance Header To where toHeader = undefined fromHeader = undefined headerStr _ = "To" instance Header From where toHeader = undefined fromHeader = undefined headerStr _ = "From" instance Header Date where toHeader = undefined fromHeader = undefined headerStr _ = "Date" -- |TODO: add proper escaping instance Header Subject where toHeader (Subject s) = ((headerStr (undefined :: Subject)), s) fromHeader (h,v) = return $ Subject v headerStr _ = "Subject" -- |TODO: add proper escaping instance Header Keywords where toHeader (Keywords w) = ((headerStr (undefined :: Keywords)), concat (intersperse ", " w)) fromHeader = undefined headerStr _ = "Keywords" -- * Filter types -- should raw header be escaped or unescaped? we want escaped if we -- are about to print it, but unescaped it we are looking stuff up. If -- we are looking stuff up though, we might want structured -- fields. Like above. We also would like to only decode the escaped -- stuff once, but we don't want to force decoding everything all the -- time. type RawHeader = (String, String) -- |add a header, leaving any current instance intact -- useful for headers that can appear any number of times -- does not include headers that can appear only 0 or 1 times class (Header h) => AddHeader h -- * Filter Instances -- |Headers that can appear multiple times instance AddHeader Keywords type HFilter = ([RawHeader] -> [RawHeader]) -- |set a header, replacing all existing occurences setHeader :: (Header h) => h -> HFilter setHeader h headers = (toHeader h) : (removeHeaders h headers) -- |add a header, leaving current occurences intact -- new header will appear first addHeader :: (AddHeader h) => h -> HFilter addHeader h headers = (toHeader h) : headers -- |remove all headers of a specific type -- probably need a variation that removes a specific header removeHeaders :: forall h. (Header h) => h -> HFilter removeHeaders _ = filter (\(n,_) -> n /= (headerStr (undefined :: h))) -- * Searching -- |use a the list monad for multiple results lookupHeader :: forall m h. (MonadPlus m, Header h) => [RawHeader] -> m h lookupHeader rawHeaders = case filter ((==) header . fst) rawHeaders of [] -> fail $ "Could not find header: " ++ header headers -> msum $ map fromHeader headers where header :: String header = headerStr (undefined :: h) -- * Experimental infix header combinators infixr 0 .+. infixr 0 .*. (.+.) :: (Header h) => h -> HFilter (.+.) h = (setHeader h) (.*.) :: (AddHeader h) => h -> HFilter (.*.) h = (addHeader h) empty :: [RawHeader] empty = [] -- it does not seem entirely obvious that the operator *after* the -- header is the one that selects whether a header will appear once or -- multiple times. exampleHeaders = ( (setHeader (Subject "whee")) . (setHeader (Subject "bork")). (addHeader (Keywords ["baz", "bar", "bam"])) . (addHeader (Keywords ["zip", "zap", "zop"])) ) exampleHeaders2 :: [RawHeader] exampleHeaders2 = ((Subject "whee") .+. (Subject "bork") .+. (Keywords ["baz", "bar", "bam"]) .+. (Keywords ["zip", "zap", "zop"]) .+. empty ) -- * HFilter helpers subject = setHeader . Subject keywords = addHeader . Keywords exampleHeaders3 :: [RawHeader] -> [RawHeader] exampleHeaders3 = ((subject "whee") . (subject "bork") . (keywords ["baz", "bar", "bam"]) . (keywords ["zip", "zap", "zop"])) -- * JUNK {- data HCons h t = HCons h t deriving Show data HNil = HNil deriving Show infixr .*. h .*. t = HCons h t -- This looks pretty, but the type-error messages do not message someAddr myAddr currTime = ( To [someAddr] .*. From myAddr .*. Date currTime .*. Subject "Test Message" .*. Body "I like cheese." ) -- This sort of hackery requires overlapping/incoherent type -- instances, and produces nasty error messages class HasHeader h m where hasHeader :: h -> m -> Bool instance HasHeader h (HCons h t) where hasHeader _ _ = True instance (HasHeader h t) => HasHeader h (HCons e t) where hasHeader _ _ = True {- class ValidMessage m where sendMessage :: (ValidMessage m) => m -> String sendMessage = -} -- parse :: String -> m -- parse -}