Refactor compileWithStrategy to use ExceptT for linear error handling

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 <noreply@anthropic.com>
This commit is contained in:
Willem van den Ende 2025-07-29 23:31:40 +02:00
parent bf6f82abe5
commit 907fcb3884
2 changed files with 28 additions and 23 deletions

View File

@ -43,7 +43,8 @@ library
pandoc >=3.0 && <3.2, pandoc >=3.0 && <3.2,
pandoc-types >=1.23 && <1.25, pandoc-types >=1.23 && <1.25,
bytestring >=0.11 && <0.13, bytestring >=0.11 && <0.13,
temporary >=1.3 && <1.4 temporary >=1.3 && <1.4,
transformers >=0.5 && <0.7
default-language: Haskell2010 default-language: Haskell2010
executable docster executable docster

View File

@ -22,6 +22,8 @@ import System.Directory (copyFile, doesFileExist)
import System.Exit (ExitCode(..)) import System.Exit (ExitCode(..))
import Control.Exception (throwIO) import Control.Exception (throwIO)
import Control.Monad (void) import Control.Monad (void)
import Control.Monad.Trans.Except (ExceptT, runExceptT, throwE)
import Control.Monad.IO.Class (liftIO)
-- | Success indicator for user feedback -- | Success indicator for user feedback
successEmoji :: Text successEmoji :: Text
@ -116,34 +118,36 @@ processHTMLOutput html outputPath = do
return $ Right () 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 -- | Higher-order compilation function that takes a strategy and executes the pipeline
compileWithStrategy :: CompilationStrategy -> SourceDir -> OutputPath -> OutputPath -> IO (Either DocsterError ()) 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 -- Step 1: Read and parse markdown
content <- TIO.readFile inputPath content <- liftIO $ TIO.readFile inputPath
let readerOptions = def { readerExtensions = getDefaultExtensions "markdown" } let readerOptions = def { readerExtensions = getDefaultExtensions "markdown" }
config = DiagramConfig sourceDir (csOutputFormat strategy) config = DiagramConfig sourceDir (csOutputFormat strategy)
parseResult <- parseMarkdown readerOptions content -- Step 2: Parse markdown
case parseResult of pandoc <- liftEitherIO $ parseMarkdown readerOptions content
Left err -> return $ Left err
Right pandoc -> -- Step 3: Transform document (process Mermaid diagrams)
-- Step 2: Transform document (process Mermaid diagrams) transformed <- liftEitherIO $ transformDocument config pandoc
transformDocument config pandoc >>= \case
Left err -> return $ Left err -- Step 4: Generate output using format-specific writer
Right transformed -> output <- liftEitherIO $ generateOutput strategy transformed
-- Step 3: Generate output using format-specific writer
generateOutput strategy transformed >>= \case -- Step 5: Process output and write to file
Left err -> return $ Left err liftEitherIO $ csProcessOutput strategy output outputPath
Right output -> do
-- Step 4: Process output and write to file -- Step 6: Print success message
processResult <- csProcessOutput strategy output outputPath liftIO $ putStrLn $ T.unpack $ csSuccessMessage strategy outputPath
case processResult of
Left err -> return $ Left err
Right _ -> do
-- Step 5: Print success message
putStrLn $ T.unpack $ csSuccessMessage strategy outputPath
return $ Right ()
-- | Parse markdown with error handling -- | Parse markdown with error handling
parseMarkdown :: ReaderOptions -> Text -> IO (Either DocsterError Pandoc) parseMarkdown :: ReaderOptions -> Text -> IO (Either DocsterError Pandoc)