module Main (main) where import Control.Exception (IOException, try) import Control.Monad.Error (ErrorT(..), MonadError(..)) import Data.List (intercalate) import System.Environment (getArgs, getProgName) import System.Exit (exitFailure, exitSuccess) import System.IO (hPutStrLn, stderr) import Language.Dot (parseDot, renderDot) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- main :: IO () main = getArgs >>= run run :: [String] -> IO () run args = case args of [fp] -> renderDotFile fp [] -> displayUsage >> exitSuccess _ -> displayUsage >> exitFailure -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- renderDotFile :: FilePath -> IO () renderDotFile fp = runErrorT (renderDotFileET fp) >>= either exitError putStrLn renderDotFileET :: FilePath -> ErrorT String IO String renderDotFileET fp = do contents <- readFile fp `liftCatch` show graph <- parseDot fp contents `liftEither` show return $ renderDot graph -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- displayUsage :: IO () displayUsage = do programName <- getProgName ePutStrLns [ programName ++ ": Pretty-print a Graphviz DOT file." , intercalate " " ["Usage:", programName, "FILE"] ] exitError :: String -> IO () exitError e = do displayUsage ePutStrLn "" let el = lines e if length el == 1 then ePutStrLn ("ERROR: " ++ e) else ePutStrLns ("ERROR:" : indent el) exitFailure where indent = map (" "++) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- liftCatch :: IO a -> (IOException -> e) -> ErrorT e IO a liftCatch a f = ErrorT $ fmap (either (Left . f) Right) (try a) liftEither :: (MonadError e m) => Either l r -> (l -> e) -> m r liftEither e f = either (throwError . f) return e -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ePutStrLn :: String -> IO () ePutStrLn = hPutStrLn stderr ePutStrLns :: [String] -> IO () ePutStrLns = mapM_ (hPutStrLn stderr)