{-# LANGUAGE FlexibleInstances #-}{-# LANGUAGE TypeSynonymInstances #-}{-# LANGUAGE Rank2Types #-}{-# LANGUAGE OverloadedStrings #-}moduleYesod.Content(-- * ContentContent(..),emptyContent,ToContent(..)-- * Mime types-- ** Data type,ContentType,typeHtml,typePlain,typeJson,typeXml,typeAtom,typeRss,typeJpeg,typePng,typeGif,typeSvg,typeJavascript,typeCss,typeFlv,typeOgv,typeOctet-- * Utilities,simpleContentType-- * Representations,ChooseRep,HasReps(..),defChooseRep-- ** Specific content types,RepHtml(..),RepJson(..),RepHtmlJson(..),RepPlain(..),RepXml(..)-- * Utilities,formatW3,formatRFC1123,formatRFC822)whereimportData.Maybe(mapMaybe)importqualifiedData.ByteStringasBimportqualifiedData.ByteString.LazyasLimportData.Text.Lazy(Text,pack)importqualifiedData.TextasTimportData.TimeimportSystem.LocaleimportqualifiedData.Text.EncodingimportqualifiedData.Text.Lazy.EncodingimportBlaze.ByteString.Builder(Builder,fromByteString,fromLazyByteString)importData.Monoid(mempty)importText.Hamlet(Html)importText.Blaze.Renderer.Utf8(renderHtmlBuilder)importData.String(IsString(fromString))importNetwork.Wai(FilePart)importData.Conduit(Source,Flush)dataContent=ContentBuilderBuilder(MaybeInt)-- ^ The content and optional content length.|ContentSource(SourceIO(FlushBuilder))|ContentFileFilePath(MaybeFilePart)-- | Zero-length enumerator.emptyContent::ContentemptyContent=ContentBuildermempty$Just0instanceIsStringContentwherefromString=toContent-- | Anything which can be converted into 'Content'. Most of the time, you will-- want to use the 'ContentBuilder' constructor. An easier approach will be to use-- a pre-defined 'toContent' function, such as converting your data into a lazy-- bytestring and then calling 'toContent' on that.---- Please note that the built-in instances for lazy data structures ('String',-- lazy 'L.ByteString', lazy 'Text' and 'Html') will not automatically include-- the content length for the 'ContentBuilder' constructor.classToContentawheretoContent::a->ContentinstanceToContentBuilderwheretoContent=flipContentBuilderNothinginstanceToContentB.ByteStringwheretoContentbs=ContentBuilder(fromByteStringbs)$Just$B.lengthbsinstanceToContentL.ByteStringwheretoContent=flipContentBuilderNothing.fromLazyByteStringinstanceToContentT.TextwheretoContent=toContent.Data.Text.Encoding.encodeUtf8instanceToContentTextwheretoContent=toContent.Data.Text.Lazy.Encoding.encodeUtf8instanceToContentStringwheretoContent=toContent.packinstanceToContentHtmlwheretoContentbs=ContentBuilder(renderHtmlBuilderbs)Nothing-- | A function which gives targetted representations of content based on the-- content-types the user accepts.typeChooseRep=[ContentType]-- ^ list of content-types user accepts, ordered by preference->IO(ContentType,Content)-- | Any type which can be converted to representations.classHasRepsawherechooseRep::a->ChooseRep-- | A helper method for generating 'HasReps' instances.---- This function should be given a list of pairs of content type and conversion-- functions. If none of the content types match, the first pair is used.defChooseRep::[(ContentType,a->IOContent)]->a->ChooseRepdefChooseReprepsats=dolet(ct,c)=casemapMaybehelpertsof(x:_)->x[]->caserepsof[]->error"Empty reps to defChooseRep"(x:_)->xc'<-careturn(ct,c')wherehelperct=doc<-lookupctrepsreturn(ct,c)instanceHasRepsChooseRepwherechooseRep=idinstanceHasReps()wherechooseRep=defChooseRep[(typePlain,const$return$toContentB.empty)]instanceHasReps(ContentType,Content)wherechooseRep=const.returninstanceHasReps[(ContentType,Content)]wherechooseRepacts=return$casefilter(\(ct,_)->goct`elem`mapgocts)aof((ct,c):_)->(ct,c)_->caseaof(x:_)->x_->error"chooseRep [(ContentType, Content)] of empty"wherego=simpleContentTypenewtypeRepHtml=RepHtmlContentinstanceHasRepsRepHtmlwherechooseRep(RepHtmlc)_=return(typeHtml,c)newtypeRepJson=RepJsonContentinstanceHasRepsRepJsonwherechooseRep(RepJsonc)_=return(typeJson,c)dataRepHtmlJson=RepHtmlJsonContentContentinstanceHasRepsRepHtmlJsonwherechooseRep(RepHtmlJsonhtmljson)=chooseRep[(typeHtml,html),(typeJson,json)]newtypeRepPlain=RepPlainContentinstanceHasRepsRepPlainwherechooseRep(RepPlainc)_=return(typePlain,c)newtypeRepXml=RepXmlContentinstanceHasRepsRepXmlwherechooseRep(RepXmlc)_=return(typeXml,c)typeContentType=B.ByteString-- FIXME Text?typeHtml::ContentTypetypeHtml="text/html; charset=utf-8"typePlain::ContentTypetypePlain="text/plain; charset=utf-8"typeJson::ContentTypetypeJson="application/json; charset=utf-8"typeXml::ContentTypetypeXml="text/xml"typeAtom::ContentTypetypeAtom="application/atom+xml"typeRss::ContentTypetypeRss="application/rss+xml"typeJpeg::ContentTypetypeJpeg="image/jpeg"typePng::ContentTypetypePng="image/png"typeGif::ContentTypetypeGif="image/gif"typeSvg::ContentTypetypeSvg="image/svg+xml"typeJavascript::ContentTypetypeJavascript="text/javascript; charset=utf-8"typeCss::ContentTypetypeCss="text/css; charset=utf-8"typeFlv::ContentTypetypeFlv="video/x-flv"typeOgv::ContentTypetypeOgv="video/ogg"typeOctet::ContentTypetypeOctet="application/octet-stream"-- | Removes \"extra\" information at the end of a content type string. In-- particular, removes everything after the semicolon, if present.---- For example, \"text/html; charset=utf-8\" is commonly used to specify the-- character encoding for HTML data. This function would return \"text/html\".simpleContentType::ContentType->ContentTypesimpleContentType=fst.B.breakByte59-- 59 == ;-- | Format a 'UTCTime' in W3 format.formatW3::UTCTime->T.TextformatW3=T.pack.formatTimedefaultTimeLocale"%FT%X-00:00"-- | Format as per RFC 1123.formatRFC1123::UTCTime->T.TextformatRFC1123=T.pack.formatTimedefaultTimeLocale"%a, %d %b %Y %X %Z"-- | Format as per RFC 822.formatRFC822::UTCTime->T.TextformatRFC822=T.pack.formatTimedefaultTimeLocale"%a, %d %b %Y %H:%M:%S %z"