hw-xml-0.5.1.2: src/HaskellWorks/Data/Xml/RawValue.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module HaskellWorks.Data.Xml.RawValue
( RawValue(..)
, RawValueAt(..)
) where
import Data.ByteString (ByteString)
import Data.List
import Data.Text (Text)
import HaskellWorks.Data.Xml.Grammar
import HaskellWorks.Data.Xml.Internal.Show
import HaskellWorks.Data.Xml.Succinct.Index
import Prettyprinter
import qualified Data.Attoparsec.ByteString.Char8 as ABC
import qualified Data.ByteString as BS
import qualified Data.Text as T
data RawValue
= RawDocument [RawValue]
| RawText Text
| RawElement Text [RawValue]
| RawCData Text
| RawComment Text
| RawMeta Text [RawValue]
| RawAttrName Text
| RawAttrValue Text
| RawAttrList [RawValue]
| RawError Text
deriving (Eq, Show)
-- TODO use colors and styles
red :: Doc ann -> Doc ann
red = id
dullwhite :: Doc ann -> Doc ann
dullwhite = id
bold :: Doc ann -> Doc ann
bold = id
dullgreen :: Doc ann -> Doc ann
dullgreen = id
instance Pretty RawValue where
pretty mjpv = case mjpv of
RawText s -> ctext $ pretty (T.unpack s)
RawAttrName s -> pretty (T.unpack s)
RawAttrValue s -> (ctext . dquotes . pretty) (T.unpack s)
RawAttrList ats -> formatAttrs ats
RawComment s -> pretty $ "<!-- " <> show s <> "-->"
RawElement s xs -> formatElem (T.unpack s) xs
RawDocument xs -> formatMeta "?" "xml" xs
RawError s -> red $ "[error " <> pretty (T.unpack s) <> "]"
RawCData s -> cangle "<!" <> ctag "[CDATA[" <> pretty (T.unpack s) <> cangle "]]>"
RawMeta s xs -> formatMeta "!" (T.unpack s) xs
where
formatAttr at = case at of
RawAttrName a -> " " <> pretty (RawAttrName a)
RawAttrValue a -> "=" <> pretty (RawAttrValue a)
RawAttrList _ -> red "ATTRS"
_ -> red "booo"
formatAttrs ats = hcat (formatAttr <$> ats)
formatElem s xs =
let (ats, es) = partition isAttrL xs
in cangle langle <> ctag (pretty s)
<> hcat (pretty <$> ats)
<> cangle rangle
<> hcat (pretty <$> es)
<> cangle "</" <> ctag (pretty s) <> cangle rangle
formatMeta b s xs =
let (ats, es) = partition isAttr xs
in cangle (langle <> pretty @String b) <> ctag (pretty @String s)
<> hcat (pretty <$> ats)
<> cangle rangle
<> hcat (pretty <$> es)
class RawValueAt a where
rawValueAt :: a -> RawValue
instance RawValueAt XmlIndex where
rawValueAt i = case i of
XmlIndexCData s -> parseTextUntil "]]>" s `as` (RawCData . T.pack)
XmlIndexComment s -> parseTextUntil "-->" s `as` (RawComment . T.pack)
XmlIndexMeta s cs -> RawMeta s (rawValueAt <$> cs)
XmlIndexElement s cs -> RawElement s (rawValueAt <$> cs)
XmlIndexDocument cs -> RawDocument (rawValueAt <$> cs)
XmlIndexAttrName cs -> parseAttrName cs `as` RawAttrName
XmlIndexAttrValue cs -> parseString cs `as` RawAttrValue
XmlIndexAttrList cs -> RawAttrList (rawValueAt <$> cs)
XmlIndexValue s -> parseTextUntil "<" s `as` (RawText . T.pack)
XmlIndexError s -> RawError s
--unknown -> XmlError ("Not yet supported: " <> show unknown)
where
parseUntil s = ABC.manyTill ABC.anyChar (ABC.string s)
parseTextUntil :: ByteString -> ByteString -> Either Text [Char]
parseTextUntil s bs = case ABC.parse (parseUntil s) bs of
ABC.Fail {} -> decodeErr ("Unable to find " <> tshow s <> ".") bs
ABC.Partial _ -> decodeErr ("Unexpected end, expected " <> tshow s <> ".") bs
ABC.Done _ r -> Right r
parseString :: ByteString -> Either Text Text
parseString bs = case ABC.parse parseXmlString bs of
ABC.Fail {} -> decodeErr "Unable to parse string" bs
ABC.Partial _ -> decodeErr "Unexpected end of string, expected" bs
ABC.Done _ r -> Right r
parseAttrName :: ByteString -> Either Text Text
parseAttrName bs = case ABC.parse parseXmlAttributeName bs of
ABC.Fail {} -> decodeErr "Unable to parse attribute name" bs
ABC.Partial _ -> decodeErr "Unexpected end of attr name, expected" bs
ABC.Done _ r -> Right r
cangle :: Doc ann -> Doc ann
cangle = dullwhite
ctag :: Doc ann -> Doc ann
ctag = bold
ctext :: Doc ann -> Doc ann
ctext = dullgreen
isAttrL :: RawValue -> Bool
isAttrL (RawAttrList _) = True
isAttrL _ = False
isAttr :: RawValue -> Bool
isAttr v = case v of
RawAttrName _ -> True
RawAttrValue _ -> True
RawAttrList _ -> True
_ -> False
as :: Either Text a -> (a -> RawValue) -> RawValue
as = flip $ either RawError
decodeErr :: Text -> BS.ByteString -> Either Text a
decodeErr reason bs = Left $ reason <> " (" <> tshow (BS.take 20 bs) <> "...)"