From 907fcb38846a1c83a80d3e48aca0951968290864 Mon Sep 17 00:00:00 2001 From: Willem van den Ende Date: Tue, 29 Jul 2025 23:31:40 +0200 Subject: [PATCH] Refactor compileWithStrategy to use ExceptT for linear error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace deeply nested Either case expressions with clean ExceptT monad transformer pipeline. This eliminates manual error propagation and creates a more readable "straight line" flow while preserving identical error handling semantics. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- docster.cabal | 3 ++- src/Docster/Compiler.hs | 48 ++++++++++++++++++++++------------------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/docster.cabal b/docster.cabal index eec832b..8d0d20d 100644 --- a/docster.cabal +++ b/docster.cabal @@ -43,7 +43,8 @@ library pandoc >=3.0 && <3.2, pandoc-types >=1.23 && <1.25, bytestring >=0.11 && <0.13, - temporary >=1.3 && <1.4 + temporary >=1.3 && <1.4, + transformers >=0.5 && <0.7 default-language: Haskell2010 executable docster diff --git a/src/Docster/Compiler.hs b/src/Docster/Compiler.hs index d9c8bc5..21e61a8 100644 --- a/src/Docster/Compiler.hs +++ b/src/Docster/Compiler.hs @@ -22,6 +22,8 @@ import System.Directory (copyFile, doesFileExist) import System.Exit (ExitCode(..)) import Control.Exception (throwIO) import Control.Monad (void) +import Control.Monad.Trans.Except (ExceptT, runExceptT, throwE) +import Control.Monad.IO.Class (liftIO) -- | Success indicator for user feedback successEmoji :: Text @@ -116,34 +118,36 @@ processHTMLOutput html outputPath = do return $ Right () +-- | Helper function to lift IO (Either DocsterError a) into ExceptT +liftEitherIO :: IO (Either DocsterError a) -> ExceptT DocsterError IO a +liftEitherIO action = do + result <- liftIO action + case result of + Left err -> throwE err + Right value -> return value + -- | Higher-order compilation function that takes a strategy and executes the pipeline compileWithStrategy :: CompilationStrategy -> SourceDir -> OutputPath -> OutputPath -> IO (Either DocsterError ()) -compileWithStrategy strategy sourceDir (OutputPath inputPath) (OutputPath outputPath) = do +compileWithStrategy strategy sourceDir (OutputPath inputPath) (OutputPath outputPath) = runExceptT $ do -- Step 1: Read and parse markdown - content <- TIO.readFile inputPath + content <- liftIO $ TIO.readFile inputPath let readerOptions = def { readerExtensions = getDefaultExtensions "markdown" } config = DiagramConfig sourceDir (csOutputFormat strategy) - parseResult <- parseMarkdown readerOptions content - case parseResult of - Left err -> return $ Left err - Right pandoc -> - -- Step 2: Transform document (process Mermaid diagrams) - transformDocument config pandoc >>= \case - Left err -> return $ Left err - Right transformed -> - -- Step 3: Generate output using format-specific writer - generateOutput strategy transformed >>= \case - Left err -> return $ Left err - Right output -> do - -- Step 4: Process output and write to file - processResult <- csProcessOutput strategy output outputPath - case processResult of - Left err -> return $ Left err - Right _ -> do - -- Step 5: Print success message - putStrLn $ T.unpack $ csSuccessMessage strategy outputPath - return $ Right () + -- Step 2: Parse markdown + pandoc <- liftEitherIO $ parseMarkdown readerOptions content + + -- Step 3: Transform document (process Mermaid diagrams) + transformed <- liftEitherIO $ transformDocument config pandoc + + -- Step 4: Generate output using format-specific writer + output <- liftEitherIO $ generateOutput strategy transformed + + -- Step 5: Process output and write to file + liftEitherIO $ csProcessOutput strategy output outputPath + + -- Step 6: Print success message + liftIO $ putStrLn $ T.unpack $ csSuccessMessage strategy outputPath -- | Parse markdown with error handling parseMarkdown :: ReaderOptions -> Text -> IO (Either DocsterError Pandoc)