{-# LANGUAGE OverloadedStrings #-}
module CExports(c_newSession) where
-- For c_newSession
import Network.URI.Fetch (Session, newSession)
-- For data Page
import StyleTree (StyleTree)
import Data.CSS.Preprocessor.Conditions (ConditionalStyles, conditionalStyles)
import Text.XML
import qualified Data.Map.Strict as M
import Network.URI
import Control.Monad (forM)
-- For c_fetchURLs
import System.Directory (getCurrentDirectory) -- default referer URI
import Control.Concurrent.Async (forConcurrently, concurrently)
import Data.Maybe (catMaybes)
import Input (fetchURLs)
-- For c_renderDoc
import Render (retreiveStyles, renderDoc)
import Data.ByteString.Lazy (toStrict)
import Data.ByteString (useAsCString)
-- For c_extractLinks
import Links (Link(..), extractLinks)
import qualified Data.Text.Foreign as Txt
-- Types I can export to C
import Foreign.Ptr
import Foreign.StablePtr
import Foreign.C.String
import Foreign.Marshal.Array
import Foreign.Marshal.Alloc
type CArray a = Ptr a
foreign export ccall c_newSession :: IO (StablePtr Session)
foreign export ccall c_freeSession :: StablePtr Session -> IO ()
c_newSession = newSession >>= newStablePtr
c_freeSession = freeStablePtr
data Page = Page {url :: URI, css :: ConditionalStyles StyleTree, html :: Document}
type PtrPage = Ptr ()
foreign export ccall c_freePages :: CArray PtrPage -> IO ()
c_freePages c_pages = do
pages <- peekArray0 nullPtr c_pages
forM pages (freeStablePtr . castPtrToStablePtr)
free c_pages
foreign export ccall c_fetchURLs :: StablePtr Session -> PtrPage -> CArray CString -> IO (CArray PtrPage)
c_fetchURLs c_session c_referer c_srcs = do
session <- deRefStablePtr c_session
referer <- if c_referer == nullPtr -- Can't check for NULL
then deRefStablePtr $ castPtrToStablePtr c_referer
else do
cwd <- getCurrentDirectory
return $ Page {
-- Default to URIs being relative to CWD.
url = URI {uriScheme = "file:", uriPath = cwd,
uriAuthority = Nothing, uriQuery = "", uriFragment = ""},
-- Blank values:
css = conditionalStyles nullURI "temp",
html = Document {
documentPrologue = Prologue [] Nothing [],
documentRoot = Element "temp" M.empty [],
documentEpilogue = []
}
}
emptyCStr <- newCString ""
srcCStrs <- peekArray0 emptyCStr c_srcs
srcs <- forConcurrently (pairs srcCStrs) $ \(c_mime, c_uri) -> do
mime <- peekCString c_mime
uri <- peekCString c_uri
return $ pair (words mime) <$> parseURIReference uri
ret <- fetchURLs session (html referer) (url referer) $ catMaybes srcs
c_ret <- forM ret $ \(url, html, css) ->
newStablePtr $ Page url css html
newArray0 nullPtr $ map castStablePtrToPtr c_ret
pairs (a:b:c) = (a, b):pairs c
pairs _ = []
pair a b = (a, b)
foreign export ccall c_renderDoc :: StablePtr Session -> PtrPage -> IO CString -- Hard to C bindings without IO
c_renderDoc c_session c_page = do
session <- deRefStablePtr c_session
page <- deRefStablePtr $ castPtrToStablePtr c_page
css' <- retreiveStyles session $ css page
toStrict (renderDoc css' $ documentRoot $ html page) `useAsCString` \cstr -> do
str <- peekCString cstr
newCString str
foreign export ccall c_extractLinks :: PtrPage -> IO (CArray CString)
c_extractLinks c_page = do
page <- deRefStablePtr $ castPtrToStablePtr c_page
ret <- forM (extractLinks $ html page) $ \link -> do
c_label <- text2cstring $ label link
c_title <- text2cstring $ title link
c_href <- newCString $ uriToString id (href link) ""
return [c_label, c_title, c_href]
emptyCStr <- newCString ""
newArray0 emptyCStr $ concat ret
text2cstring txt = Txt.withCStringLen txt $ \s -> (peekCStringLen s >>= newCString)
foreign export ccall c_docLinksAndRendering :: StablePtr Session -> PtrPage -> IO (CArray CString)
c_docLinksAndRendering c_session c_page = do
(c_links, ssml) <- c_extractLinks c_page `concurrently` c_renderDoc c_session c_page
nil <- newCString ""
links <- peekArray0 nil c_links
newArray0 nil (ssml : links)