    {-# LANGUAGE QuasiQuotes #-}
    {-# LANGUAGE TemplateHaskell #-}

    {- |
    Module      :  Text.XML.QQ

    Copyright   :  Dennis Gosnell 2017
    License     :  BSD3

    Maintainer  :  Dennis Gosnell (cdep.illabout@gmail.com)
    Stability   :  experimental
    Portability :  unknown

    This module provides a quasi-quoter for XML 'Document's.  See the 'xml'
    function for some examples.

    The difference between "Text.XML.QQ" and "Text.HTML.QQ" is the function that is
    used to parse the input 'String' into a 'Document'.

    'Text.XML.QQ.xml' uses 'Text.XML.parseText' to parse the input 'String'.
    'Text.XML.parseText' returns an error on a malformed document.  This is
    generally what you want for XML documents.

    'Text.HTML.QQ.html' uses 'Text.HTML.DOM'.parseLT' to parse the input 'String'.
    'Text.HTML.DOM.parseLT' will parse any HTML document, skipping parts of the
    document that are malformed.  This is generally what you want for HTML
    documents.
    -}

    module Text.XML.QQ
      ( xml
      , xmlUnsafe
      , xmlRaw
        -- * Types
      , Document
      , SomeException
      ) where

    import Control.Exception (SomeException)
    import Control.FromSum (fromEither)
    import Data.Default (def)
    import Data.Text.Lazy (pack)
    import Language.Haskell.TH (appE)
    import Language.Haskell.TH.Quote (QuasiQuoter(..))
    import Language.Haskell.TH.Syntax (lift)
    import Text.Blaze.Renderer.Text (renderMarkup)
    import Text.Heterocephalus (compileFromString, textSetting)
    import Text.XML (Document(..), parseText)

    import Text.XMLHTML.Internal
           (createExpQuasiQuoter, handleParseDocErr)

    -- $setup
    -- >>> :set -XQuasiQuotes
    -- >>> :set -XTemplateHaskell

    -- | This 'QuasiQuoter' produces XML 'Document's.
    --
    -- This 'QuasiQuoter' produces expressions of type
    -- @'Either' 'SomeException' 'Document'@.  It produces a
    -- @'Left' 'SomeException'@ when the input string cannot be parsed into an XML
    -- 'Document'.
    --
    -- Here's a simple example of using it:
    --
    -- >>> [xml|<html></html>|] :: Either SomeException Document
    -- Right (Document {documentPrologue = Prologue {prologueBefore = [], prologueDoctype = Nothing, prologueAfter = []}, documentRoot = Element {elementName = Name {nameLocalName = "html", nameNamespace = Nothing, namePrefix = Nothing}, elementAttributes = fromList [], elementNodes = []}, documentEpilogue = []})
    --
    -- Internally, this function is using the
    -- <https://hackage.haskell.org/package/heterocephalus heterocephalus> package.
    -- This means you can use variable interpolation, as well as @forall@, @if@,
    -- and @case@ control statements.  Checkout the
    -- <https://github.com/arowM/heterocephalus#syntax heterocephalus README> for
    -- more info.
    --
    -- >>> let a = "hello world"
    -- >>> [xml|<html>#{a}</html>|]
    -- Right ...
    --
    -- Here's an example of invalue XML that will produce a 'Left' value:
    --
    -- >>> [xml|<html </html>|]
    -- Left ...
    --
    -- Here's an example of a template that can be parsed as an HTML 'Document', but
    -- not as an XML 'Document':
    --
    -- >>> [xml|<html><br></html>|]
    -- Left ...
    xml :: QuasiQuoter
    xml :: QuasiQuoter
    xml =
      (String -> Q Exp) -> QuasiQuoter
    createExpQuasiQuoter ((String -> Q Exp) -> QuasiQuoter)
    -> (String -> Q Exp) -> QuasiQuoter
    forall a b. (a -> b) -> a -> b
    $ \String
    string ->
        Q Exp -> Q Exp -> Q Exp
    forall (m :: * -> *). Quote m => m Exp -> m Exp -> m Exp
    appE [|parseText def . renderMarkup|] (Q Exp -> Q Exp) -> Q Exp -> Q Exp
    forall a b. (a -> b) -> a -> b
    $ HeterocephalusSetting -> String -> Q Exp
    compileFromString HeterocephalusSetting
    textSetting String
    string

    -- | This function is just like 'xml', but produces expressions of type
    -- 'Document'.
    --
    -- If your input string cannot be parsed into a valid 'Document', an error will
    -- be thrown at runtime with 'error'.
    --
    -- This function is nice to use in GHCi or tests, but should __NOT__ be used in
    -- production code.
    --
    -- Here's a simple example of using it:
    --
    -- >>> [xmlUnsafe|<html></html>|] :: Document
    -- Document ...
    xmlUnsafe :: QuasiQuoter
    xmlUnsafe :: QuasiQuoter
    xmlUnsafe =
      (String -> Q Exp) -> QuasiQuoter
    createExpQuasiQuoter ((String -> Q Exp) -> QuasiQuoter)
    -> (String -> Q Exp) -> QuasiQuoter
    forall a b. (a -> b) -> a -> b
    $ \String
    string ->
        Q Exp -> Q Exp -> Q Exp
    forall (m :: * -> *). Quote m => m Exp -> m Exp -> m Exp
    appE
          [|fromEither (handleParseDocErr "XML" "Text.XML.parseText" string) .
            parseText def . renderMarkup|]
          (HeterocephalusSetting -> String -> Q Exp
    compileFromString HeterocephalusSetting
    textSetting String
    string)

    -- | This function is similar to 'xml', but doesn't allow variable interpolation
    -- or control statements.  It produces expressions of type 'Document'.
    --
    -- An error will be thrown at compile-time if the input string cannot be parsed
    -- into a 'Document'.
    --
    -- Unlike 'xmlUnsafe', this function is safe to use in production code.
    --
    -- Here's a simple example of using it:
    --
    -- >>> [xmlRaw|<html></html>|] :: Document
    -- Document ...
    xmlRaw :: QuasiQuoter
    xmlRaw :: QuasiQuoter
    xmlRaw =
      (String -> Q Exp) -> QuasiQuoter
    createExpQuasiQuoter ((String -> Q Exp) -> QuasiQuoter)
    -> (String -> Q Exp) -> QuasiQuoter
    forall a b. (a -> b) -> a -> b
    $ \String
    string ->
        let eitherDoc :: Either SomeException Document
    eitherDoc = ParseSettings -> Text -> Either SomeException Document
    parseText ParseSettings
    forall a. Default a => a
    def (Text -> Either SomeException Document)
    -> Text -> Either SomeException Document
    forall a b. (a -> b) -> a -> b
    $ String -> Text
    pack String
    string
        in (SomeException -> Q Exp)
    -> (Document -> Q Exp) -> Either SomeException Document -> Q Exp
    forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
    either
             (String -> String -> String -> SomeException -> Q Exp
    forall a. String -> String -> String -> SomeException -> a
    handleParseDocErr String
    "XML" String
    "Text.XML.parseText" String
    string)
             Document -> Q Exp
    forall t (m :: * -> *). (Lift t, Quote m) => t -> m Exp
    forall (m :: * -> *). Quote m => Document -> m Exp
    lift
             Either SomeException Document
    eitherDoc