{-# LANGUAGE RecordWildCards, BangPatterns #-}
module Text.XML.Hexml(
Node, Attribute(..),
parse, render,
location, name, inner, outer,
attributes, children, contents,
attributeBy, childrenBy
) where
import Control.Applicative
import Control.Monad
import Data.Int
import Foreign.C
import Foreign.Ptr
import Foreign.Marshal hiding (void)
import Foreign.ForeignPtr
import Foreign.Storable
import System.IO.Unsafe
import Data.Monoid
import Data.Tuple.Extra
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Unsafe as BS
import qualified Data.ByteString.Internal as BS
import Prelude
data CDocument
data CNode
data CAttr
szAttr :: Int
szAttr = Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Str -> Int
forall a. Storable a => a -> Int
sizeOf (Str
forall a. HasCallStack => a
undefined :: Str)
szNode :: Int
szNode = Int
5 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Str -> Int
forall a. Storable a => a -> Int
sizeOf (Str
forall a. HasCallStack => a
undefined :: Str)
data Str = Str {Str -> Int32
strStart :: {-# UNPACK #-} !Int32, Str -> Int32
strLength :: {-# UNPACK #-} !Int32} deriving Int -> Str -> ShowS
[Str] -> ShowS
Str -> String
(Int -> Str -> ShowS)
-> (Str -> String) -> ([Str] -> ShowS) -> Show Str
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Str -> ShowS
showsPrec :: Int -> Str -> ShowS
$cshow :: Str -> String
show :: Str -> String
$cshowList :: [Str] -> ShowS
showList :: [Str] -> ShowS
Show
strEnd :: Str -> Int32
strEnd :: Str -> Int32
strEnd Str{Int32
strStart :: Str -> Int32
strLength :: Str -> Int32
strStart :: Int32
strLength :: Int32
..} = Int32
strStart Int32 -> Int32 -> Int32
forall a. Num a => a -> a -> a
+ Int32
strLength
instance Storable Str where
sizeOf :: Str -> Int
sizeOf Str
_ = Int
8
alignment :: Str -> Int
alignment Str
_ = Int64 -> Int
forall a. Storable a => a -> Int
alignment (Int64
0 :: Int64)
peek :: Ptr Str -> IO Str
peek Ptr Str
p = Int32 -> Int32 -> Str
Str (Int32 -> Int32 -> Str) -> IO Int32 -> IO (Int32 -> Str)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr Str -> Int -> IO Int32
forall b. Ptr b -> Int -> IO Int32
forall a b. Storable a => Ptr b -> Int -> IO a
peekByteOff Ptr Str
p Int
0 IO (Int32 -> Str) -> IO Int32 -> IO Str
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Ptr Str -> Int -> IO Int32
forall b. Ptr b -> Int -> IO Int32
forall a b. Storable a => Ptr b -> Int -> IO a
peekByteOff Ptr Str
p Int
4
poke :: Ptr Str -> Str -> IO ()
poke Ptr Str
p Str{Int32
strStart :: Str -> Int32
strLength :: Str -> Int32
strStart :: Int32
strLength :: Int32
..} = Ptr Str -> Int -> Int32 -> IO ()
forall b. Ptr b -> Int -> Int32 -> IO ()
forall a b. Storable a => Ptr b -> Int -> a -> IO ()
pokeByteOff Ptr Str
p Int
0 Int32
strStart IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Ptr Str -> Int -> Int32 -> IO ()
forall b. Ptr b -> Int -> Int32 -> IO ()
forall a b. Storable a => Ptr b -> Int -> a -> IO ()
pokeByteOff Ptr Str
p Int
4 Int32
strLength
foreign import ccall hexml_document_parse :: CString -> CInt -> IO (Ptr CDocument)
foreign import ccall hexml_document_free :: Ptr CDocument -> IO ()
foreign import ccall "&hexml_document_free" hexml_document_free_funptr :: FunPtr (Ptr CDocument -> IO ())
foreign import ccall hexml_node_render :: Ptr CDocument -> Ptr CNode -> CString -> CInt -> IO CInt
foreign import ccall unsafe hexml_document_error :: Ptr CDocument -> IO CString
foreign import ccall unsafe hexml_document_node :: Ptr CDocument -> IO (Ptr CNode)
foreign import ccall unsafe hexml_node_children :: Ptr CDocument -> Ptr CNode -> Ptr CInt -> IO (Ptr CNode)
foreign import ccall unsafe hexml_node_attributes :: Ptr CDocument -> Ptr CNode -> Ptr CInt -> IO (Ptr CAttr)
foreign import ccall unsafe hexml_node_child :: Ptr CDocument -> Ptr CNode -> Ptr CNode -> CString -> CInt -> IO (Ptr CNode)
foreign import ccall unsafe hexml_node_attribute :: Ptr CDocument -> Ptr CNode -> CString -> CInt -> IO (Ptr CAttr)
data Node = Node BS.ByteString (ForeignPtr CDocument) (Ptr CNode)
data Attribute = Attribute
{Attribute -> ByteString
attributeName :: BS.ByteString
,Attribute -> ByteString
attributeValue :: BS.ByteString
} deriving (Int -> Attribute -> ShowS
[Attribute] -> ShowS
Attribute -> String
(Int -> Attribute -> ShowS)
-> (Attribute -> String)
-> ([Attribute] -> ShowS)
-> Show Attribute
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Attribute -> ShowS
showsPrec :: Int -> Attribute -> ShowS
$cshow :: Attribute -> String
show :: Attribute -> String
$cshowList :: [Attribute] -> ShowS
showList :: [Attribute] -> ShowS
Show, Attribute -> Attribute -> Bool
(Attribute -> Attribute -> Bool)
-> (Attribute -> Attribute -> Bool) -> Eq Attribute
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Attribute -> Attribute -> Bool
== :: Attribute -> Attribute -> Bool
$c/= :: Attribute -> Attribute -> Bool
/= :: Attribute -> Attribute -> Bool
Eq, Eq Attribute
Eq Attribute =>
(Attribute -> Attribute -> Ordering)
-> (Attribute -> Attribute -> Bool)
-> (Attribute -> Attribute -> Bool)
-> (Attribute -> Attribute -> Bool)
-> (Attribute -> Attribute -> Bool)
-> (Attribute -> Attribute -> Attribute)
-> (Attribute -> Attribute -> Attribute)
-> Ord Attribute
Attribute -> Attribute -> Bool
Attribute -> Attribute -> Ordering
Attribute -> Attribute -> Attribute
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
$ccompare :: Attribute -> Attribute -> Ordering
compare :: Attribute -> Attribute -> Ordering
$c< :: Attribute -> Attribute -> Bool
< :: Attribute -> Attribute -> Bool
$c<= :: Attribute -> Attribute -> Bool
<= :: Attribute -> Attribute -> Bool
$c> :: Attribute -> Attribute -> Bool
> :: Attribute -> Attribute -> Bool
$c>= :: Attribute -> Attribute -> Bool
>= :: Attribute -> Attribute -> Bool
$cmax :: Attribute -> Attribute -> Attribute
max :: Attribute -> Attribute -> Attribute
$cmin :: Attribute -> Attribute -> Attribute
min :: Attribute -> Attribute -> Attribute
Ord)
instance Show Node where
show :: Node -> String
show Node
d = String
"Node " String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
forall a. Show a => a -> String
show (ByteString -> String
BS.unpack (ByteString -> String) -> ByteString -> String
forall a b. (a -> b) -> a -> b
$ Node -> ByteString
outer Node
d)
touchBS :: BS.ByteString -> IO ()
touchBS :: ByteString -> IO ()
touchBS = ForeignPtr Word8 -> IO ()
forall a. ForeignPtr a -> IO ()
touchForeignPtr (ForeignPtr Word8 -> IO ())
-> (ByteString -> ForeignPtr Word8) -> ByteString -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ForeignPtr Word8, Int, Int) -> ForeignPtr Word8
forall a b c. (a, b, c) -> a
fst3 ((ForeignPtr Word8, Int, Int) -> ForeignPtr Word8)
-> (ByteString -> (ForeignPtr Word8, Int, Int))
-> ByteString
-> ForeignPtr Word8
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> (ForeignPtr Word8, Int, Int)
BS.toForeignPtr
parse :: BS.ByteString -> Either BS.ByteString Node
parse :: ByteString -> Either ByteString Node
parse ByteString
src = do
let src0 :: ByteString
src0 = ByteString
src ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> Char -> ByteString
BS.singleton Char
'\0'
IO (Either ByteString Node) -> Either ByteString Node
forall a. IO a -> a
unsafePerformIO (IO (Either ByteString Node) -> Either ByteString Node)
-> IO (Either ByteString Node) -> Either ByteString Node
forall a b. (a -> b) -> a -> b
$ ByteString
-> (CStringLen -> IO (Either ByteString Node))
-> IO (Either ByteString Node)
forall a. ByteString -> (CStringLen -> IO a) -> IO a
BS.unsafeUseAsCStringLen ByteString
src0 ((CStringLen -> IO (Either ByteString Node))
-> IO (Either ByteString Node))
-> (CStringLen -> IO (Either ByteString Node))
-> IO (Either ByteString Node)
forall a b. (a -> b) -> a -> b
$ \(CString
str, Int
len) -> do
doc <- CString -> CInt -> IO (Ptr CDocument)
hexml_document_parse CString
str (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len CInt -> CInt -> CInt
forall a. Num a => a -> a -> a
- CInt
1)
err <- hexml_document_error doc
if err /= nullPtr then do
bs <- BS.packCString =<< hexml_document_error doc
hexml_document_free doc
pure $ Left bs
else do
node <- hexml_document_node doc
doc <- newForeignPtr hexml_document_free_funptr doc
pure $ Right $ Node src0 doc node
render :: Node -> BS.ByteString
render :: Node -> ByteString
render (Node ByteString
src ForeignPtr CDocument
doc Ptr CNode
n) = IO ByteString -> ByteString
forall a. IO a -> a
unsafePerformIO (IO ByteString -> ByteString) -> IO ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ ForeignPtr CDocument
-> (Ptr CDocument -> IO ByteString) -> IO ByteString
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr CDocument
doc ((Ptr CDocument -> IO ByteString) -> IO ByteString)
-> (Ptr CDocument -> IO ByteString) -> IO ByteString
forall a b. (a -> b) -> a -> b
$ \Ptr CDocument
d -> do
i <- Ptr CDocument -> Ptr CNode -> CString -> CInt -> IO CInt
hexml_node_render Ptr CDocument
d Ptr CNode
n CString
forall a. Ptr a
nullPtr CInt
0
res <- BS.create (fromIntegral i) $ \Ptr Word8
ptr -> IO CInt -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO CInt -> IO ()) -> IO CInt -> IO ()
forall a b. (a -> b) -> a -> b
$ Ptr CDocument -> Ptr CNode -> CString -> CInt -> IO CInt
hexml_node_render Ptr CDocument
d Ptr CNode
n (Ptr Word8 -> CString
forall a b. Ptr a -> Ptr b
castPtr Ptr Word8
ptr) CInt
i
touchBS src
pure res
applyStr :: BS.ByteString -> Str -> BS.ByteString
applyStr :: ByteString -> Str -> ByteString
applyStr ByteString
bs Str{Int32
strStart :: Str -> Int32
strLength :: Str -> Int32
strStart :: Int32
strLength :: Int32
..} = Int -> ByteString -> ByteString
BS.take (Int32 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
strLength) (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Int -> ByteString -> ByteString
BS.drop (Int32 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
strStart) ByteString
bs
nodeStr :: Int -> Node -> Str
nodeStr :: Int -> Node -> Str
nodeStr Int
i (Node ByteString
src ForeignPtr CDocument
doc Ptr CNode
n) = IO Str -> Str
forall a. IO a -> a
unsafePerformIO (IO Str -> Str) -> IO Str -> Str
forall a b. (a -> b) -> a -> b
$ ForeignPtr CDocument -> (Ptr CDocument -> IO Str) -> IO Str
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr CDocument
doc ((Ptr CDocument -> IO Str) -> IO Str)
-> (Ptr CDocument -> IO Str) -> IO Str
forall a b. (a -> b) -> a -> b
$ \Ptr CDocument
_ -> Ptr Str -> Int -> IO Str
forall a. Storable a => Ptr a -> Int -> IO a
peekElemOff (Ptr CNode -> Ptr Str
forall a b. Ptr a -> Ptr b
castPtr Ptr CNode
n) Int
i
nodeBS :: Int -> Node -> BS.ByteString
nodeBS :: Int -> Node -> ByteString
nodeBS Int
i node :: Node
node@(Node ByteString
src ForeignPtr CDocument
doc Ptr CNode
n) = ByteString -> Str -> ByteString
applyStr ByteString
src (Str -> ByteString) -> Str -> ByteString
forall a b. (a -> b) -> a -> b
$ Int -> Node -> Str
nodeStr Int
i Node
node
attrPeek :: BS.ByteString -> ForeignPtr CDocument -> Ptr CAttr -> Attribute
attrPeek :: ByteString -> ForeignPtr CDocument -> Ptr CAttr -> Attribute
attrPeek ByteString
src ForeignPtr CDocument
doc Ptr CAttr
a = IO Attribute -> Attribute
forall a. IO a -> a
unsafePerformIO (IO Attribute -> Attribute) -> IO Attribute -> Attribute
forall a b. (a -> b) -> a -> b
$ ForeignPtr CDocument
-> (Ptr CDocument -> IO Attribute) -> IO Attribute
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr CDocument
doc ((Ptr CDocument -> IO Attribute) -> IO Attribute)
-> (Ptr CDocument -> IO Attribute) -> IO Attribute
forall a b. (a -> b) -> a -> b
$ \Ptr CDocument
_ -> do
name <- ByteString -> Str -> ByteString
applyStr ByteString
src (Str -> ByteString) -> IO Str -> IO ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr Str -> Int -> IO Str
forall a. Storable a => Ptr a -> Int -> IO a
peekElemOff (Ptr CAttr -> Ptr Str
forall a b. Ptr a -> Ptr b
castPtr Ptr CAttr
a) Int
0
val <- applyStr src <$> peekElemOff (castPtr a) 1
pure $ Attribute name val
name :: Node -> BS.ByteString
name :: Node -> ByteString
name = Int -> Node -> ByteString
nodeBS Int
0
inner :: Node -> BS.ByteString
inner :: Node -> ByteString
inner = Int -> Node -> ByteString
nodeBS Int
1
outer :: Node -> BS.ByteString
outer :: Node -> ByteString
outer = Int -> Node -> ByteString
nodeBS Int
2
contents :: Node -> [Either BS.ByteString Node]
contents :: Node -> [Either ByteString Node]
contents n :: Node
n@(Node ByteString
src ForeignPtr CDocument
_ Ptr CNode
_) = Int32 -> [(Str, Node)] -> [Either ByteString Node]
forall {b}. Int32 -> [(Str, b)] -> [Either ByteString b]
f (Str -> Int32
strStart Str
inner) [(Str, Node)]
outers
where
f :: Int32 -> [(Str, b)] -> [Either ByteString b]
f Int32
i [] = Int32 -> Int32 -> [Either ByteString b]
forall {b}. Int32 -> Int32 -> [Either ByteString b]
string Int32
i (Str -> Int32
strEnd Str
inner)
f Int32
i ((Str
x, b
n):[(Str, b)]
xs) = Int32 -> Int32 -> [Either ByteString b]
forall {b}. Int32 -> Int32 -> [Either ByteString b]
string Int32
i (Str -> Int32
strStart Str
x) [Either ByteString b]
-> [Either ByteString b] -> [Either ByteString b]
forall a. [a] -> [a] -> [a]
++ b -> Either ByteString b
forall a b. b -> Either a b
Right b
n Either ByteString b
-> [Either ByteString b] -> [Either ByteString b]
forall a. a -> [a] -> [a]
: Int32 -> [(Str, b)] -> [Either ByteString b]
f (Str -> Int32
strEnd Str
x) [(Str, b)]
xs
string :: Int32 -> Int32 -> [Either ByteString b]
string Int32
start Int32
end | Int32
start Int32 -> Int32 -> Bool
forall a. Eq a => a -> a -> Bool
== Int32
end = []
| Bool
otherwise = [ByteString -> Either ByteString b
forall a b. a -> Either a b
Left (ByteString -> Either ByteString b)
-> ByteString -> Either ByteString b
forall a b. (a -> b) -> a -> b
$ ByteString -> Str -> ByteString
applyStr ByteString
src (Str -> ByteString) -> Str -> ByteString
forall a b. (a -> b) -> a -> b
$ Int32 -> Int32 -> Str
Str Int32
start (Int32
end Int32 -> Int32 -> Int32
forall a. Num a => a -> a -> a
- Int32
start)]
inner :: Str
inner = Int -> Node -> Str
nodeStr Int
1 Node
n
outers :: [(Str, Node)]
outers = (Node -> (Str, Node)) -> [Node] -> [(Str, Node)]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> Node -> Str
nodeStr Int
2 (Node -> Str) -> (Node -> Node) -> Node -> (Str, Node)
forall a b c. (a -> b) -> (a -> c) -> a -> (b, c)
&&& Node -> Node
forall a. a -> a
id) ([Node] -> [(Str, Node)]) -> [Node] -> [(Str, Node)]
forall a b. (a -> b) -> a -> b
$ Node -> [Node]
children Node
n
children :: Node -> [Node]
children :: Node -> [Node]
children (Node ByteString
src ForeignPtr CDocument
doc Ptr CNode
n) = IO [Node] -> [Node]
forall a. IO a -> a
unsafePerformIO (IO [Node] -> [Node]) -> IO [Node] -> [Node]
forall a b. (a -> b) -> a -> b
$ ForeignPtr CDocument -> (Ptr CDocument -> IO [Node]) -> IO [Node]
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr CDocument
doc ((Ptr CDocument -> IO [Node]) -> IO [Node])
-> (Ptr CDocument -> IO [Node]) -> IO [Node]
forall a b. (a -> b) -> a -> b
$ \Ptr CDocument
d ->
(Ptr CInt -> IO [Node]) -> IO [Node]
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CInt -> IO [Node]) -> IO [Node])
-> (Ptr CInt -> IO [Node]) -> IO [Node]
forall a b. (a -> b) -> a -> b
$ \Ptr CInt
count -> do
res <- Ptr CDocument -> Ptr CNode -> Ptr CInt -> IO (Ptr CNode)
hexml_node_children Ptr CDocument
d Ptr CNode
n Ptr CInt
count
count <- fromIntegral <$> peek count
pure [Node src doc $ plusPtr res $ i*szNode | i <- [0..count-1]]
attributes :: Node -> [Attribute]
attributes :: Node -> [Attribute]
attributes (Node ByteString
src ForeignPtr CDocument
doc Ptr CNode
n) = IO [Attribute] -> [Attribute]
forall a. IO a -> a
unsafePerformIO (IO [Attribute] -> [Attribute]) -> IO [Attribute] -> [Attribute]
forall a b. (a -> b) -> a -> b
$ ForeignPtr CDocument
-> (Ptr CDocument -> IO [Attribute]) -> IO [Attribute]
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr CDocument
doc ((Ptr CDocument -> IO [Attribute]) -> IO [Attribute])
-> (Ptr CDocument -> IO [Attribute]) -> IO [Attribute]
forall a b. (a -> b) -> a -> b
$ \Ptr CDocument
d ->
(Ptr CInt -> IO [Attribute]) -> IO [Attribute]
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CInt -> IO [Attribute]) -> IO [Attribute])
-> (Ptr CInt -> IO [Attribute]) -> IO [Attribute]
forall a b. (a -> b) -> a -> b
$ \Ptr CInt
count -> do
res <- Ptr CDocument -> Ptr CNode -> Ptr CInt -> IO (Ptr CAttr)
hexml_node_attributes Ptr CDocument
d Ptr CNode
n Ptr CInt
count
count <- fromIntegral <$> peek count
pure [attrPeek src doc $ plusPtr res $ i*szAttr | i <- [0..count-1]]
childrenBy :: Node -> BS.ByteString -> [Node]
childrenBy :: Node -> ByteString -> [Node]
childrenBy (Node ByteString
src ForeignPtr CDocument
doc Ptr CNode
n) ByteString
str = Ptr CNode -> [Node]
go Ptr CNode
forall a. Ptr a
nullPtr
where
go :: Ptr CNode -> [Node]
go Ptr CNode
old = IO [Node] -> [Node]
forall a. IO a -> a
unsafePerformIO (IO [Node] -> [Node]) -> IO [Node] -> [Node]
forall a b. (a -> b) -> a -> b
$ ForeignPtr CDocument -> (Ptr CDocument -> IO [Node]) -> IO [Node]
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr CDocument
doc ((Ptr CDocument -> IO [Node]) -> IO [Node])
-> (Ptr CDocument -> IO [Node]) -> IO [Node]
forall a b. (a -> b) -> a -> b
$ \Ptr CDocument
d ->
ByteString -> (CStringLen -> IO [Node]) -> IO [Node]
forall a. ByteString -> (CStringLen -> IO a) -> IO a
BS.unsafeUseAsCStringLen ByteString
str ((CStringLen -> IO [Node]) -> IO [Node])
-> (CStringLen -> IO [Node]) -> IO [Node]
forall a b. (a -> b) -> a -> b
$ \(CString
bs, Int
len) -> do
r <- Ptr CDocument
-> Ptr CNode -> Ptr CNode -> CString -> CInt -> IO (Ptr CNode)
hexml_node_child Ptr CDocument
d Ptr CNode
n Ptr CNode
old CString
bs (CInt -> IO (Ptr CNode)) -> CInt -> IO (Ptr CNode)
forall a b. (a -> b) -> a -> b
$ Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len
touchBS src
pure $ if r == nullPtr then [] else Node src doc r : go r
attributeBy :: Node -> BS.ByteString -> Maybe Attribute
attributeBy :: Node -> ByteString -> Maybe Attribute
attributeBy (Node ByteString
src ForeignPtr CDocument
doc Ptr CNode
n) ByteString
str = IO (Maybe Attribute) -> Maybe Attribute
forall a. IO a -> a
unsafePerformIO (IO (Maybe Attribute) -> Maybe Attribute)
-> IO (Maybe Attribute) -> Maybe Attribute
forall a b. (a -> b) -> a -> b
$ ForeignPtr CDocument
-> (Ptr CDocument -> IO (Maybe Attribute)) -> IO (Maybe Attribute)
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr CDocument
doc ((Ptr CDocument -> IO (Maybe Attribute)) -> IO (Maybe Attribute))
-> (Ptr CDocument -> IO (Maybe Attribute)) -> IO (Maybe Attribute)
forall a b. (a -> b) -> a -> b
$ \Ptr CDocument
d ->
ByteString
-> (CStringLen -> IO (Maybe Attribute)) -> IO (Maybe Attribute)
forall a. ByteString -> (CStringLen -> IO a) -> IO a
BS.unsafeUseAsCStringLen ByteString
str ((CStringLen -> IO (Maybe Attribute)) -> IO (Maybe Attribute))
-> (CStringLen -> IO (Maybe Attribute)) -> IO (Maybe Attribute)
forall a b. (a -> b) -> a -> b
$ \(CString
bs, Int
len) -> do
r <- Ptr CDocument -> Ptr CNode -> CString -> CInt -> IO (Ptr CAttr)
hexml_node_attribute Ptr CDocument
d Ptr CNode
n CString
bs (CInt -> IO (Ptr CAttr)) -> CInt -> IO (Ptr CAttr)
forall a b. (a -> b) -> a -> b
$ Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len
touchBS src
pure $ if r == nullPtr then Nothing else Just $ attrPeek src doc r
location :: Node -> (Int, Int)
location :: Node -> (Int, Int)
location n :: Node
n@(Node ByteString
src ForeignPtr CDocument
_ Ptr CNode
_) = ((Int, Int) -> Char -> (Int, Int))
-> (Int, Int) -> ByteString -> (Int, Int)
forall a. (a -> Char -> a) -> a -> ByteString -> a
BS.foldl' (Int, Int) -> Char -> (Int, Int)
forall {a} {b}. (Num a, Num b) => (a, b) -> Char -> (a, b)
f (Int -> Int -> (Int, Int)
forall {a} {b}. a -> b -> (a, b)
pair Int
1 Int
1) (ByteString -> (Int, Int)) -> ByteString -> (Int, Int)
forall a b. (a -> b) -> a -> b
$ Int -> ByteString -> ByteString
BS.take (Int32 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
i) ByteString
src
where
pair :: a -> b -> (a, b)
pair !a
a !b
b = (a
a,b
b)
i :: Int32
i = Str -> Int32
strStart (Str -> Int32) -> Str -> Int32
forall a b. (a -> b) -> a -> b
$ Int -> Node -> Str
nodeStr Int
2 Node
n
f :: (a, b) -> Char -> (a, b)
f (!a
line, !b
col) Char
c
| Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'\n' = a -> b -> (a, b)
forall {a} {b}. a -> b -> (a, b)
pair (a
linea -> a -> a
forall a. Num a => a -> a -> a
+a
1) b
1
| Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'\t' = a -> b -> (a, b)
forall {a} {b}. a -> b -> (a, b)
pair a
line (b
colb -> b -> b
forall a. Num a => a -> a -> a
+b
8)
| Bool
otherwise = a -> b -> (a, b)
forall {a} {b}. a -> b -> (a, b)
pair a
line (b
colb -> b -> b
forall a. Num a => a -> a -> a
+b
1)