packages feed

haskell-xmpp-2.0.0: src/Network/XMPP/XML.hs

{-# LANGUAGE OverloadedStrings #-}

-----------------------------------------------------------------------------
-- |
-- Copyright   :  (c) riskbook, 2020
-- SPDX-License-Identifier:  BSD3
--
-----------------------------------------------------------------------------
module Network.XMPP.XML
  ( strAttr
  , getVals
  , isVal
  , getText
  , getText_
  , txtpat
  , xtractp
  , matchPatterns
  , mread
  , mattr
  , mattr'
  , literal -- from HaXML
  , noelem
  , lookupAttr
  , FromXML(..)
  , ToXML(..)
  ) where

import           Text.XML                       (Node)
import           Text.XML.HaXml                 hiding (tag)
import           Text.XML.HaXml.Posn
import qualified Text.XML.HaXml.Pretty           as P
import           Text.XML.HaXml.Xtract.Parse     (xtract)
import           Text.PrettyPrint.HughesPJ       (hcat)
import           Data.Text                       (Text, pack, unpack)
import           Text.Read
import           Control.Applicative             ((<|>))

class FromXML a where
  decodeXml :: Content Posn -> Maybe a

class ToXML a where
  encodeXml :: a -> [Node]

instance FromXML () where
  decodeXml _ = Just ()

instance (FromXML a, FromXML b) => FromXML (Either a b) where
  decodeXml m = (Left <$> decodeXml m) <|> (Right <$> decodeXml m)

strAttr :: a -> String -> (a, CFilter i)
strAttr s d = (s, literal d)

-- | Returns strings extracted by xtract query
getVals :: Text -> [Content Posn] -> [Text]
getVals q = map (getText_ . xtract id (unpack q))

-- | Queries xml for specific value
-- @isVal str = any (== str) . getVals@
isVal :: Text -> Text -> [Content Posn] -> Bool
isVal str cont = any (== str) . getVals cont

-- 
getText :: Content i -> Text
getText cs@CString{} = pack . render . P.content $ cs
getText cs@CRef{}    = pack . render . P.content $ cs
getText x =
  error
    $  "Attempt to extract text from content that is not a string: "
    ++ render (P.content x)

getText_ :: [Content i] -> Text
getText_ = pack . render . hcat . map P.content

-- | Extract text from `Content Posn' with supplied pattern
txtpat :: Text      -- ^ xtract-like pattern to match
    -> Content Posn -- ^ message being processed
    -> Text         -- ^ result of extraction
txtpat p m = getText_ $ xtract id (unpack p) m

xtractp :: (Text -> Text) -> Text -> Content i -> Bool
xtractp f p m = not . null $ xtract (unpack . f . pack) (unpack p) m

matchPatterns :: Content i -> [Text] -> Bool
matchPatterns m = all $ flip (xtractp id) m

mread :: Read a => Text -> Maybe a
mread "" = Nothing
mread a = readMaybe $ unpack a

mattr :: (Show a) => b -> Maybe a -> [(b, CFilter i)]
mattr s (Just a) = [ strAttr s (show a) ]
mattr _ Nothing = []

mattr' :: a -> Maybe String -> [(a, CFilter i)]
mattr' s (Just a) = [ strAttr s a ]
mattr' _ Nothing = []

noelem :: Content Posn
noelem = CElem (Elem (N "root") [] []) noPos

lookupAttr :: String -> [Attribute] -> Maybe String
lookupAttr k lst = do
  x <- lookup (N k) lst
  case x of
    AttValue [Left str] -> Just str
    AttValue _          -> Nothing