{-# LANGUAGE MultiParamTypeClasses, OverloadedStrings #-}

{- |
   Module      : Data.GraphViz.Commands
   Description : Functions to run Graphviz commands.
   Copyright   : (c) Matthew Sackman, Ivan Lazar Miljenovic
   License     : 3-Clause BSD-style
   Maintainer  : Ivan.Miljenovic@gmail.com

   This module defines functions to call the various Graphviz
   commands.

   Whilst various output formats are supported (see 'GraphvizOutput'
   for a complete list), it is not yet possible to choose a desired
   renderer and formatter.  Being able to determine which renderers
   and formatters are applicable for a specific 'GraphvizOutput' is
   not easy (there is no listing of available renderers or formatters
   on the Graphviz website), and for the most part the default ones do
   the job well.

   Please note that for 'GraphvizOutput' and 'GraphvizCanvas', you
   will see that they are instances of a @GraphvizResult@ class; this is
   an internal class that should not be visible outside this module, but
   Haddock is being too helpful for its own good.
-}
module Data.GraphViz.Commands
    ( -- * The different Graphviz tools available.
      GraphvizCommand(..)
    , dirCommand
    , undirCommand
    , commandFor
      -- * The possible outputs that Graphviz supports.
      -- $outputs
    , GraphvizOutput(..)
    , GraphvizCanvas(..)
      -- * Running Graphviz.
    , runGraphviz
    , runGraphvizCommand
    , addExtension
    , runGraphvizCanvas
    , runGraphvizCanvas'
    , graphvizWithHandle
      -- * Testing if Graphviz is installed
    , isGraphvizInstalled
    , quitWithoutGraphviz
    ) where

import Data.GraphViz.Types
-- This is here just for Haddock linking purposes.
import Data.GraphViz.Commands.Available
import Data.GraphViz.Commands.IO        (runCommand)
import Data.GraphViz.Exception

import           Control.Monad    (liftM, unless)
import qualified Data.ByteString  as SB
import           Data.Maybe       (isJust)
import           Data.Version     (Version (..), showVersion)
import           System.Directory (findExecutable)
import           System.Exit      (ExitCode (..), exitWith)
import           System.FilePath  ((<.>))
import           System.IO        (Handle, hPutStrLn, hSetBinaryMode, stderr)

-- -----------------------------------------------------------------------------

showCmd           :: GraphvizCommand -> String
showCmd :: GraphvizCommand -> String
showCmd Dot       = "dot"
showCmd Neato     = "neato"
showCmd TwoPi     = "twopi"
showCmd Circo     = "circo"
showCmd Fdp       = "fdp"
showCmd Sfdp      = "sfdp"
showCmd Osage     = "osage"
showCmd Patchwork = "patchwork"

-- | The default command for directed graphs.
dirCommand :: GraphvizCommand
dirCommand :: GraphvizCommand
dirCommand = GraphvizCommand
Dot

-- | The default command for undirected graphs.
undirCommand :: GraphvizCommand
undirCommand :: GraphvizCommand
undirCommand = GraphvizCommand
Neato

-- | The appropriate (default) Graphviz command for the given graph.
commandFor    :: (DotRepr dg n) => dg n -> GraphvizCommand
commandFor :: dg n -> GraphvizCommand
commandFor dg :: dg n
dg = if dg n -> Bool
forall (dg :: * -> *) n. DotRepr dg n => dg n -> Bool
graphIsDirected dg n
dg
                then GraphvizCommand
dirCommand
                else GraphvizCommand
undirCommand

-- -----------------------------------------------------------------------------

{- $outputs

   The list of output types supported by Graphviz is dependent upon
   how it is built on your system.  To determine which actual formats
   are available on your system, run @dot -T?@.  Trying to use an
   output type that is not supported by your installation of Graphviz
   will result in an error.

   The outputs defined here in 'GraphvizOutput' and 'GraphvizCanvas'
   are those from the default list of available outputs.  For more
   information, see:
     <http://graphviz.org/doc/info/output.html>

-}

-- | This class is for those data types that are valid options for the
--   Graphviz tools to use with the @-T@ argument.
class GraphvizResult o where
  outputCall :: o -> String

-- | The possible Graphviz output formats (that is, those that
--   actually produce a file).
data GraphvizOutput = Bmp       -- ^ Windows Bitmap Format.
                    | Canon     -- ^ Pretty-printed Dot output with no
                                --   layout performed.
                    | DotOutput -- ^ Reproduces the input along with
                                --   layout information.
                    | XDot (Maybe Version)
                      -- ^ As with 'DotOutput', but provides even more
                      --   information on how the graph is drawn.  The
                      --   optional 'Version' is the same as
                      --   specifying the @XDotVersion@ attribute.
                    | Eps       -- ^ Encapsulated PostScript.
                    | Fig       -- ^ FIG graphics language.
                    | Gd        -- ^ Internal GD library format.
                    | Gd2       -- ^ Compressed version of 'Gd'.
                    | Gif       -- ^ Graphics Interchange Format.
                    | Ico       -- ^ Icon image file format.
                    | Imap      -- ^ Server-side imagemap.
                    | Cmapx     -- ^ Client-side imagemap.
                    | ImapNP    -- ^ As for 'Imap', except only
                                --   rectangles are used as active
                                --   areas.
                    | CmapxNP   -- ^ As for 'Cmapx', except only
                                --   rectangles are used as active
                                --   areas.
                    | Jpeg      -- ^ The JPEG image format.
                    | Pdf       -- ^ Portable Document Format.
                    | Plain     -- ^ Simple text format.
                    | PlainExt  -- ^ As for 'Plain', but provides port
                                --   names on head and tail nodes when
                                --   applicable.
                    | Png       -- ^ Portable Network Graphics format.
                    | Ps        -- ^ PostScript.
                    | Ps2       -- ^ PostScript for PDF.
                    | Svg       -- ^ Scalable Vector Graphics format.
                    | SvgZ      -- ^ Compressed SVG format.
                    | Tiff      -- ^ Tagged Image File Format.
                    | Vml       -- ^ Vector Markup Language; 'Svg' is
                                --   usually preferred.
                    | VmlZ      -- ^ Compressed VML format; 'SvgZ' is
                                --   usually preferred.
                    | Vrml      -- ^ Virtual Reality Modeling Language
                                --   format; requires nodes to have a
                                --   third dimension set via the @Pos@
                                --   attribute (and with a @Dim@ value
                                --   of at least @3@).
                    | WBmp      -- ^ Wireless BitMap format;
                                --   monochrome format usually used
                                --   for mobile computing devices.
                    | WebP      -- ^ Google's WebP format; requires
                                --   Graphviz >= 2.29.0.
                    deriving (GraphvizOutput -> GraphvizOutput -> Bool
(GraphvizOutput -> GraphvizOutput -> Bool)
-> (GraphvizOutput -> GraphvizOutput -> Bool) -> Eq GraphvizOutput
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: GraphvizOutput -> GraphvizOutput -> Bool
$c/= :: GraphvizOutput -> GraphvizOutput -> Bool
== :: GraphvizOutput -> GraphvizOutput -> Bool
$c== :: GraphvizOutput -> GraphvizOutput -> Bool
Eq, Eq GraphvizOutput
Eq GraphvizOutput =>
(GraphvizOutput -> GraphvizOutput -> Ordering)
-> (GraphvizOutput -> GraphvizOutput -> Bool)
-> (GraphvizOutput -> GraphvizOutput -> Bool)
-> (GraphvizOutput -> GraphvizOutput -> Bool)
-> (GraphvizOutput -> GraphvizOutput -> Bool)
-> (GraphvizOutput -> GraphvizOutput -> GraphvizOutput)
-> (GraphvizOutput -> GraphvizOutput -> GraphvizOutput)
-> Ord GraphvizOutput
GraphvizOutput -> GraphvizOutput -> Bool
GraphvizOutput -> GraphvizOutput -> Ordering
GraphvizOutput -> GraphvizOutput -> GraphvizOutput
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
min :: GraphvizOutput -> GraphvizOutput -> GraphvizOutput
$cmin :: GraphvizOutput -> GraphvizOutput -> GraphvizOutput
max :: GraphvizOutput -> GraphvizOutput -> GraphvizOutput
$cmax :: GraphvizOutput -> GraphvizOutput -> GraphvizOutput
>= :: GraphvizOutput -> GraphvizOutput -> Bool
$c>= :: GraphvizOutput -> GraphvizOutput -> Bool
> :: GraphvizOutput -> GraphvizOutput -> Bool
$c> :: GraphvizOutput -> GraphvizOutput -> Bool
<= :: GraphvizOutput -> GraphvizOutput -> Bool
$c<= :: GraphvizOutput -> GraphvizOutput -> Bool
< :: GraphvizOutput -> GraphvizOutput -> Bool
$c< :: GraphvizOutput -> GraphvizOutput -> Bool
compare :: GraphvizOutput -> GraphvizOutput -> Ordering
$ccompare :: GraphvizOutput -> GraphvizOutput -> Ordering
$cp1Ord :: Eq GraphvizOutput
Ord, Int -> GraphvizOutput -> ShowS
[GraphvizOutput] -> ShowS
GraphvizOutput -> String
(Int -> GraphvizOutput -> ShowS)
-> (GraphvizOutput -> String)
-> ([GraphvizOutput] -> ShowS)
-> Show GraphvizOutput
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [GraphvizOutput] -> ShowS
$cshowList :: [GraphvizOutput] -> ShowS
show :: GraphvizOutput -> String
$cshow :: GraphvizOutput -> String
showsPrec :: Int -> GraphvizOutput -> ShowS
$cshowsPrec :: Int -> GraphvizOutput -> ShowS
Show, ReadPrec [GraphvizOutput]
ReadPrec GraphvizOutput
Int -> ReadS GraphvizOutput
ReadS [GraphvizOutput]
(Int -> ReadS GraphvizOutput)
-> ReadS [GraphvizOutput]
-> ReadPrec GraphvizOutput
-> ReadPrec [GraphvizOutput]
-> Read GraphvizOutput
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [GraphvizOutput]
$creadListPrec :: ReadPrec [GraphvizOutput]
readPrec :: ReadPrec GraphvizOutput
$creadPrec :: ReadPrec GraphvizOutput
readList :: ReadS [GraphvizOutput]
$creadList :: ReadS [GraphvizOutput]
readsPrec :: Int -> ReadS GraphvizOutput
$creadsPrec :: Int -> ReadS GraphvizOutput
Read)

instance GraphvizResult GraphvizOutput where
  outputCall :: GraphvizOutput -> String
outputCall Bmp       = "bmp"
  outputCall Canon     = "canon"
  outputCall DotOutput = "dot"
  outputCall (XDot mv :: Maybe Version
mv) = "xdot" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String -> (Version -> String) -> Maybe Version -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe "" Version -> String
showVersion Maybe Version
mv
  outputCall Eps       = "eps"
  outputCall Fig       = "fig"
  outputCall Gd        = "gd"
  outputCall Gd2       = "gd2"
  outputCall Gif       = "gif"
  outputCall Ico       = "ico"
  outputCall Imap      = "imap"
  outputCall Cmapx     = "cmapx"
  outputCall ImapNP    = "imap_np"
  outputCall CmapxNP   = "cmapx_np"
  outputCall Jpeg      = "jpeg"
  outputCall Pdf       = "pdf"
  outputCall Plain     = "plain"
  outputCall PlainExt  = "plain-ext"
  outputCall Png       = "png"
  outputCall Ps        = "ps"
  outputCall Ps2       = "ps2"
  outputCall Svg       = "svg"
  outputCall SvgZ      = "svgz"
  outputCall Tiff      = "tiff"
  outputCall Vml       = "vml"
  outputCall VmlZ      = "vmlz"
  outputCall Vrml      = "vrml"
  outputCall WBmp      = "wbmp"
  outputCall WebP      = "webp"

-- | A default file extension for each 'GraphvizOutput'.
defaultExtension           :: GraphvizOutput -> String
defaultExtension :: GraphvizOutput -> String
defaultExtension Bmp       = "bmp"
defaultExtension Canon     = "gv"
defaultExtension DotOutput = "gv"
defaultExtension XDot{}    = "gv"
defaultExtension Eps       = "eps"
defaultExtension Fig       = "fig"
defaultExtension Gd        = "gd"
defaultExtension Gd2       = "gd2"
defaultExtension Gif       = "gif"
defaultExtension Ico       = "ico"
defaultExtension Imap      = "map"
defaultExtension Cmapx     = "map"
defaultExtension ImapNP    = "map"
defaultExtension CmapxNP   = "map"
defaultExtension Jpeg      = "jpg"
defaultExtension Pdf       = "pdf"
defaultExtension Plain     = "txt"
defaultExtension PlainExt  = "txt"
defaultExtension Png       = "png"
defaultExtension Ps        = "ps"
defaultExtension Ps2       = "ps"
defaultExtension Svg       = "svg"
defaultExtension SvgZ      = "svgz"
defaultExtension Tiff      = "tif"
defaultExtension Vml       = "vml"
defaultExtension VmlZ      = "vmlz"
defaultExtension Vrml      = "vrml"
defaultExtension WBmp      = "wbmp"
defaultExtension WebP      = "webp"

-- | Unlike 'GraphvizOutput', these items do not produce an output
--   file; instead, they directly draw a canvas (i.e. a window) with
--   the resulting image.
data GraphvizCanvas = Gtk | Xlib
                    deriving (GraphvizCanvas -> GraphvizCanvas -> Bool
(GraphvizCanvas -> GraphvizCanvas -> Bool)
-> (GraphvizCanvas -> GraphvizCanvas -> Bool) -> Eq GraphvizCanvas
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: GraphvizCanvas -> GraphvizCanvas -> Bool
$c/= :: GraphvizCanvas -> GraphvizCanvas -> Bool
== :: GraphvizCanvas -> GraphvizCanvas -> Bool
$c== :: GraphvizCanvas -> GraphvizCanvas -> Bool
Eq, Eq GraphvizCanvas
Eq GraphvizCanvas =>
(GraphvizCanvas -> GraphvizCanvas -> Ordering)
-> (GraphvizCanvas -> GraphvizCanvas -> Bool)
-> (GraphvizCanvas -> GraphvizCanvas -> Bool)
-> (GraphvizCanvas -> GraphvizCanvas -> Bool)
-> (GraphvizCanvas -> GraphvizCanvas -> Bool)
-> (GraphvizCanvas -> GraphvizCanvas -> GraphvizCanvas)
-> (GraphvizCanvas -> GraphvizCanvas -> GraphvizCanvas)
-> Ord GraphvizCanvas
GraphvizCanvas -> GraphvizCanvas -> Bool
GraphvizCanvas -> GraphvizCanvas -> Ordering
GraphvizCanvas -> GraphvizCanvas -> GraphvizCanvas
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
min :: GraphvizCanvas -> GraphvizCanvas -> GraphvizCanvas
$cmin :: GraphvizCanvas -> GraphvizCanvas -> GraphvizCanvas
max :: GraphvizCanvas -> GraphvizCanvas -> GraphvizCanvas
$cmax :: GraphvizCanvas -> GraphvizCanvas -> GraphvizCanvas
>= :: GraphvizCanvas -> GraphvizCanvas -> Bool
$c>= :: GraphvizCanvas -> GraphvizCanvas -> Bool
> :: GraphvizCanvas -> GraphvizCanvas -> Bool
$c> :: GraphvizCanvas -> GraphvizCanvas -> Bool
<= :: GraphvizCanvas -> GraphvizCanvas -> Bool
$c<= :: GraphvizCanvas -> GraphvizCanvas -> Bool
< :: GraphvizCanvas -> GraphvizCanvas -> Bool
$c< :: GraphvizCanvas -> GraphvizCanvas -> Bool
compare :: GraphvizCanvas -> GraphvizCanvas -> Ordering
$ccompare :: GraphvizCanvas -> GraphvizCanvas -> Ordering
$cp1Ord :: Eq GraphvizCanvas
Ord, GraphvizCanvas
GraphvizCanvas -> GraphvizCanvas -> Bounded GraphvizCanvas
forall a. a -> a -> Bounded a
maxBound :: GraphvizCanvas
$cmaxBound :: GraphvizCanvas
minBound :: GraphvizCanvas
$cminBound :: GraphvizCanvas
Bounded, Int -> GraphvizCanvas
GraphvizCanvas -> Int
GraphvizCanvas -> [GraphvizCanvas]
GraphvizCanvas -> GraphvizCanvas
GraphvizCanvas -> GraphvizCanvas -> [GraphvizCanvas]
GraphvizCanvas
-> GraphvizCanvas -> GraphvizCanvas -> [GraphvizCanvas]
(GraphvizCanvas -> GraphvizCanvas)
-> (GraphvizCanvas -> GraphvizCanvas)
-> (Int -> GraphvizCanvas)
-> (GraphvizCanvas -> Int)
-> (GraphvizCanvas -> [GraphvizCanvas])
-> (GraphvizCanvas -> GraphvizCanvas -> [GraphvizCanvas])
-> (GraphvizCanvas -> GraphvizCanvas -> [GraphvizCanvas])
-> (GraphvizCanvas
    -> GraphvizCanvas -> GraphvizCanvas -> [GraphvizCanvas])
-> Enum GraphvizCanvas
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: GraphvizCanvas
-> GraphvizCanvas -> GraphvizCanvas -> [GraphvizCanvas]
$cenumFromThenTo :: GraphvizCanvas
-> GraphvizCanvas -> GraphvizCanvas -> [GraphvizCanvas]
enumFromTo :: GraphvizCanvas -> GraphvizCanvas -> [GraphvizCanvas]
$cenumFromTo :: GraphvizCanvas -> GraphvizCanvas -> [GraphvizCanvas]
enumFromThen :: GraphvizCanvas -> GraphvizCanvas -> [GraphvizCanvas]
$cenumFromThen :: GraphvizCanvas -> GraphvizCanvas -> [GraphvizCanvas]
enumFrom :: GraphvizCanvas -> [GraphvizCanvas]
$cenumFrom :: GraphvizCanvas -> [GraphvizCanvas]
fromEnum :: GraphvizCanvas -> Int
$cfromEnum :: GraphvizCanvas -> Int
toEnum :: Int -> GraphvizCanvas
$ctoEnum :: Int -> GraphvizCanvas
pred :: GraphvizCanvas -> GraphvizCanvas
$cpred :: GraphvizCanvas -> GraphvizCanvas
succ :: GraphvizCanvas -> GraphvizCanvas
$csucc :: GraphvizCanvas -> GraphvizCanvas
Enum, ReadPrec [GraphvizCanvas]
ReadPrec GraphvizCanvas
Int -> ReadS GraphvizCanvas
ReadS [GraphvizCanvas]
(Int -> ReadS GraphvizCanvas)
-> ReadS [GraphvizCanvas]
-> ReadPrec GraphvizCanvas
-> ReadPrec [GraphvizCanvas]
-> Read GraphvizCanvas
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [GraphvizCanvas]
$creadListPrec :: ReadPrec [GraphvizCanvas]
readPrec :: ReadPrec GraphvizCanvas
$creadPrec :: ReadPrec GraphvizCanvas
readList :: ReadS [GraphvizCanvas]
$creadList :: ReadS [GraphvizCanvas]
readsPrec :: Int -> ReadS GraphvizCanvas
$creadsPrec :: Int -> ReadS GraphvizCanvas
Read, Int -> GraphvizCanvas -> ShowS
[GraphvizCanvas] -> ShowS
GraphvizCanvas -> String
(Int -> GraphvizCanvas -> ShowS)
-> (GraphvizCanvas -> String)
-> ([GraphvizCanvas] -> ShowS)
-> Show GraphvizCanvas
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [GraphvizCanvas] -> ShowS
$cshowList :: [GraphvizCanvas] -> ShowS
show :: GraphvizCanvas -> String
$cshow :: GraphvizCanvas -> String
showsPrec :: Int -> GraphvizCanvas -> ShowS
$cshowsPrec :: Int -> GraphvizCanvas -> ShowS
Show)

instance GraphvizResult GraphvizCanvas where
  outputCall :: GraphvizCanvas -> String
outputCall Gtk       = "gtk"
  outputCall Xlib      = "xlib"

-- -----------------------------------------------------------------------------

-- | Run the recommended Graphviz command on this graph, saving the result
--   to the file provided (note: file extensions are /not/ checked).
runGraphviz    :: (PrintDotRepr dg n) => dg n -> GraphvizOutput -> FilePath
                  -> IO FilePath
runGraphviz :: dg n -> GraphvizOutput -> String -> IO String
runGraphviz gr :: dg n
gr = GraphvizCommand -> dg n -> GraphvizOutput -> String -> IO String
forall (dg :: * -> *) n.
PrintDotRepr dg n =>
GraphvizCommand -> dg n -> GraphvizOutput -> String -> IO String
runGraphvizCommand (dg n -> GraphvizCommand
forall (dg :: * -> *) n. DotRepr dg n => dg n -> GraphvizCommand
commandFor dg n
gr) dg n
gr

-- | Run the chosen Graphviz command on this graph, saving the result
--   to the file provided (note: file extensions are /not/ checked).
runGraphvizCommand :: (PrintDotRepr dg n) => GraphvizCommand -> dg n
                      -> GraphvizOutput -> FilePath
                      -> IO FilePath
runGraphvizCommand :: GraphvizCommand -> dg n -> GraphvizOutput -> String -> IO String
runGraphvizCommand cmd :: GraphvizCommand
cmd gr :: dg n
gr t :: GraphvizOutput
t fp :: String
fp
  = (GraphvizException -> GraphvizException) -> IO String -> IO String
forall e1 e2 a.
(Exception e1, Exception e2) =>
(e1 -> e2) -> a -> a
mapException GraphvizException -> GraphvizException
addExc (IO String -> IO String) -> IO String -> IO String
forall a b. (a -> b) -> a -> b
$ GraphvizCommand
-> dg n -> GraphvizOutput -> (Handle -> IO String) -> IO String
forall (dg :: * -> *) n a.
PrintDotRepr dg n =>
GraphvizCommand
-> dg n -> GraphvizOutput -> (Handle -> IO a) -> IO a
graphvizWithHandle GraphvizCommand
cmd dg n
gr GraphvizOutput
t Handle -> IO String
toFile
  where
    addFl :: ShowS
addFl = String -> ShowS
forall a. [a] -> [a] -> [a]
(++) ("Unable to create " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
fp String -> ShowS
forall a. [a] -> [a] -> [a]
++ "\n")
    toFile :: Handle -> IO String
toFile h :: Handle
h = Handle -> IO ByteString
SB.hGetContents Handle
h IO ByteString -> (ByteString -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> ByteString -> IO ()
SB.writeFile String
fp IO () -> IO String -> IO String
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> String -> IO String
forall (m :: * -> *) a. Monad m => a -> m a
return String
fp

    addExc :: GraphvizException -> GraphvizException
addExc (GVProgramExc e :: String
e) = String -> GraphvizException
GVProgramExc (String -> GraphvizException) -> String -> GraphvizException
forall a b. (a -> b) -> a -> b
$ ShowS
addFl String
e
    addExc e :: GraphvizException
e                = GraphvizException
e

-- | Append the default extension for the provided 'GraphvizOutput' to
--   the provided 'FilePath' for the output file.
addExtension          :: (GraphvizOutput -> FilePath -> a)
                         -> GraphvizOutput -> FilePath -> a
addExtension :: (GraphvizOutput -> String -> a) -> GraphvizOutput -> String -> a
addExtension cmd :: GraphvizOutput -> String -> a
cmd t :: GraphvizOutput
t fp :: String
fp = GraphvizOutput -> String -> a
cmd GraphvizOutput
t String
fp'
  where
    fp' :: String
fp' = String
fp String -> ShowS
<.> GraphvizOutput -> String
defaultExtension GraphvizOutput
t

-- | Run the chosen Graphviz command on this graph, but send the
--   result to the given handle rather than to a file.
--
--   Note that the @'Handle' -> 'IO' a@ function /must/ fully consume
--   the input from the 'Handle'; e.g. use strict @ByteStrings@ rather
--   than lazy ones.
--
--   If the command was unsuccessful, then a 'GraphvizException' is
--   thrown.
graphvizWithHandle :: (PrintDotRepr dg n)
                      => GraphvizCommand  -- ^ Which command to run
                      -> dg n             -- ^ The 'DotRepr' to use
                      -> GraphvizOutput   -- ^ The 'GraphvizOutput' type
                      -> (Handle -> IO a) -- ^ Extract the output
                      -> IO a             -- ^ The error or the result.
graphvizWithHandle :: GraphvizCommand
-> dg n -> GraphvizOutput -> (Handle -> IO a) -> IO a
graphvizWithHandle = GraphvizCommand
-> dg n -> GraphvizOutput -> (Handle -> IO a) -> IO a
forall (dg :: * -> *) n o a.
(PrintDotRepr dg n, GraphvizResult o) =>
GraphvizCommand -> dg n -> o -> (Handle -> IO a) -> IO a
graphvizWithHandle'

-- This version is not exported as we don't want to let arbitrary
-- @Handle -> IO a@ functions to be used for GraphvizCanvas outputs.
graphvizWithHandle' :: (PrintDotRepr dg n, GraphvizResult o)
                       => GraphvizCommand -> dg n -> o
                       -> (Handle -> IO a) -> IO a
graphvizWithHandle' :: GraphvizCommand -> dg n -> o -> (Handle -> IO a) -> IO a
graphvizWithHandle' cmd :: GraphvizCommand
cmd dg :: dg n
dg t :: o
t f :: Handle -> IO a
f = String -> [String] -> (Handle -> IO a) -> dg n -> IO a
forall (dg :: * -> *) n a.
PrintDotRepr dg n =>
String -> [String] -> (Handle -> IO a) -> dg n -> IO a
runCommand (GraphvizCommand -> String
showCmd GraphvizCommand
cmd)
                                            ["-T" String -> ShowS
forall a. [a] -> [a] -> [a]
++ o -> String
forall o. GraphvizResult o => o -> String
outputCall o
t]
                                            Handle -> IO a
f'
                                            dg n
dg
  where
    f' :: Handle -> IO a
f' h :: Handle
h = Handle -> Bool -> IO ()
hSetBinaryMode Handle
h Bool
True IO () -> IO a -> IO a
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Handle -> IO a
f Handle
h

-- | Run the chosen Graphviz command on this graph and render it using
--   the given canvas type.
runGraphvizCanvas          :: (PrintDotRepr dg n) => GraphvizCommand -> dg n
                              -> GraphvizCanvas -> IO ()
runGraphvizCanvas :: GraphvizCommand -> dg n -> GraphvizCanvas -> IO ()
runGraphvizCanvas cmd :: GraphvizCommand
cmd gr :: dg n
gr c :: GraphvizCanvas
c = GraphvizCommand
-> dg n -> GraphvizCanvas -> (Handle -> IO ()) -> IO ()
forall (dg :: * -> *) n o a.
(PrintDotRepr dg n, GraphvizResult o) =>
GraphvizCommand -> dg n -> o -> (Handle -> IO a) -> IO a
graphvizWithHandle' GraphvizCommand
cmd dg n
gr GraphvizCanvas
c Handle -> IO ()
nullHandle
  where
    nullHandle :: Handle -> IO ()
    nullHandle :: Handle -> IO ()
nullHandle = (ByteString -> ()) -> IO ByteString -> IO ()
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM (() -> ByteString -> ()
forall a b. a -> b -> a
const ()) (IO ByteString -> IO ())
-> (Handle -> IO ByteString) -> Handle -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handle -> IO ByteString
SB.hGetContents

-- | Run the recommended Graphviz command on this graph and render it
--   using the given canvas type.
runGraphvizCanvas'   :: (PrintDotRepr dg n) => dg n -> GraphvizCanvas -> IO ()
runGraphvizCanvas' :: dg n -> GraphvizCanvas -> IO ()
runGraphvizCanvas' d :: dg n
d = GraphvizCommand -> dg n -> GraphvizCanvas -> IO ()
forall (dg :: * -> *) n.
PrintDotRepr dg n =>
GraphvizCommand -> dg n -> GraphvizCanvas -> IO ()
runGraphvizCanvas (dg n -> GraphvizCommand
forall (dg :: * -> *) n. DotRepr dg n => dg n -> GraphvizCommand
commandFor dg n
d) dg n
d

-- -----------------------------------------------------------------------------

-- | Is the Graphviz suite of tools installed?  This is determined by
--   whether @dot@ is available in the @PATH@.
isGraphvizInstalled :: IO Bool
isGraphvizInstalled :: IO Bool
isGraphvizInstalled = (Maybe String -> Bool) -> IO (Maybe String) -> IO Bool
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM Maybe String -> Bool
forall a. Maybe a -> Bool
isJust (IO (Maybe String) -> IO Bool)
-> (String -> IO (Maybe String)) -> String -> IO Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> IO (Maybe String)
findExecutable (String -> IO Bool) -> String -> IO Bool
forall a b. (a -> b) -> a -> b
$ GraphvizCommand -> String
showCmd GraphvizCommand
Dot

-- | If Graphviz does not seem to be available, print the provided
--   error message and then exit fatally.
quitWithoutGraphviz     :: String -> IO ()
quitWithoutGraphviz :: String -> IO ()
quitWithoutGraphviz err :: String
err = do Bool
hasGraphviz <- IO Bool
isGraphvizInstalled
                             Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
hasGraphviz
                               (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ Handle -> String -> IO ()
hPutStrLn Handle
stderr String
err IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> ExitCode -> IO ()
forall a. ExitCode -> IO a
exitWith (Int -> ExitCode
ExitFailure 1)