{-# LANGUAGE OverloadedStrings #-}
module Swish.QName
( QName
, LName
, emptyLName
, newLName
, getLName
, newQName
, qnameFromURI
, getNamespace
, getLocalName
, getQNameURI
, qnameFromFilePath
)
where
import Data.Char (isAscii)
import Data.Maybe (fromMaybe)
import Data.Interned (intern, unintern)
import Data.Interned.URI (InternedURI)
import Data.Ord (comparing)
import Data.String (IsString(..))
import Network.URI (URI(..), URIAuth(..), parseURIReference)
import System.Directory (canonicalizePath)
import System.FilePath (splitFileName)
import qualified Data.Text as T
newtype LName = LName T.Text
deriving (LName -> LName -> Bool
(LName -> LName -> Bool) -> (LName -> LName -> Bool) -> Eq LName
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: LName -> LName -> Bool
$c/= :: LName -> LName -> Bool
== :: LName -> LName -> Bool
$c== :: LName -> LName -> Bool
Eq, Eq LName
Eq LName
-> (LName -> LName -> Ordering)
-> (LName -> LName -> Bool)
-> (LName -> LName -> Bool)
-> (LName -> LName -> Bool)
-> (LName -> LName -> Bool)
-> (LName -> LName -> LName)
-> (LName -> LName -> LName)
-> Ord LName
LName -> LName -> Bool
LName -> LName -> Ordering
LName -> LName -> LName
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: LName -> LName -> LName
$cmin :: LName -> LName -> LName
max :: LName -> LName -> LName
$cmax :: LName -> LName -> LName
>= :: LName -> LName -> Bool
$c>= :: LName -> LName -> Bool
> :: LName -> LName -> Bool
$c> :: LName -> LName -> Bool
<= :: LName -> LName -> Bool
$c<= :: LName -> LName -> Bool
< :: LName -> LName -> Bool
$c< :: LName -> LName -> Bool
compare :: LName -> LName -> Ordering
$ccompare :: LName -> LName -> Ordering
$cp1Ord :: Eq LName
Ord)
instance Show LName where
show :: LName -> String
show (LName Text
t) = Text -> String
forall a. Show a => a -> String
show Text
t
instance IsString LName where
fromString :: String -> LName
fromString String
s =
LName -> Maybe LName -> LName
forall a. a -> Maybe a -> a
fromMaybe (String -> LName
forall a. HasCallStack => String -> a
error (String
"Invalid local name: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
s)) (Maybe LName -> LName) -> Maybe LName -> LName
forall a b. (a -> b) -> a -> b
$
Text -> Maybe LName
newLName (String -> Text
T.pack String
s)
emptyLName :: LName
emptyLName :: LName
emptyLName = Text -> LName
LName Text
""
newLName :: T.Text -> Maybe LName
newLName :: Text -> Maybe LName
newLName Text
l = if (Char -> Bool) -> Text -> Bool
T.any (\Char
c -> Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
' ' Bool -> Bool -> Bool
|| Bool -> Bool
not (Char -> Bool
isAscii Char
c)) Text
l then Maybe LName
forall a. Maybe a
Nothing else LName -> Maybe LName
forall a. a -> Maybe a
Just (Text -> LName
LName Text
l)
getLName :: LName -> T.Text
getLName :: LName -> Text
getLName (LName Text
l) = Text
l
data QName = QName !InternedURI URI LName
instance IsString QName where
fromString :: String -> QName
fromString String
s =
QName -> Maybe QName -> QName
forall a. a -> Maybe a -> a
fromMaybe (String -> QName
forall a. HasCallStack => String -> a
error (String
"QName conversion given an invalid URI: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
s))
(String -> Maybe URI
parseURIReference String
s Maybe URI -> (URI -> Maybe QName) -> Maybe QName
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= URI -> Maybe QName
qnameFromURI)
instance Eq QName where
QName
u1 == :: QName -> QName -> Bool
== QName
u2 = QName -> URI
getQNameURI QName
u1 URI -> URI -> Bool
forall a. Eq a => a -> a -> Bool
== QName -> URI
getQNameURI QName
u2
instance Ord QName where
compare :: QName -> QName -> Ordering
compare = (QName -> URI) -> QName -> QName -> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing QName -> URI
getQNameURI
instance Show QName where
show :: QName -> String
show (QName InternedURI
u URI
_ LName
_) = String
"<" String -> ShowS
forall a. [a] -> [a] -> [a]
++ InternedURI -> String
forall a. Show a => a -> String
show InternedURI
u String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
">"
newQName ::
URI
-> LName
-> QName
newQName :: URI -> LName -> QName
newQName URI
ns l :: LName
l@(LName Text
local) =
let lstr :: String
lstr = Text -> String
T.unpack Text
local
uristr :: String
uristr = URI -> String
forall a. Show a => a -> String
show URI
ns String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
lstr
in case String -> Maybe URI
parseURIReference String
uristr of
Just URI
uri -> InternedURI -> URI -> LName -> QName
QName (Uninterned InternedURI -> InternedURI
forall t. Interned t => Uninterned t -> t
intern Uninterned InternedURI
URI
uri) URI
ns LName
l
Maybe URI
_ -> String -> QName
forall a. HasCallStack => String -> a
error (String -> QName) -> String -> QName
forall a b. (a -> b) -> a -> b
$ String
"Unable to combine " String -> ShowS
forall a. [a] -> [a] -> [a]
++ URI -> String
forall a. Show a => a -> String
show URI
ns String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" with " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
lstr
qnameFromURI ::
URI
-> Maybe QName
qnameFromURI :: URI -> Maybe QName
qnameFromURI URI
uri =
let uf :: String
uf = URI -> String
uriFragment URI
uri
up :: String
up = URI -> String
uriPath URI
uri
q0 :: Maybe QName
q0 = QName -> Maybe QName
forall a. a -> Maybe a
Just (QName -> Maybe QName) -> QName -> Maybe QName
forall a b. (a -> b) -> a -> b
$ URI -> LName -> QName
start URI
uri LName
emptyLName
start :: URI -> LName -> QName
start = InternedURI -> URI -> LName -> QName
QName (Uninterned InternedURI -> InternedURI
forall t. Interned t => Uninterned t -> t
intern Uninterned InternedURI
URI
uri)
in case String
uf of
String
"#" -> Maybe QName
q0
Char
'#':String
xs -> URI -> LName -> QName
start (URI
uri {uriFragment :: String
uriFragment = String
"#"}) (LName -> QName) -> Maybe LName -> Maybe QName
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` Text -> Maybe LName
newLName (String -> Text
T.pack String
xs)
String
"" -> case (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
==Char
'/') (ShowS
forall a. [a] -> [a]
reverse String
up) of
(String
"",String
_) -> Maybe QName
q0
(String
_,String
"") -> Maybe QName
q0
(String
rlname,String
rpath) ->
URI -> LName -> QName
start (URI
uri {uriPath :: String
uriPath = ShowS
forall a. [a] -> [a]
reverse String
rpath}) (LName -> QName) -> Maybe LName -> Maybe QName
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap`
Text -> Maybe LName
newLName (String -> Text
T.pack (ShowS
forall a. [a] -> [a]
reverse String
rlname))
String
_ -> Maybe QName
forall a. Maybe a
Nothing
getNamespace :: QName -> URI
getNamespace :: QName -> URI
getNamespace (QName InternedURI
_ URI
ns LName
_) = URI
ns
getLocalName :: QName -> LName
getLocalName :: QName -> LName
getLocalName (QName InternedURI
_ URI
_ LName
l) = LName
l
getQNameURI :: QName -> URI
getQNameURI :: QName -> URI
getQNameURI (QName InternedURI
u URI
_ LName
_) = InternedURI -> Uninterned InternedURI
forall t. Uninternable t => t -> Uninterned t
unintern InternedURI
u
qnameFromFilePath :: FilePath -> IO QName
qnameFromFilePath :: String -> IO QName
qnameFromFilePath String
fname = do
String
ipath <- String -> IO String
canonicalizePath String
fname
let (String
dname, String
lname) = String -> (String, String)
splitFileName String
ipath
nsuri :: URI
nsuri = String -> Maybe URIAuth -> String -> String -> String -> URI
URI String
"file:" Maybe URIAuth
emptyAuth String
dname String
"" String
""
uri :: URI
uri = String -> Maybe URIAuth -> String -> String -> String -> URI
URI String
"file:" Maybe URIAuth
emptyAuth String
ipath String
"" String
""
case String
lname of
String
"" -> QName -> IO QName
forall (m :: * -> *) a. Monad m => a -> m a
return (QName -> IO QName) -> QName -> IO QName
forall a b. (a -> b) -> a -> b
$ InternedURI -> URI -> LName -> QName
QName (Uninterned InternedURI -> InternedURI
forall t. Interned t => Uninterned t -> t
intern Uninterned InternedURI
URI
nsuri) URI
nsuri LName
emptyLName
String
_ -> QName -> IO QName
forall (m :: * -> *) a. Monad m => a -> m a
return (QName -> IO QName) -> QName -> IO QName
forall a b. (a -> b) -> a -> b
$ InternedURI -> URI -> LName -> QName
QName (Uninterned InternedURI -> InternedURI
forall t. Interned t => Uninterned t -> t
intern Uninterned InternedURI
URI
uri) URI
nsuri (Text -> LName
LName (String -> Text
T.pack String
lname))
emptyAuth :: Maybe URIAuth
emptyAuth :: Maybe URIAuth
emptyAuth = URIAuth -> Maybe URIAuth
forall a. a -> Maybe a
Just (URIAuth -> Maybe URIAuth) -> URIAuth -> Maybe URIAuth
forall a b. (a -> b) -> a -> b
$ String -> String -> String -> URIAuth
URIAuth String
"" String
"" String
""