X-0.3.1.0: src/Text/XML/Types/Internal.hs
{-# LANGUAGE FlexibleInstances #-}
{-
Copyright (c) 2019 Herbert Valerio Riedel <hvr@gnu.org>
This file is free software: you may copy, redistribute and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
This file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program (see `LICENSE.GPLv3`). If not, see
<https://www.gnu.org/licenses/gpl-3.0.html>.
-}
-- |
-- Module : Text.XML.Types.Internal
-- Copyright : (c) Herbert Valerio Riedel 2019
-- SPDX-License-Identifier: GPL-3.0-or-later
--
-- @since 0.3
module Text.XML.Types.Internal where
import Common
import qualified Data.Text as T
import qualified Data.Text.Short as TS
import Text.XML.Types.Core
import Utils
-- | Convenience class for converting to/from 'Content' values
--
-- @since 0.3.0
class IsContent x where
-- | /upcast/ or generalize to 'Content'
toContent :: x -> Content
-- | /downcast/ or specialize (if possible) to a specific 'Content' subtype
fromContent :: Content -> Maybe x
-- | (currently private) Specialize to an 'Element' if possible
--
-- This method is included in this class as an optimization over the default impl below
toElem :: x -> Maybe Element
toElem = fromContent . toContent
----------------------------------------------------------------------------
-- trivial instance
instance IsContent Content where
toContent = id
fromContent = Just
toElem (Elem el) = Just el
toElem _ = Nothing
----------------------------------------------------------------------------
-- primitive instances
instance IsContent Element where
toContent = Elem
fromContent (Elem x) = Just x
fromContent _ = Nothing
toElem = Just
instance IsContent Comment where
toContent = Comm
fromContent (Comm x) = Just x
fromContent _ = Nothing
toElem _ = Nothing
instance IsContent PI where
toContent = Proc
fromContent (Proc x) = Just x
fromContent _ = Nothing
toElem _ = Nothing
instance IsContent CData where
toContent = Text
fromContent (Text x) = Just x
fromContent _ = Nothing
toElem _ = Nothing
----------------------------------------------------------------------------
-- | Convenient for e.g. 'MiscNodes'
instance (IsContent l, IsContent r) => IsContent (Either l r) where
toContent = either toContent toContent
fromContent c = (Left <$> fromContent c) <|> (Right <$> fromContent c)
toElem = either toElem toElem
-- | Convert a 'QName' to its text-representation, i.e.
--
-- > QName ::= PrefixedName | UnprefixedName
-- > PrefixedName ::= Prefix ':' LocalPart
-- > UnprefixedName ::= LocalPart
-- > Prefix ::= NCName
-- > LocalPart ::= NCName
--
-- See also 'NCName'
--
-- >>> qnameToText (QName "foo" "urn:example.org:bar" (Just "doo"))
-- "doo:foo"
--
-- >>> qnameToText (QName "foo" "urn:example.org:bar" Nothing)
-- "foo"
--
-- See also 'qnameFromText'
--
-- @since 0.3.1
qnameToText :: QName -> Text
qnameToText (QName (LName ln) _uri Nothing) = TS.toText ln
qnameToText (QName (LName ln) _uri (Just pfx)) = TS.toText (mconcat [pfx, TS.singleton ':', ln])
-- | Decode a 'QName' from its text-representation (see 'qnameToText')
--
-- This is the inverse to the 'qnameToText' function. However,
-- 'qnameToText' is a lossy conversion, therefore this function needs
-- to reconstruct the namespace (i.e. 'qURI') with the help of a
-- lookup function provided in the first argument: The lookup
-- functions takes a 'ShortText' which can be either
--
-- - the empty string (i.e. @""@) which denotes an unprefixed name, or
-- - a non-empty @NCName@ string which denotes a prefixed name.
--
-- The result of this function shall be the respective namespace 'URI'
-- to associate with this QName. An empty 'URI' may be returned In
-- case of unprefixed names to denote the name being in no namespace.
--
-- Finally, this function returns 'Nothing' in case of syntax errors
-- or when the prefix lookup function returns an empty 'URI' (see
-- 'isNullURI') for a /prefixed/ name.
--
-- @since 0.3.1
qnameFromText :: (ShortText -> URI) -> Text -> Maybe QName
qnameFromText nslup txt
= case T.split (==':') txt of
[ln] | isName ln -> Just (QName (LName (TS.fromText ln)) (nslup mempty) Nothing)
[ln,pfx] | isName ln, isName pfx -> do
let pfx' = TS.fromText pfx
uri = nslup pfx'
guard (not (isNullURI uri))
pure (QName (LName (TS.fromText ln)) uri (Just pfx'))
_ -> Nothing
where
isName t
| Just (c,t') <- T.uncons t = isNameStartChar c && T.all isNameChar t'
| otherwise = False