{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric      #-}
{-# LANGUAGE OverloadedStrings  #-}
{- |
   Module      : Text.Pandoc.Error
   Copyright   : Copyright (C) 2006-2020 John MacFarlane
   License     : GNU GPL, version 2 or above

   Maintainer  : John MacFarlane <jgm@berkeley.edu>
   Stability   : alpha
   Portability : portable

This module provides a standard way to deal with possible errors encounted
during parsing.

-}
module Text.Pandoc.Error (
  PandocError(..),
  handleError) where

import Control.Exception (Exception)
import Data.Typeable (Typeable)
import Data.Word (Word8)
import Data.Text (Text)
import qualified Data.Text as T
import GHC.Generics (Generic)
import Network.HTTP.Client (HttpException)
import System.Exit (ExitCode (..), exitWith)
import System.IO (stderr)
import qualified Text.Pandoc.UTF8 as UTF8
import Text.Printf (printf)
import Text.Parsec.Error
import Text.Parsec.Pos hiding (Line)

type Input = Text

data PandocError = PandocIOError Text IOError
                 | PandocHttpError Text HttpException
                 | PandocShouldNeverHappenError Text
                 | PandocSomeError Text
                 | PandocParseError Text
                 | PandocParsecError Input ParseError
                 | PandocMakePDFError Text
                 | PandocOptionError Text
                 | PandocSyntaxMapError Text
                 | PandocFailOnWarningError
                 | PandocPDFProgramNotFoundError Text
                 | PandocPDFError Text
                 | PandocFilterError Text Text
                 | PandocCouldNotFindDataFileError Text
                 | PandocResourceNotFound Text
                 | PandocTemplateError Text
                 | PandocAppError Text
                 | PandocEpubSubdirectoryError Text
                 | PandocMacroLoop Text
                 | PandocUTF8DecodingError Text Int Word8
                 | PandocIpynbDecodingError Text
                 | PandocUnknownReaderError Text
                 | PandocUnknownWriterError Text
                 | PandocUnsupportedExtensionError Text Text
                 deriving (Int -> PandocError -> ShowS
[PandocError] -> ShowS
PandocError -> String
(Int -> PandocError -> ShowS)
-> (PandocError -> String)
-> ([PandocError] -> ShowS)
-> Show PandocError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [PandocError] -> ShowS
$cshowList :: [PandocError] -> ShowS
show :: PandocError -> String
$cshow :: PandocError -> String
showsPrec :: Int -> PandocError -> ShowS
$cshowsPrec :: Int -> PandocError -> ShowS
Show, Typeable, (forall x. PandocError -> Rep PandocError x)
-> (forall x. Rep PandocError x -> PandocError)
-> Generic PandocError
forall x. Rep PandocError x -> PandocError
forall x. PandocError -> Rep PandocError x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep PandocError x -> PandocError
$cfrom :: forall x. PandocError -> Rep PandocError x
Generic)

instance Exception PandocError

-- | Handle PandocError by exiting with an error message.
handleError :: Either PandocError a -> IO a
handleError :: Either PandocError a -> IO a
handleError (Right r :: a
r) = a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return a
r
handleError (Left e :: PandocError
e) =
  case PandocError
e of
    PandocIOError _ err' :: IOError
err' -> IOError -> IO a
forall a. IOError -> IO a
ioError IOError
err'
    PandocHttpError u :: Text
u err' :: HttpException
err' -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 61 (Text -> IO a) -> Text -> IO a
forall a b. (a -> b) -> a -> b
$
      "Could not fetch " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
u Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> "\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> HttpException -> Text
forall a. Show a => a -> Text
tshow HttpException
err'
    PandocShouldNeverHappenError s :: Text
s -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 62 (Text -> IO a) -> Text -> IO a
forall a b. (a -> b) -> a -> b
$
      "Something we thought was impossible happened!\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
      "Please report this to pandoc's developers: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
s
    PandocSomeError s :: Text
s -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 63 Text
s
    PandocParseError s :: Text
s -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 64 Text
s
    PandocParsecError input :: Text
input err' :: ParseError
err' ->
        let errPos :: SourcePos
errPos = ParseError -> SourcePos
errorPos ParseError
err'
            errLine :: Int
errLine = SourcePos -> Int
sourceLine SourcePos
errPos
            errColumn :: Int
errColumn = SourcePos -> Int
sourceColumn SourcePos
errPos
            ls :: [Text]
ls = Text -> [Text]
T.lines Text
input [Text] -> [Text] -> [Text]
forall a. Semigroup a => a -> a -> a
<> [""]
            errorInFile :: Text
errorInFile = if [Text] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Text]
ls Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
errLine Int -> Int -> Int
forall a. Num a => a -> a -> a
- 1
                            then [Text] -> Text
T.concat ["\n", [Text]
ls [Text] -> Int -> Text
forall a. [a] -> Int -> a
!! (Int
errLine Int -> Int -> Int
forall a. Num a => a -> a -> a
- 1)
                                          ,"\n", Int -> Text -> Text
T.replicate (Int
errColumn Int -> Int -> Int
forall a. Num a => a -> a -> a
- 1) " "
                                          ,"^"]
                        else ""
        in  Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 65 (Text -> IO a) -> Text -> IO a
forall a b. (a -> b) -> a -> b
$ "\nError at " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> ParseError -> Text
forall a. Show a => a -> Text
tshow  ParseError
err' Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
                     -- if error comes from a chunk or included file,
                     -- then we won't get the right text this way:
                     if SourcePos -> String
sourceName SourcePos
errPos String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "source"
                        then Text
errorInFile
                        else ""
    PandocMakePDFError s :: Text
s -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 66 Text
s
    PandocOptionError s :: Text
s -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 6 Text
s
    PandocSyntaxMapError s :: Text
s -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 67 Text
s
    PandocFailOnWarningError -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 3 "Failing because there were warnings."
    PandocPDFProgramNotFoundError pdfprog :: Text
pdfprog -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 47 (Text -> IO a) -> Text -> IO a
forall a b. (a -> b) -> a -> b
$
        Text
pdfprog Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> " not found. Please select a different --pdf-engine or install " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
pdfprog Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> " -- see also /usr/share/doc/pandoc/README.Debian"
    PandocPDFError logmsg :: Text
logmsg -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 43 (Text -> IO a) -> Text -> IO a
forall a b. (a -> b) -> a -> b
$ "Error producing PDF.\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
logmsg
    PandocFilterError filtername :: Text
filtername msg :: Text
msg -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 83 (Text -> IO a) -> Text -> IO a
forall a b. (a -> b) -> a -> b
$ "Error running filter " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
        Text
filtername Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> ":\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
msg
    PandocCouldNotFindDataFileError fn :: Text
fn -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 97 (Text -> IO a) -> Text -> IO a
forall a b. (a -> b) -> a -> b
$
        "Could not find data file " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
fn
    PandocResourceNotFound fn :: Text
fn -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 99 (Text -> IO a) -> Text -> IO a
forall a b. (a -> b) -> a -> b
$
        "File " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
fn Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> " not found in resource path"
    PandocTemplateError s :: Text
s -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 5 (Text -> IO a) -> Text -> IO a
forall a b. (a -> b) -> a -> b
$ "Error compiling template " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
s
    PandocAppError s :: Text
s -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 4 Text
s
    PandocEpubSubdirectoryError s :: Text
s -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 31 (Text -> IO a) -> Text -> IO a
forall a b. (a -> b) -> a -> b
$
      "EPUB subdirectory name '" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
s Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> "' contains illegal characters"
    PandocMacroLoop s :: Text
s -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 91 (Text -> IO a) -> Text -> IO a
forall a b. (a -> b) -> a -> b
$
      "Loop encountered in expanding macro " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
s
    PandocUTF8DecodingError f :: Text
f offset :: Int
offset w :: Word8
w -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 92 (Text -> IO a) -> Text -> IO a
forall a b. (a -> b) -> a -> b
$
      "UTF-8 decoding error in " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
f Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> " at byte offset " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall a. Show a => a -> Text
tshow Int
offset Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
      " (" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
T.pack (String -> Word8 -> String
forall r. PrintfType r => String -> r
printf "%2x" Word8
w) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> ").\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
      "The input must be a UTF-8 encoded text."
    PandocIpynbDecodingError w :: Text
w -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 93 (Text -> IO a) -> Text -> IO a
forall a b. (a -> b) -> a -> b
$
      "ipynb decoding error: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
w
    PandocUnknownReaderError r :: Text
r -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 21 (Text -> IO a) -> Text -> IO a
forall a b. (a -> b) -> a -> b
$
      "Unknown input format " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
r Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
      case Text
r of
        "doc" -> "\nPandoc can convert from DOCX, but not from DOC." Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
                 "\nTry using Word to save your DOC file as DOCX," Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
                 " and convert that with pandoc."
        "pdf" -> "\nPandoc can convert to PDF, but not from PDF."
        _     -> ""
    PandocUnknownWriterError w :: Text
w -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 22 (Text -> IO a) -> Text -> IO a
forall a b. (a -> b) -> a -> b
$
       "Unknown output format " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
w Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
       case Text
w of
         "pdf" -> "To create a pdf using pandoc, use" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
                  " -t latex|beamer|context|ms|html5" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
                 "\nand specify an output file with " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
                 ".pdf extension (-o filename.pdf)."
         "doc" -> "\nPandoc can convert to DOCX, but not from DOC."
         _     -> ""
    PandocUnsupportedExtensionError ext :: Text
ext f :: Text
f -> Int -> Text -> IO a
forall a. Int -> Text -> IO a
err 23 (Text -> IO a) -> Text -> IO a
forall a b. (a -> b) -> a -> b
$
      "The extension " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
ext Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> " is not supported " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
      "for " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
f

err :: Int -> Text -> IO a
err :: Int -> Text -> IO a
err exitCode :: Int
exitCode msg :: Text
msg = do
  Handle -> String -> IO ()
UTF8.hPutStrLn Handle
stderr (Text -> String
T.unpack Text
msg)
  ExitCode -> IO Any
forall a. ExitCode -> IO a
exitWith (ExitCode -> IO Any) -> ExitCode -> IO Any
forall a b. (a -> b) -> a -> b
$ Int -> ExitCode
ExitFailure Int
exitCode
  a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return a
forall a. HasCallStack => a
undefined

tshow :: Show a => a -> Text
tshow :: a -> Text
tshow = String -> Text
T.pack (String -> Text) -> (a -> String) -> a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> String
forall a. Show a => a -> String
show