{-# LANGUAGE CPP #-} module Main where import Data.Spreadsheet as S import Control.Monad.Exception.Asynchronous.Lazy as AsExc import Control.Monad (liftM) import qualified Data.ByteString.Lazy.UTF8 as U import Data.Monoid (mempty) import Data.Char (toUpper) import Data.List (isSuffixOf) import Data.Version (showVersion) import Network.HTTP.Conduit import Paths_pandoc_placetable (version) import System.Environment (getArgs) import Text.Pandoc.JSON import Text.Pandoc.Definition import Text.Pandoc.Builder ( Inlines , Blocks , toList , fromList , table , plain , str , divWith ) #if defined(INLINE_MARKDOWN) import Text.Pandoc.Readers.Markdown import Text.Pandoc.Options #endif main :: IO () main = do #if defined(INLINE_MARKDOWN) let withInlineMarkdown = "with" #else let withInlineMarkdown = "without" #endif args <- getArgs let hasArg a = a `elem` args if hasArg "-v" || hasArg "--version" || hasArg "-h" || hasArg "--help" then putStrLn $ unlines [ "pandoc-placetable " ++ showVersion version , "Compiled " ++ withInlineMarkdown ++ " the inlineMarkdown flag." , "https://github.com/mb21/pandoc-placetable" ] else toJSONFilter placeTable httpConduitManager :: IO Manager httpConduitManager = newManager tlsManagerSettings placeTable :: Block -> IO [Block] placeTable (CodeBlock (ident, cls, kvs) txt) | "table" `elem` cls = do csv <- find "file" (return "") getCsv let header = find "header" False (== "yes") let inlinemd = find "inlinemarkdown" False (== "yes") let aligns = find "aligns" (repeat AlignDefault) (map toAlign) let widths = find "widths" [] (map read . words) let capt = find "caption" "" id let qc = find "quotechar" '"' head let sep = find "delimiter" ',' $ \d -> if head d == '\\' then case head (tail d) of 't' -> '\t' 's' -> ' ' _ -> '\\' else head d let s' = if null txt then csv else txt ++ "\n" ++ csv let s = if isSuffixOf "\n" s' then s' else s' ++ "\n" let csvTable = csvToTable header inlinemd aligns widths capt qc sep s return $ toList $ if null ident then csvTable else divWith (ident,cls,[]) csvTable where find key def extract = case lookup key kvs of Just x -> extract x Nothing -> def getCsv url = case parseUrl url of Nothing -> readFile url Just req -> do mgr <- httpConduitManager res <- httpLbs req mgr return $ U.toString $ responseBody res toAlign c = case toUpper c of 'L' -> AlignLeft 'R' -> AlignRight 'C' -> AlignCenter _ -> AlignDefault placeTable a = return [a] -- | Convert a CSV String to a Pandoc Table simpleCsvToTable :: String -> Blocks simpleCsvToTable s = csvToTable False False (repeat AlignDefault) [] mempty '"' ',' s -- | Convert a bunch of options and a CSV String to a Pandoc Table csvToTable :: Bool -- ^ interpret first row as headers -> Bool -- ^ interpret as inline markdown (needs inlineMarkdown compile flag) -> [Alignment] -- ^ table column alignments -> [Double] -- ^ table column widths -> String -- ^ table caption -> Char -- ^ csv quotation character like " -> Char -- ^ csv field separator like , -> String -- ^ csv string to parse -> Blocks csvToTable header inlinemd aligns widths caption qc sep s = table (strToInlines caption) cellspecs (map strToBlocks headers) $ (map . map) strToBlocks rows where exc = S.fromString qc sep s rows' = case exception exc of Nothing -> result exc Just e -> [["Error parsing CSV: " ++ e]] (headers, rows) = if header && length rows' > 0 then (head rows', tail rows') else (replicate nrCols [], rows') nrCols = if null rows' then 0 else length $ head rows' widths' = if length widths == nrCols then widths else replicate nrCols 0 cellspecs = zip aligns widths' #if defined(INLINE_MARKDOWN) strToInlines s = if inlinemd then -- strip newlines and wrap s in a header so only inline syntax is parsed let s' = "# " ++ (concat $ lines s) extractIns (Header _ _ ins) = ins extractIns _ = [] in case readMarkdown def s' of Right (Pandoc _ bs) -> fromList $ extractIns $ head bs Left e -> str $ show e else if null s then mempty else str s strToBlocks s = if inlinemd then case readMarkdown def s of Right (Pandoc _ bs) -> fromList bs Left e -> plain $ str $ show e else plain $ str s #else strToInlines [] = mempty strToInlines s = str s strToBlocks s = plain $ str s #endif