{-# Language ForeignFunctionInterface #-}
{-|

Warning: you should not need to use this module, if you're storing your text
sanely. Use "Text.Sundown.Html.String" or "Text.Sundown.Html.Text" so that you
won't have to worry about text encoding.

If you really want to use 'ByteString's directly, make sure that they are UTF-8.

-}
module Text.Sundown.Html.ByteString
    ( renderHtml
    , smartypants
      -- * Markdown extensions         
    , Extensions (..)
    , allExtensions
    , noExtensions
      -- * Html render modes         
    , HtmlRenderMode (..)
    , noHtmlModes
    , allHtmlModes
    ) where

import Data.Maybe (fromMaybe)
import Foreign.Marshal
import Foreign.Ptr
import Foreign.Storable
import System.IO.Unsafe

import Data.ByteString (ByteString)
import qualified Data.ByteString.Unsafe as BS

import Text.Sundown.Buffer.Foreign
import Text.Sundown.Foreign
import Text.Sundown.Html.Foreign

defaultMaxNesting :: Int
defaultMaxNesting :: Int
defaultMaxNesting = Int
16

-- | Parses a 'ByteString' containing the markdown, returns the Html code.
renderHtml :: Extensions
           -> HtmlRenderMode
           -> Bool              -- ^ If true, smartypant the output
           -> Maybe Int
           -- ^ The maximum nesting of the HTML. If Nothing, a default value
           -- (16) will be used.
           -> ByteString
           -> ByteString
{-# NOINLINE renderHtml #-}
renderHtml :: Extensions
-> HtmlRenderMode -> Bool -> Maybe Int -> ByteString -> ByteString
renderHtml Extensions
exts HtmlRenderMode
mode Bool
sp Maybe Int
maxNestingM ByteString
input =
    IO ByteString -> ByteString
forall a. IO a -> a
unsafePerformIO (IO ByteString -> ByteString) -> IO ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$
    (Ptr Callbacks -> IO ByteString) -> IO ByteString
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr Callbacks -> IO ByteString) -> IO ByteString)
-> (Ptr Callbacks -> IO ByteString) -> IO ByteString
forall a b. (a -> b) -> a -> b
$ \Ptr Callbacks
callbacks ->
    (Ptr HtmlRenderOptions -> IO ByteString) -> IO ByteString
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr HtmlRenderOptions -> IO ByteString) -> IO ByteString)
-> (Ptr HtmlRenderOptions -> IO ByteString) -> IO ByteString
forall a b. (a -> b) -> a -> b
$ \Ptr HtmlRenderOptions
options ->
    ByteString -> (CStringLen -> IO ByteString) -> IO ByteString
forall a. ByteString -> (CStringLen -> IO a) -> IO a
BS.unsafeUseAsCStringLen ByteString
input ((CStringLen -> IO ByteString) -> IO ByteString)
-> (CStringLen -> IO ByteString) -> IO ByteString
forall a b. (a -> b) -> a -> b
$ \(Ptr CChar
ptr, Int
len) -> do
        Ptr Buffer
ob <- CSize -> IO (Ptr Buffer)
bufnew CSize
64
        Ptr Callbacks -> Ptr HtmlRenderOptions -> HtmlRenderMode -> IO ()
sdhtml_renderer Ptr Callbacks
callbacks Ptr HtmlRenderOptions
options HtmlRenderMode
mode
        let maxNesting :: CSize
maxNesting = Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> CSize) -> Int -> CSize
forall a b. (a -> b) -> a -> b
$ Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
defaultMaxNesting Maybe Int
maxNestingM
        Ptr Markdown
markdown <- Extensions -> CSize -> Ptr Callbacks -> Ptr () -> IO (Ptr Markdown)
sd_markdown_new Extensions
exts CSize
maxNesting Ptr Callbacks
callbacks (Ptr HtmlRenderOptions -> Ptr ()
forall a b. Ptr a -> Ptr b
castPtr Ptr HtmlRenderOptions
options)
        Ptr Buffer -> Ptr CChar -> CSize -> Ptr Markdown -> IO ()
sd_markdown_render Ptr Buffer
ob Ptr CChar
ptr (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len) Ptr Markdown
markdown
        Ptr Markdown -> IO ()
sd_markdown_free Ptr Markdown
markdown
        Ptr Buffer
res <- if Bool
sp then
                   do Ptr Buffer
ob' <- CSize -> IO (Ptr Buffer)
bufnew CSize
64
                      Buffer {buf_data :: Buffer -> Ptr CChar
buf_data = Ptr CChar
optr, buf_size :: Buffer -> CSize
buf_size = CSize
olen} <- Ptr Buffer -> IO Buffer
forall a. Storable a => Ptr a -> IO a
peek Ptr Buffer
ob
                      Ptr Buffer -> Ptr CChar -> CSize -> IO ()
sdhtml_smartypants Ptr Buffer
ob' Ptr CChar
optr CSize
olen
                      Ptr Buffer -> IO ()
bufrelease Ptr Buffer
ob
                      Ptr Buffer -> IO (Ptr Buffer)
forall (m :: * -> *) a. Monad m => a -> m a
return Ptr Buffer
ob'
               else Ptr Buffer -> IO (Ptr Buffer)
forall (m :: * -> *) a. Monad m => a -> m a
return Ptr Buffer
ob
        ByteString
output <- Ptr Buffer -> IO Buffer
forall a. Storable a => Ptr a -> IO a
peek Ptr Buffer
res IO Buffer -> (Buffer -> IO ByteString) -> IO ByteString
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Buffer -> IO ByteString
getBufferData
        Ptr Buffer -> IO ()
bufrelease Ptr Buffer
res
        ByteString -> IO ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
output

-- | All the 'HtmlRenderMode' disabled
noHtmlModes :: HtmlRenderMode
noHtmlModes :: HtmlRenderMode
noHtmlModes = Bool
-> Bool
-> Bool
-> Bool
-> Bool
-> Bool
-> Bool
-> Bool
-> Bool
-> Bool
-> HtmlRenderMode
HtmlRenderMode Bool
False Bool
False Bool
False Bool
False Bool
False Bool
False Bool
False Bool
False
                             Bool
False Bool
False

-- | All the 'HtmlRenderMode' enabled
allHtmlModes :: HtmlRenderMode
allHtmlModes :: HtmlRenderMode
allHtmlModes = Bool
-> Bool
-> Bool
-> Bool
-> Bool
-> Bool
-> Bool
-> Bool
-> Bool
-> Bool
-> HtmlRenderMode
HtmlRenderMode Bool
True Bool
True Bool
True Bool
True Bool
True Bool
True Bool
True Bool
True Bool
True Bool
True

-- | Converts punctuation in Html entities,
-- <http://daringfireball.net/projects/smartypants/>
smartypants :: ByteString -> ByteString
{-# NOINLINE smartypants #-}
smartypants :: ByteString -> ByteString
smartypants ByteString
input =
    IO ByteString -> ByteString
forall a. IO a -> a
unsafePerformIO (IO ByteString -> ByteString) -> IO ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$
    ByteString -> (CStringLen -> IO ByteString) -> IO ByteString
forall a. ByteString -> (CStringLen -> IO a) -> IO a
BS.unsafeUseAsCStringLen ByteString
input ((CStringLen -> IO ByteString) -> IO ByteString)
-> (CStringLen -> IO ByteString) -> IO ByteString
forall a b. (a -> b) -> a -> b
$ \(Ptr CChar
ptr, Int
len) -> do
        Ptr Buffer
ob <- CSize -> IO (Ptr Buffer)
bufnew CSize
64
        Ptr Buffer -> Ptr CChar -> CSize -> IO ()
sdhtml_smartypants Ptr Buffer
ob Ptr CChar
ptr (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len)
        ByteString
output <- Ptr Buffer -> IO Buffer
forall a. Storable a => Ptr a -> IO a
peek Ptr Buffer
ob IO Buffer -> (Buffer -> IO ByteString) -> IO ByteString
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Buffer -> IO ByteString
getBufferData
        Ptr Buffer -> IO ()
bufrelease Ptr Buffer
ob
        ByteString -> IO ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
output